diff --git a/commands/Items/buy-item.js b/commands/Items/buy-item.js index fbf7c3aa..f7f322c8 100644 --- a/commands/Items/buy-item.js +++ b/commands/Items/buy-item.js @@ -1,6 +1,8 @@ const Command = require('../../base/Command.js'); const { EmbedBuilder } = require('discord.js'); const { QuickDB } = require('quick.db'); +require('moment-duration-format'); +const moment = require('moment'); const db = new QuickDB(); class BuyItem extends Command { @@ -33,30 +35,90 @@ class BuyItem extends Command { let userCash = BigInt(await db.get(`servers.${msg.guild.id}.users.${msg.member.id}.economy.cash`)); if (userCash < itemCost) return msg.reply('You do not have enough money to buy this item.'); - const roleRequired = item.roleRequired; - if (roleRequired) { - const role = this.client.util.getRole(msg, roleRequired); - if (role) { + if (item.roleRequired) { + const roleRequired = this.client.util.getRole(msg, item.roleRequired); + if (roleRequired) { // Check if the member has the role - const hasRole = msg.member.roles.cache.has(role.id); + const hasRole = msg.member.roles.cache.has(roleRequired.id); if (!hasRole) { - return msg.reply(`You do not have the required role: ${role.name}`); + return msg.reply(`You do not have the required role **${roleRequired.name}** to purchase this item.`); } } else { return msg.reply('The required role specified does not exist.'); } } + if (item.roleGiven || item.roleRemoved) { + if (!msg.guild.members.me.permissions.has('ManageRoles')) + return this.client.util.errorEmbed( + msg, + 'Manage Roles permission is required on the bot to buy this item.', + 'Missing Permission', + ); + } + // Deduct the cost from the user's cash userCash = userCash - itemCost; await db.set(`servers.${msg.guild.id}.users.${msg.member.id}.economy.cash`, userCash.toString()); + if (item.roleGiven) { + const role = this.client.util.getRole(msg, item.roleGiven); + await msg.member.roles.add(role).catch((error) => msg.channel.send(error)); + } + if (item.roleRemoved) { + const role = this.client.util.getRole(msg, item.roleRemoved); + await msg.member.roles.remove(role).catch((error) => msg.channel.send(error)); + } + if (!item.inventory) { if (!item.replyMessage) { return msg.channel.send('👍'); } - return msg.channel.send(item.replyMessage); + // Replace Member + const memberCreatedAt = moment(msg.author.createdAt); + const memberCreated = memberCreatedAt.format('D MM YY'); + const memberCreatedDuration = memberCreatedAt.from(moment(), true); + let replyMessage = item.replyMessage + .replace('{member.id}', msg.author.id) + .replace('{member.username}', msg.author.username) + .replace('{member.tag}', msg.author.tag) + .replace('{member.mention}', msg.author) + .replace('{member.created}', memberCreated) + .replace('{member.created.duration}', memberCreatedDuration); + + // Replace Server + const guildCreatedAt = moment(msg.guild.createdAt); + const serverCreated = guildCreatedAt.format('D MM YY'); + const serverCreatedDuration = guildCreatedAt.from(moment(), true); + + replyMessage = replyMessage + .replace('{server.id}', msg.guild.id) + .replace('{server.name}', msg.guild.name) + .replace('{server.members}', msg.guild.memberCount.toLocaleString()) + .replace('{server.created}', serverCreated) + .replace('{servers.created.duration', serverCreatedDuration); + + const role = + (await this.client.util.getRole(msg, item.roleGiven)) || + (await this.client.util.getRole(msg, item.roleRemoved)) || + (await this.client.util.getRole(msg, item.roleRequired)); + + console.log(role); + if (role) { + const roleCreatedAt = moment(role.createdAt); + const roleCreated = roleCreatedAt.format('D MM YY'); + const roleCreatedDuration = roleCreatedAt.from(moment(), true); + + replyMessage = replyMessage + .replace('{role.id}', role.id) + .replace('{role.name}', role.name) + .replace('{role.mention}', role) + .replace('{role.members}', role.members.size.toLocaleString()) + .replace('{role.created}', roleCreated) + .replace('{role.created.duration}', roleCreatedDuration); + } + return msg.channel.send(replyMessage); } const userInventory = (await db.get(`servers.${msg.guild.id}.users.${msg.member.id}.economy.inventory`)) || []; diff --git a/commands/Items/create-item.js b/commands/Items/create-item.js index 101d1dae..2e7cfa46 100644 --- a/commands/Items/create-item.js +++ b/commands/Items/create-item.js @@ -1,8 +1,6 @@ const Command = require('../../base/Command.js'); const { EmbedBuilder } = require('discord.js'); const { QuickDB } = require('quick.db'); -require('moment-duration-format'); -const moment = require('moment'); const db = new QuickDB(); class CreateItem extends Command { @@ -74,18 +72,18 @@ class CreateItem extends Command { if (isNaN(cost)) { await collected .first() - .reply('The cost must be a valid number. Please enter the cost again or type `cancel` to exit.'); + .reply('The cost must be a valid number. Please enter the price again or type `cancel` to exit.'); } else if (cost === Infinity) { - await collected.first().reply(`The cost must be less than ${BigInt(Number.MAX_VALUE).toLocaleString()}.`); + await collected.first().reply(`The price must be less than ${BigInt(Number.MAX_VALUE).toLocaleString()}.`); } else if (cost < 0) { - await msg.reply('The cost must be at least zero. Please enter a valid cost.'); + await msg.reply('The price must be at least zero. Please enter a valid cost.'); } else { isValid = true; } } const currencySymbol = (await db.get(`servers.${msg.guild.id}.economy.symbol`)) || '$'; - embed.addFields([{ name: 'Cost', value: currencySymbol + cost.toLocaleString(), inline: true }]); + embed.addFields([{ name: 'Price', value: currencySymbol + cost.toLocaleString(), inline: true }]); await message.edit({ content: '2️⃣ What would you like the description to be? \nThis should be no more than 1000 characters', embeds: [embed], @@ -161,7 +159,7 @@ class CreateItem extends Command { } } } - embed.addFields([{ name: 'Show in inventory?', value: inventory ? 'True' : 'False', inline: true }]); + embed.addFields([{ name: 'Show in inventory?', value: inventory ? 'Yes' : 'No', inline: true }]); await message.edit({ content: @@ -213,7 +211,8 @@ class CreateItem extends Command { ]); await message.edit({ - content: '5️⃣ What role should be required to purchase this item? \nIf none, just reply `skip`.', + content: + '5️⃣ What role do you want to be given when this item is bought (or used if an inventory item)? \nIf none, just reply `skip`.', embeds: [embed], }); @@ -262,7 +261,56 @@ class CreateItem extends Command { await message.edit({ content: - '6️⃣ What message do you want the bot to reply with, when the item is bought (or used if an inventory item)? \nYou can use the Member, Server & Role tags from https://unbelievaboat.com/tags in this message. \nIf none, just reply `skip`.', + '6️⃣ What role do you want to be removed from the user when this item is bought (or used if an inventory item)?\nIf none, just reply `skip`.', + embeds: [embed], + }); + + isValid = false; + let roleRemoved; + + while (!isValid) { + collected = await msg.channel + .awaitMessages({ + filter, + max: 1, + time: 60000, + errors: ['time'], + }) + .catch(() => null); + if (!collected) { + return msg.reply('You did not reply in time, the command has been cancelled.'); + } + + const response = collected.first().content.toLowerCase(); + if (response === 'cancel') { + return collected.first().reply('The command has been cancelled.'); + } + if (response === 'skip') { + roleRemoved = null; + isValid = true; + break; + } + + roleRemoved = this.client.util.getRole(msg, collected.first().content); + + if (!roleRemoved) { + collected.first().reply('Please reply with a valid server role.'); + } else { + roleRemoved = roleRemoved.id; + isValid = true; + } + } + embed.addFields([ + { + name: 'Role Removed', + value: roleRemoved ? this.client.util.getRole(msg, roleRemoved)?.toString() : 'None', + inline: true, + }, + ]); + + await message.edit({ + content: + '7️⃣ What message do you want the bot to reply with, when the item is bought (or used if an inventory item)? \nYou can use the Member, Server & Role tags from https://unbelievaboat.com/tags in this message. \nIf none, just reply `skip`.', embeds: [embed], }); @@ -283,45 +331,7 @@ class CreateItem extends Command { return collected.first().reply('The command has been cancelled.'); } - // Replace Member - const memberCreatedAt = moment(msg.author.createdAt); - const memberCreated = memberCreatedAt.format('D MM YY'); - const memberCreatedDuration = memberCreatedAt.from(moment(), true); - replyMessage = collected - .first() - .content.replace('{member.id}', msg.author.id) - .replace('{member.username}', msg.author.username) - .replace('{member.tag}', msg.author.tag) - .replace('{member.mention}', msg.author) - .replace('{member.created}', memberCreated) - .replace('{member.created.duration}', memberCreatedDuration); - - // Replace Server - const guildCreatedAt = moment(msg.guild.createdAt); - const serverCreated = guildCreatedAt.format('D MM YY'); - const serverCreatedDuration = guildCreatedAt.from(moment(), true); - - replyMessage = replyMessage - .replace('{server.id}', msg.guild.id) - .replace('{server.name}', msg.guild.name) - .replace('{server.members}', msg.guild.memberCount.toLocaleString()) - .replace('{server.created}', serverCreated) - .replace('{servers.created.duration', serverCreatedDuration); - - const role = this.client.util.getRole(msg, roleRequired); // || this.client.util.getRole(msg, roleGiven); - if (role) { - const roleCreatedAt = moment(role.createdAt); - const roleCreated = roleCreatedAt.format('D MM YY'); - const roleCreatedDuration = roleCreatedAt.from(moment(), true); - - replyMessage = replyMessage - .replace('{role.id}', role.id) - .replace('{role.name}', role.name) - .replace('{role.mention}', role) - .replace('{role.members}', role.members.size.toLocaleString()) - .replace('{role.created}', roleCreated) - .replace('{role.created.duration}', roleCreatedDuration); - } + replyMessage = collected.first().content; if (replyMessage.toLowerCase() === 'skip') replyMessage = false; @@ -333,6 +343,7 @@ class CreateItem extends Command { inventory, roleRequired, roleGiven, + roleRemoved, replyMessage, }; diff --git a/commands/Items/edit-item.js b/commands/Items/edit-item.js index 1362ce39..1d92066e 100644 --- a/commands/Items/edit-item.js +++ b/commands/Items/edit-item.js @@ -13,37 +13,49 @@ class EditItem extends Command { usage: 'edit-item "" ', aliases: ['edititem'], permLevel: 'Administrator', + examples: ['edit-item name pizzza pizza', 'edit-item price "Large Crate" 100'], guildOnly: true, - requiredArgs: 3, + requiredArgs: 2, }); } async run(msg, args) { const attribute = args.shift().toLowerCase(); - const itemNameStartIndex = args.findIndex((arg) => arg.startsWith('"')); - const itemNameEndIndex = args.findIndex((arg) => arg.endsWith('"')); + let itemName; + let newValue; - if (itemNameStartIndex === -1 || itemNameEndIndex === -1) { - return msg.reply('Please enclose the item name in double quotes.'); + if (args[0].startsWith('"')) { + // Find the ending index of the item name enclosed in double quotes + const itemNameEndIndex = args.findIndex((arg) => arg.endsWith('"')); + if (itemNameEndIndex === -1) { + return msg.reply('Please enclose the item name in double quotes.'); + } + + // Extract the item name and remove the double quotes + itemName = args + .slice(0, itemNameEndIndex + 1) + .join(' ') + .replace(/"/g, ''); + newValue = args.slice(itemNameEndIndex + 1).join(' '); + } else { + // The first argument is the item name without spaces + itemName = args.shift(); + newValue = args.join(' '); } - const itemName = args - .slice(itemNameStartIndex, itemNameEndIndex + 1) - .join(' ') - .replace(/"/g, ''); - const newValue = args.slice(itemNameEndIndex + 1).join(' '); + // Proceed with using itemName and newValue const store = (await db.get(`servers.${msg.guild.id}.economy.store`)) || {}; // Find the item in the store regardless of case - const itemKey = Object.keys(store).find((key) => key.toLowerCase() === itemName.toLowerCase()); + let itemKey = Object.keys(store).find((key) => key.toLowerCase() === itemName.toLowerCase()); if (!itemKey) return msg.reply('That item does not exist in the store.'); const item = store[itemKey]; switch (attribute) { - case 'name': + case 'name': { // Ensure the new name is not already taken const newItemKey = newValue.toLowerCase(); if (Object.keys(store).find((key) => key.toLowerCase() === newItemKey)) { @@ -52,8 +64,11 @@ class EditItem extends Command { // Update the item name store[newValue] = item; delete store[itemKey]; + itemKey = newValue; // Update the reference to the new item key break; - case 'price': + } + + case 'price': { const price = parseInt(newValue, 10); if (isNaN(price) || price < 0) { return msg.reply('Please provide a valid price.'); @@ -61,20 +76,109 @@ class EditItem extends Command { item.cost = price; store[itemKey] = item; break; - case 'description': - item.description = newValue; + } + + case 'description': { + item.description = newValue.slice(0, 1000); + store[itemKey] = item; + break; + } + + case 'inventory': { + if (['yes', 'no'].includes(newValue.toLowerCase())) { + item.inventory = newValue.toLowerCase() === 'yes'; + store[itemKey] = item; + } else { + return msg.reply('Please respond with "yes" or "no" for inventory.'); + } + break; + } + + case 'role-required': { + if (!newValue) { + item.roleRequired = null; + break; + } + const role = this.client.util.getRole(msg, newValue); + if (!role) { + return msg.reply('Please re-run the command with a valid role.'); + } + item.roleRequired = role.id; + store[itemKey] = item; + break; + } + + case 'role-given': { + if (!newValue) { + item.roleGiven = null; + break; + } + const role = this.client.util.getRole(msg, newValue); + if (!role) { + return msg.reply('Please re-run the command with a valid role.'); + } + item.roleGiven = role.id; store[itemKey] = item; break; + } + + case 'role-removed': { + if (!newValue) { + item.roleRemoved = null; + break; + } + const role = this.client.util.getRole(msg, newValue); + if (!role) { + return msg.reply('Please re-run the command with a valid role.'); + } + item.roleRemoved = role.id; + store[itemKey] = item; + break; + } + + case 'reply-message': { + if (!newValue) { + item.replyMessage = null; + break; + } + item.replyMessage = newValue.slice(0, 1000); + store[itemKey] = item; + break; + } + default: - return msg.reply('Invalid attribute. You can only edit name, price, or description.'); + return msg.reply( + 'Invalid attribute. You can only edit name, price, description, inventory, role-required, role-given, role-removed or reply-message.', + ); } await db.set(`servers.${msg.guild.id}.economy.store`, store); const embed = new EmbedBuilder() .setTitle('Item Edited') - .setDescription(`The **${attribute}** of **${itemKey}** has been updated to **${newValue}**.`) .setColor(msg.settings.embedColor) + .addFields([ + { name: 'Name', value: itemKey, inline: true }, + { name: 'Price', value: BigInt(item.cost).toLocaleString(), inline: true }, + { name: 'Description', value: item.description, inline: false }, + { name: 'Show in Inventory?', value: item.inventory ? 'Yes' : 'No', inline: true }, + { + name: 'Role Required', + value: item.roleRequired ? this.client.util.getRole(msg, item.roleRequired).toString() : 'None', + inline: true, + }, + { + name: 'Role Given', + value: item.roleGiven ? this.client.util.getRole(msg, item.roleGiven).toString() : 'None', + inline: true, + }, + { + name: 'Role Removed', + value: item.roleRemoved ? this.client.util.getRole(msg, item.roleRemoved).toString() : 'None', + inline: true, + }, + { name: 'Reply Message', value: item.replyMessage || 'None', inline: true }, + ]) .setTimestamp(); return msg.channel.send({ embeds: [embed] }); diff --git a/commands/Items/inventory.js b/commands/Items/inventory.js index c7af7477..16fd56e8 100644 --- a/commands/Items/inventory.js +++ b/commands/Items/inventory.js @@ -24,6 +24,7 @@ class Inventory extends Command { } else if (args.length === 1) { if (!parseInt(args[0])) { mem = await this.client.util.getMember(msg, args[0]); + if (!mem) mem = msg.member; } else { page = parseInt(args[0]?.replace(/[^0-9\\.]/g, '') || 1); } @@ -43,7 +44,7 @@ class Inventory extends Command { const inventoryDetails = paginatedInventory .map((item) => { - return `**${item.name}** - ${BigInt(item.cost || 0)?.toLocaleString()}\n${item.description}`; + return `**${item?.name}** - ${BigInt(item?.cost || 0)?.toLocaleString()}\n${item?.description}`; }) .join('\n'); diff --git a/commands/Items/item-info.js b/commands/Items/item-info.js index 070447b7..8724fdb3 100644 --- a/commands/Items/item-info.js +++ b/commands/Items/item-info.js @@ -42,12 +42,22 @@ class ItemInfo extends Command { { name: 'Name', value: itemKey, inline: true }, { name: 'Cost', value: currencySymbol + BigInt(item.cost).toLocaleString(), inline: true }, { name: 'Description', value: item.description, inline: false }, - { name: 'Show in Inventory?', value: item.inventory ? 'True' : 'False', inline: true }, + { name: 'Show in Inventory?', value: item.inventory ? 'Yes' : 'No', inline: true }, { name: 'Role Required', value: item.roleRequired ? this.client.util.getRole(msg, item.roleRequired).toString() : 'None', inline: true, }, + { + name: 'Role Given', + value: item.roleGiven ? this.client.util.getRole(msg, item.roleGiven).toString() : 'None', + inline: true, + }, + { + name: 'Role Removed', + value: item.roleRemoved ? this.client.util.getRole(msg, item.roleRemoved).toString() : 'None', + inline: true, + }, { name: 'Reply Message', value: item.replyMessage || 'None', inline: true }, ]); return msg.channel.send({ embeds: [embed] }); diff --git a/events/Member/guildMemberAdd.mjs b/events/Member/guildMemberAdd.mjs index 64743c89..bf603a2f 100644 --- a/events/Member/guildMemberAdd.mjs +++ b/events/Member/guildMemberAdd.mjs @@ -45,7 +45,7 @@ export async function run(client, member) { if (!roles) return; for (let i = 0; i < roles.length; i++) { - member.roles.add(roles[i]); + await member.roles.add(roles[i]).catch((error) => console.error(error)); await setTimeoutPromise(1000); }