Goal: Build a full credit system with add, remove, and view commands using MongoDB.
You will build a production-ready credit system with:
/credit add, /credit remove, /credit view
Only users with allowed role IDs can modify credits.
MongoDB stores user balances permanently.
Credits never go below 0 using Math.max().
Create:
commands/credit.js
const { SlashCommandBuilder } = require('discord.js');
const Credit = require('../schemas/credit');
const CREDIT_MANAGER_ROLE_IDS = [
'ALLOWED_ROLE_ID',
'ALLOWED_ROLE_ID',
'ALLOWED_ROLE_ID',
];
module.exports = {
data: new SlashCommandBuilder()
.setName('credit')
.setDescription('Manage credits')
.addSubcommand(sub =>
sub
.setName('add')
.setDescription('Add credits to a user')
.addUserOption(opt =>
opt.setName('user')
.setDescription('User to add credits to')
.setRequired(true)
)
.addIntegerOption(opt =>
opt.setName('amount')
.setDescription('Amount of credits')
.setRequired(true)
.setMinValue(1)
)
)
.addSubcommand(sub =>
sub
.setName('remove')
.setDescription('Remove credits')
.addUserOption(opt =>
opt.setName('user')
.setRequired(true)
)
.addIntegerOption(opt =>
opt.setName('amount')
.setRequired(true)
.setMinValue(1)
)
)
.addSubcommand(sub =>
sub
.setName('view')
.setDescription('View credits')
.addUserOption(opt =>
opt.setName('user')
.setRequired(false)
)
),
run: async ({ interaction }) => {
const sub = interaction.options.getSubcommand(true);
if (['add', 'remove'].includes(sub)) {
if (!interaction.member.roles.cache.some(role =>
CREDIT_MANAGER_ROLE_IDS.includes(role.id)
)) {
return interaction.reply({
content: 'No permission.',
ephemeral: true,
});
}
}
if (sub === 'add') {
const user = interaction.options.getUser('user');
const amount = interaction.options.getInteger('amount');
let data = await Credit.findOne({ user_id: user.id });
if (!data) {
data = await Credit.create({
user_id: user.id,
credits: '0',
});
}
const newTotal = parseInt(data.credits, 10) + amount;
data.credits = newTotal.toString();
await data.save();
return interaction.reply({
content: `Added R$${amount} to ${user.tag}\nBalance: ${newTotal}`,
});
}
if (sub === 'remove') {
const user = interaction.options.getUser('user');
const amount = interaction.options.getInteger('amount');
let data = await Credit.findOne({ user_id: user.id });
if (!data) {
data = await Credit.create({
user_id: user.id,
credits: '0',
});
}
const newTotal = Math.max(parseInt(data.credits, 10) - amount, 0);
data.credits = newTotal.toString();
await data.save();
return interaction.reply({
content: `Removed R$${amount} from ${user.tag}\nBalance: ${newTotal}`,
});
}
if (sub === 'view') {
const user = interaction.options.getUser('user') || interaction.user;
const data = await Credit.findOne({ user_id: user.id });
const total = data ? parseInt(data.credits, 10) : 0;
return interaction.reply({
content: `${user.tag} has R$${total} credits`,
});
}
},
};
Create:
schemas/credit.js
const { Schema, model } = require('mongoose');
const credit = new Schema({
user_id: {
type: String,
required: true,
},
credits: {
type: String,
required: true,
},
});
module.exports = model('credit', credit);
node index.js