This discord bot was built with custom classes, i18next for translations, Pino as logger and MongoDB as database.
Made with 💖 by Nikki
All it takes is just 6-7 simple steps.
- Clone the repository.
git clone https://github.com/CuteNikki/discord-bot.git
- Navigate into the project directory.
cd discord-bot
- Install all the dependencies.
bun install
- Set up your config file.
# copy example.config.json and rename to config.json
# or use this command if you are on Linux
cp example.config.json config.json
# fill in all values (more details in the config file)
- Deploy the slash commands.
bun run deploy
# you may also use the /register-commands slash command on discord
# once the commands have been registered using the above command.
- Run the bot using a script.
# run in development
bun run dev
# or compile and run
bun run build
bun run start
- (optional) Configure more settings using the developer command.
This displays the use of all command properties including autocomplete.
import {
Colors, // all discord colors
EmbedBuilder,
PermissionFlagsBits,
SlashCommandBuilder,
type ColorResolvable
} from 'discord.js';
import { t } from 'i18next';
import { Command, ModuleType } from 'classes/command';
import { logger } from 'utils/logger';
export default new Command({
module: ModuleType.General,
// the module this command belongs to
// it categorizes commands in the commands list
cooldown: 1_000,
// 1 second cooldown between command uses
isDeveloperOnly: false,
// only developers can use this command
botPermissions: ['SendMessages'],
// the bot needs to have this permission to be able to use this command
data: new SlashCommandBuilder()
.setName('preview-color')
// command name
.setDescription('Sends an embed with a color to preview')
// command description
.setDefaultMemberPermissions(PermissionFlagsBits.ManageMessages)
// only users with the manage messages permission can see and use this command
.addStringOption(
(option) =>
option
.setName('color')
// option name
.setDescription('The color to preview')
// option description
.setRequired(true)
// makes the option required
.setAutocomplete(true)
// enables autocompletion
),
async autocomplete({ interaction, client }) {
const input = interaction.options.getFocused();
// This gets us whatever the user has typed in the autocomplete
const colors = [
{ name: 'white', value: Colors.White.toString(16) },
{ name: 'aqua', value: Colors.Aqua.toString(16) },
{ name: 'green', value: Colors.Green.toString(16) },
{ name: 'blue', value: Colors.Blue.toString(16) },
{ name: 'yellow', value: Colors.Yellow.toString(16) },
{ name: 'purple', value: Colors.Purple.toString(16) },
{ name: 'luminous-vivid-pink', value: Colors.LuminousVividPink.toString(16) },
{ name: 'fuchsia', value: Colors.Fuchsia.toString(16) },
{ name: 'gold', value: Colors.Gold.toString(16) },
{ name: 'orange', value: Colors.Orange.toString(16) },
{ name: 'red', value: Colors.Red.toString(16) },
{ name: 'grey', value: Colors.Grey.toString(16) },
{ name: 'navy', value: Colors.Navy.toString(16) },
{ name: 'dark-aqua', value: Colors.DarkAqua.toString(16) },
{ name: 'dark-green', value: Colors.DarkGreen.toString(16) },
{ name: 'dark-blue', value: Colors.DarkBlue.toString(16) },
{ name: 'dark-purple', value: Colors.DarkPurple.toString(16) },
{ name: 'dark-vivid-pink', value: Colors.DarkVividPink.toString(16) },
{ name: 'dark-gold', value: Colors.DarkGold.toString(16) },
{ name: 'dark-orange', value: Colors.DarkOrange.toString(16) },
{ name: 'dark-red', value: Colors.DarkRed.toString(16) },
{ name: 'dark-grey', value: Colors.DarkGrey.toString(16) },
{ name: 'darker-grey', value: Colors.DarkerGrey.toString(16) },
{ name: 'light-grey', value: Colors.LightGrey.toString(16) },
{ name: 'dark-navy', value: Colors.DarkNavy.toString(16) },
{ name: 'blurple', value: Colors.Blurple.toString(16) },
{ name: 'greyple', value: Colors.Greyple.toString(16) },
{ name: 'dark-but-not-black', value: Colors.DarkButNotBlack.toString(16) },
{ name: 'not-quite-black', value: Colors.NotQuiteBlack.toString(16) }
];
if (!input.length) return await interaction.respond(colors.slice(0, 25));
await interaction.respond(
colors
.filter(
(color) => color.name.toLowerCase().includes(input.toLowerCase())
// Check if the input includes the color name
)
.slice(0, 25)
);
// Making sure we only ever return max of 25 results
},
async execute({ interaction, client, lng }) {
// the order of interaction, client and lng does not matter
// get a guilds language
// import { getGuildLanguage } from 'db/guild';
// const guildLng = await getGuildLanguage(guildId);
// get a different users language
// import { getUserLanguage } from 'db/user';
// const otherLng = await getUserLanguage(userId);
const color = interaction.options.getString('color', true);
// Autocomplete allows you to give the user a list to choose from
// but they will still be able to type in whatever they want!
// it's a must to check if they actually provided a valid color.
try {
await interaction.reply({
embeds: [
new EmbedBuilder()
.setColor(color as ColorResolvable)
// casting the color to a color resolvable
.setDescription(t('preview-color.preview', { lng, color }))
]
});
} catch (err) {
logger.debug({ err }, 'Error while previewing color');
if (!interaction.replied) {
await interaction.reply({ content: t('preview-color.invalid', { lng }), ephemeral: true });
} else {
await interaction.editReply({ content: t('preview-color.invalid', { lng }) });
}
}
}
});
src/structure/locales/{lng}-messages.json
{
"preview-color": {
"preview": "Heres a preview of the color {{color}}!",
"invalid": "The color you provided is invalid!"
}
}
src/structure/locales/{lng}-commands.json
{
"preview-color": {
"name": "preview-color",
"description": "Sends an embed with a color to preview",
"options": [
{
"name": "color",
"description": "The color to preview"
}
]
}
}
import { Events } from 'discord.js';
import { Event } from 'classes/event';
import { logger } from 'utils/logger';
export default new Event({
name: Events.ClientReady,
// the name of the event
once: true,
// only run this once, won't run again even if another ready event is emitted
execute(client, readyClient) {
// it is important to always have client first
// and other events properties after that
logger.info(`Logged in as ${readyClient.user.username}#${readyClient.user.discriminator}`);
}
});
import { Button } from 'classes/button';
export default new Button({
customId: 'ping',
// the custom id of the button
isAuthorOnly: true,
// if the button was received by a command, only the command sender can use this button
isCustomIdIncluded: false,
// if true then a button with the custom id of "ping-two" would still
// trigger this button because the custom id includes "ping"
permissions: ['SendMessages'],
// the permissions required to use this button
botPermissions: ['SendMessages'],
// permissions the bot needs to execute this function
async execute({ interaction, client, lng }) {
await interaction.channel?.send({ content: 'pong!' });
}
});
Contributions, issues and feature requests are welcome. Feel free to check issues page if you want to contribute.
Please ⭐️ this repository if this project helped you!
Copyright © 2024 CuteNikki. This project is MIT licensed.
- miscellaneous
- fully translated all messages sent by the bot
- added locales to all registered slash commands
- replace any null/undefined, true/false and so on with proper translations
- fix translation props (sometimes the id is displayed because
toString()
was not used)
- moderation module
- infractions command
- slash and user context menu commands
- purge command
- filter by specific user, channel and before/after a message
- ban command
- temporary bans
- automatically unbans users once the ban expires
- temporary bans
- unban command
- kick command
- timeout command
- warn command
- infractions command
- general module
- language command
- editable user language
- editable server language (mainly used for the ticket system and the server log)
- bot information
- botinfo command
- clusters/shards command
- ping command
- uptime command
- list of commands (
/commands
) - support command
- invite command
- language command
- level module
- automatically resets weekly level
- rank command (with weekly option)
- leaderboard command (with weekly option)
- modify users level/xp with config command
- utility module (not checked because I'd like to add more)
- avatar/banner slash and user context menu commands
- userinfo slash and user context menu commands
- serverinfo command
- weather command
- reminder command (weatherapi.com)
- developer module (more features might be added)
- evaluate code (
/eval
)- edit button
- execute console command (
/exec
) - register commands command (
/register-commands
)
- evaluate code (
- fun module
- phone command
- if no message sent within 4 minutes, automatically end the call
- button to enter the queue again or end the call
- game command
- Rock-Paper-Scissors
- Tic-Tac-Toe
- Connect-4
- Trivia
- Hangman
- Memory
- Snake
- Fast-Type
- Remember-Emoji
- Tetris
- Sokoban
- 2048 (maybe?)
- Lights Out (maybe?)
- phone command
- config commands
- custom voice channels
- fully customizable for users
- reaction roles
- choose between reactions and buttons (Buttons provide more user feedback)
- fully customizable message
- counting game
- reset on wrong number is optional
- word chain game (next word needs to start with the last letter of the previous word)
- giveaway
- create giveaway (time, prize, winnercount, channel)
- edit giveaway (time, prize, winnercount)
- delete giveaway (giveaway id)
- reroll giveaway (giveaway id)
- list active giveaways
- join/leave giveaway (updates participants count on giveaway)
- announce winners and delete giveaway on end
- economy
- needs todo
- confession
- needs todo
- suggestions
- needs todo
- starboard
- enable/disable module
- starboard messages can be deleted or edited by the author
- users can only star a message once and can also remove their star
- moderation config
- enable/disable module
- staff role (maybe? not sure yet)
- adjustable option to make reasons required (optional by default)
- level config
- refactor
- disable/enable module
- modify users level/xp
- levelup announcement
- can send to current/other channel or dms
- fully customizable message
- ignored roles and channels
- enabled channels
- welcome config
- disable/enable module
- fully customizable welcome messages
- add roles on join
- farewell config
- disable/enable module
- fully customizable farewell messages
- ticket config
- disable/enable module
- fully customizable ticket options (color of buttons)
- developer config
- update support settings
- invite url
- support server
- globally ban users
- custom badges
- add/remove badges from users
- implement badges:
- developer
- staff member
- translator
- expert bughunter
- bughunter
- supporter
- automatically added on support server boost
- update support settings
- server log config
- refactor
- enable/disable
- implement events:
- autoModerationActionExecution
- applicationCommandPermissionUpdate
- autoModerationRuleCreate
- autoModerationRuleDelete
- autoModerationRuleUpdate
- channelCreate
- channelDelete
- channelUpdate
- emojiCreate
- emojiDelete
- emojiUpdate
- guildBanAdd
- guildBanRemove
- guildMemberAdd
- warns about potentially dangerous users and gives option to ban/kick them
- guildMemberRemove
- guildMemberUpdate
- guildScheduledEventCreate
- guildScheduledEventDelete
- guildScheduledEventUpdate
- guildScheduledEventUserAdd
- guildScheduledEventUserRemove
- guildUpdate
- inviteCreate
- inviteDelete
- messageUpdate
- messageDelete
- messageBulkDelete
- messageReactionRemoveAll
- roleCreate
- roleDelete
- roleUpdate
- stickerCreate
- stickerDelete
- stickerUpdate
- threadCreate
- threadDelete
- threadUpdate
- voiceStateUpdate
- custom voice channels