← Back to lessons
DISCORD SYSTEMS

Lesson — Credit System

Goal: Build a full credit system with add, remove, and view commands using MongoDB.

Lesson Overview

You will build a production-ready credit system with:

Video Lesson

Notes — Understanding the System

Subcommands

/credit add, /credit remove, /credit view

Permissions

Only users with allowed role IDs can modify credits.

Database

MongoDB stores user balances permanently.

Safety

Credits never go below 0 using Math.max().

Step-by-Step

Create Command File

Create: commands/credit.js

Full Command Code

Show Full File
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 Schema File

Create: schemas/credit.js

Full Schema Code

Show Full File
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);

Restart Bot

node index.js

Challenge

← Course Home Next Lesson →