From 022e09a1f363697fe2a19863ceb394eb448da9e7 Mon Sep 17 00:00:00 2001 From: Fretux Date: Mon, 26 Jan 2026 12:14:18 +0100 Subject: [PATCH] Add currency commands and polish utilities --- commands/db/balance.js | 26 +++++++++++++++ commands/db/earn.js | 59 +++++++++++++++++++++++++++++++++ commands/db/give.js | 40 ++++++++++++++--------- commands/db/transfer.js | 68 +++++++++++++++++++++++++++++++++++++++ commands/utility/about.js | 18 +++++++---- commands/utility/help.js | 26 ++++++++------- commands/utility/ping.js | 13 ++++++-- 7 files changed, 213 insertions(+), 37 deletions(-) create mode 100644 commands/db/earn.js create mode 100644 commands/db/transfer.js diff --git a/commands/db/balance.js b/commands/db/balance.js index e69de29..32b274e 100644 --- a/commands/db/balance.js +++ b/commands/db/balance.js @@ -0,0 +1,26 @@ +const { SlashCommandBuilder } = require('discord.js'); +const { connectToDatabase } = require('../../db.js'); + +module.exports = { + data: new SlashCommandBuilder() + .setName('balance') + .setDescription('Check your coin balance or another user\'s balance.') + .addUserOption(option => + option + .setName('user') + .setDescription('User to check (defaults to you)') + .setRequired(false)), + async execute(interaction) { + const dbClient = await connectToDatabase(); + const db = dbClient.db('discord'); + const collection = db.collection('currency'); + + const target = interaction.options.getUser('user') ?? interaction.user; + const targetData = await collection.findOne({ userID: target.id }); + const balance = targetData?.balance ?? 0; + + return interaction.reply({ + content: `${target.tag} has **${balance}** coins.`, + }); + } +}; diff --git a/commands/db/earn.js b/commands/db/earn.js new file mode 100644 index 0000000..6853b1e --- /dev/null +++ b/commands/db/earn.js @@ -0,0 +1,59 @@ +const { SlashCommandBuilder } = require('discord.js'); +const { connectToDatabase } = require('../../db.js'); + +const REWARD_AMOUNT = 100; +const COOLDOWN_MS = 24 * 60 * 60 * 1000; + +const formatDuration = (ms) => { + const totalSeconds = Math.ceil(ms / 1000); + const hours = Math.floor(totalSeconds / 3600); + const minutes = Math.floor((totalSeconds % 3600) / 60); + const seconds = totalSeconds % 60; + + const parts = []; + if (hours) parts.push(`${hours}h`); + if (minutes) parts.push(`${minutes}m`); + if (!hours && !minutes) parts.push(`${seconds}s`); + + return parts.join(' '); +}; + +module.exports = { + data: new SlashCommandBuilder() + .setName('earn') + .setDescription('Claim your daily coin reward.'), + async execute(interaction) { + const dbClient = await connectToDatabase(); + const db = dbClient.db('discord'); + const collection = db.collection('currency'); + const now = new Date(); + + const userId = interaction.user.id; + const userData = await collection.findOne({ userID: userId }); + const lastClaim = userData?.lastClaimedAt ? new Date(userData.lastClaimedAt) : null; + + if (lastClaim) { + const elapsed = now - lastClaim; + if (elapsed < COOLDOWN_MS) { + const remaining = COOLDOWN_MS - elapsed; + return interaction.reply({ + content: `You already claimed your reward. Try again in **${formatDuration(remaining)}**.`, + ephemeral: true, + }); + } + } + + await collection.updateOne( + { userID: userId }, + { + $set: { lastClaimedAt: now.toISOString() }, + $inc: { balance: REWARD_AMOUNT }, + }, + { upsert: true } + ); + + return interaction.reply({ + content: `You earned **${REWARD_AMOUNT}** coins! Come back tomorrow for more.`, + }); + } +}; diff --git a/commands/db/give.js b/commands/db/give.js index 4bc2441..77a6548 100644 --- a/commands/db/give.js +++ b/commands/db/give.js @@ -1,37 +1,45 @@ -// exampleCommand.js -const { SlashCommandBuilder } = require('discord.js'); +const { SlashCommandBuilder, PermissionFlagsBits } = require('discord.js'); const { connectToDatabase } = require('../../db.js'); module.exports = { data: new SlashCommandBuilder() .setName('give') - .setDescription('give somebody coins') + .setDescription('Admin: grant coins to a user.') + .setDefaultMemberPermissions(PermissionFlagsBits.ManageGuild) .addIntegerOption(option => option .setName('amount') - .setDescription('amount of the coins') + .setDescription('Amount of coins to give') + .setMinValue(1) .setRequired(true)) .addUserOption(option => - option.setName('target') - .setDescription('The user to give coins to') + option + .setName('target') + .setDescription('User to receive coins') .setRequired(true)), async execute(interaction) { const dbClient = await connectToDatabase(); const target = interaction.options.getUser('target'); const amount = interaction.options.getInteger('amount'); - // Choose a DB + Collection + if (target.bot) { + return interaction.reply({ + content: 'You cannot grant coins to bot accounts.', + ephemeral: true, + }); + } + const db = dbClient.db('discord'); const collection = db.collection('currency'); - let targetData = await collection.findOne({ userID: target.id }); - if (!targetData) { - await collection.insertOne({ userID: target.id, balance: 0 }); - } + await collection.updateOne( + { userID: target.id }, + { $inc: { balance: amount } }, + { upsert: true } + ); - // Perform the transaction: subtract from sender and add to target. - await collection.updateOne({ userID: target.id }, { $inc: { balance: amount } }, { upsert: true }); - - return interaction.reply({ content: `You have given ${amount} coins to ${target.tag}.` }); + return interaction.reply({ + content: `You granted **${amount}** coins to ${target.tag}.`, + }); } -}; \ No newline at end of file +}; diff --git a/commands/db/transfer.js b/commands/db/transfer.js new file mode 100644 index 0000000..b2adda2 --- /dev/null +++ b/commands/db/transfer.js @@ -0,0 +1,68 @@ +const { SlashCommandBuilder } = require('discord.js'); +const { connectToDatabase } = require('../../db.js'); + +module.exports = { + data: new SlashCommandBuilder() + .setName('transfer') + .setDescription('Send coins to another user.') + .addUserOption(option => + option + .setName('target') + .setDescription('User to send coins to') + .setRequired(true)) + .addIntegerOption(option => + option + .setName('amount') + .setDescription('Amount of coins to send') + .setMinValue(1) + .setRequired(true)), + async execute(interaction) { + const dbClient = await connectToDatabase(); + const db = dbClient.db('discord'); + const collection = db.collection('currency'); + + const target = interaction.options.getUser('target'); + const amount = interaction.options.getInteger('amount'); + + if (target.bot) { + return interaction.reply({ + content: 'You cannot transfer coins to a bot account.', + ephemeral: true, + }); + } + + if (target.id === interaction.user.id) { + return interaction.reply({ + content: 'You cannot transfer coins to yourself.', + ephemeral: true, + }); + } + + const senderId = interaction.user.id; + const senderData = await collection.findOne({ userID: senderId }); + const senderBalance = senderData?.balance ?? 0; + + if (senderBalance < amount) { + return interaction.reply({ + content: `You only have **${senderBalance}** coins available.`, + ephemeral: true, + }); + } + + await collection.updateOne( + { userID: senderId }, + { $inc: { balance: -amount } }, + { upsert: true } + ); + + await collection.updateOne( + { userID: target.id }, + { $inc: { balance: amount } }, + { upsert: true } + ); + + return interaction.reply({ + content: `You sent **${amount}** coins to ${target.tag}.`, + }); + } +}; diff --git a/commands/utility/about.js b/commands/utility/about.js index c809cd3..723c24c 100644 --- a/commands/utility/about.js +++ b/commands/utility/about.js @@ -1,19 +1,23 @@ -const {SlashCommandBuilder, EmbedBuilder} = require("discord.js"); +const { SlashCommandBuilder, EmbedBuilder } = require('discord.js'); module.exports = { data: new SlashCommandBuilder() - .setName("about") - .setDescription("Information about this bot and creator"), + .setName('about') + .setDescription('Information about this bot and creator'), async execute(interaction) { const exampleEmbed = new EmbedBuilder() .setColor(0x0099FF) - .setTitle('About Page') - .setDescription('I build this bot for managing my servers. (to lazy to write mor text rn)') + .setTitle('About This Bot') + .setDescription('A multipurpose Discord bot focused on helpful utilities, moderation, and fun community features.') + .addFields( + { name: 'Highlights', value: 'Timezone converter, quiz games, and a growing currency system.' }, + { name: 'Created By', value: interaction.client.user?.username ?? 'Bot Creator' } + ) .setImage('https://i.postimg.cc/QdZZwKVD/Screenshot-2024-12-25-005933.png') .setTimestamp(); await interaction.reply({ embeds: [exampleEmbed] - }) + }); } -} \ No newline at end of file +}; diff --git a/commands/utility/help.js b/commands/utility/help.js index 9936a41..14a7cfe 100644 --- a/commands/utility/help.js +++ b/commands/utility/help.js @@ -1,23 +1,27 @@ -const { SlashCommandBuilder} = require("discord.js"); +const { SlashCommandBuilder, EmbedBuilder } = require('discord.js'); module.exports = { data: new SlashCommandBuilder() .setName('help') .setDescription('Gives all available commands.'), async execute(interaction) { - // Map over the full command objects to get names and descriptions - const commandDetails = interaction.client.commands.map(cmd => ({ - name: cmd.data.name, - description: cmd.data.description, - })); + const commandDetails = interaction.client.commands + .map(cmd => ({ + name: cmd.data.name, + description: cmd.data.description, + })) + .sort((a, b) => a.name.localeCompare(b.name)); - // Format the commands into a user-friendly help message const helpMessage = commandDetails - .map(detail => `/${detail.name} - ${detail.description}`) + .map(detail => `• **/${detail.name}** — ${detail.description}`) .join('\n'); - // Send the help message - await interaction.reply(`Here are all the available commands:\n\n${helpMessage}`); + const embed = new EmbedBuilder() + .setColor(0x4b7bec) + .setTitle('Available Commands') + .setDescription(helpMessage) + .setFooter({ text: `${commandDetails.length} commands loaded.` }); + await interaction.reply({ embeds: [embed], ephemeral: true }); }, -}; \ No newline at end of file +}; diff --git a/commands/utility/ping.js b/commands/utility/ping.js index 2b6a058..c694e32 100644 --- a/commands/utility/ping.js +++ b/commands/utility/ping.js @@ -3,8 +3,15 @@ const { SlashCommandBuilder } = require('discord.js'); module.exports = { data: new SlashCommandBuilder() .setName('ping') - .setDescription('Check if the bots responsivnes'), + .setDescription('Check the bot\'s responsiveness.'), async execute(interaction) { - await interaction.reply({ content: 'Pong! (Online)', ephemeral: true }); + const sentAt = Date.now(); + await interaction.reply({ content: 'Pinging...', ephemeral: true }); + const latency = Date.now() - sentAt; + const apiLatency = Math.round(interaction.client.ws.ping); + + await interaction.editReply({ + content: `Pong! **${latency}ms** (API: **${apiLatency}ms**)`, + }); }, -}; \ No newline at end of file +};