From de14723df03f7246e4287b57a9ae68470ed1f786 Mon Sep 17 00:00:00 2001 From: Ethan Lane Date: Sat, 11 May 2024 12:48:48 +0100 Subject: [PATCH 1/5] Update give command to allow currency to be given (#220) # Description Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change. - Update the give command to have the existing card functionality moved to a subcommand - Add a subcommand to the give command to allow currency to be given #206 ## Type of change Please delete options that are not relevant. - [x] New feature (non-breaking change which adds functionality) # How Has This Been Tested? Please describe the tests that you ran to verify the changes. Provide instructions so we can reproduce. Please also list any relevant details to your test configuration. # Checklist - [ ] My code follows the style guidelines of this project - [ ] I have performed a self-review of my own code - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [ ] My changes generate no new warnings - [ ] I have added tests that provde my fix is effective or that my feature works - [ ] New and existing unit tests pass locally with my changes - [ ] Any dependent changes have been merged and published in downstream modules Reviewed-on: https://git.vylpes.xyz/External/card-drop/pulls/220 Reviewed-by: VylpesTester Co-authored-by: Ethan Lane Co-committed-by: Ethan Lane --- src/buttonEvents/Claim.ts | 2 +- src/commands/give.ts | 81 ++++++++++++++++++++++++------- src/database/entities/app/User.ts | 4 ++ src/type/command.ts | 5 +- 4 files changed, 72 insertions(+), 20 deletions(-) diff --git a/src/buttonEvents/Claim.ts b/src/buttonEvents/Claim.ts index 9d29f7b..f15e1ae 100644 --- a/src/buttonEvents/Claim.ts +++ b/src/buttonEvents/Claim.ts @@ -42,7 +42,7 @@ export default class Claim extends ButtonEvent { await inventory.Save(Inventory, inventory); - let user = await User.FetchOneById(User, userId) || new User(userId, 300); + const user = await User.FetchOneById(User, userId) || new User(userId, 300); AppLogger.LogSilly("Button/Claim", `${user.Id} has ${user.Currency} currency`); diff --git a/src/commands/give.ts b/src/commands/give.ts index 3656dc5..ebdebe4 100644 --- a/src/commands/give.ts +++ b/src/commands/give.ts @@ -5,6 +5,7 @@ import Config from "../database/entities/app/Config"; import CardDropHelperMetadata from "../helpers/CardDropHelperMetadata"; import Inventory from "../database/entities/app/Inventory"; import AppLogger from "../client/appLogger"; +import User from "../database/entities/app/User"; export default class Give extends Command { constructor() { @@ -14,19 +15,57 @@ export default class Give extends Command { .setName("give") .setDescription("Give a user a card manually, in case bot breaks") .setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator) - .addStringOption(x => + .addSubcommand(x => x - .setName("cardnumber") - .setDescription("G") - .setRequired(true)) - .addUserOption(x => + .setName("card") + .setDescription("Give a user a card manually") + .addStringOption(x => + x + .setName("cardnumber") + .setDescription("The card to give") + .setRequired(true)) + .addUserOption(x => + x + .setName("user") + .setDescription("The user to give the card to") + .setRequired(true))) + .addSubcommand(x => x - .setName("user") - .setDescription("The user to give the card to") - .setRequired(true)); + .setName("currency") + .setDescription("Give a user currency manually") + .addNumberOption(x => + x + .setName("amount") + .setDescription("The amount to give") + .setRequired(true)) + .addUserOption(x => + x + .setName("user") + .setDescription("The user to give the currency to") + .setRequired(true))); } public override async execute(interaction: CommandInteraction) { + if (!interaction.isChatInputCommand()) return; + + const whitelistedUsers = process.env.BOT_ADMINS!.split(","); + + if (!whitelistedUsers.find(x => x == interaction.user.id)) { + await interaction.reply("Only whitelisted users can use this command."); + return; + } + + switch (interaction.options.getSubcommand()) { + case "card": + await this.GiveCard(interaction); + break; + case "currency": + await this.GiveCurrency(interaction); + break; + } + } + + private async GiveCard(interaction: CommandInteraction) { if (!CoreClient.AllowDrops) { await interaction.reply("Bot is currently syncing, please wait until its done."); return; @@ -37,17 +76,10 @@ export default class Give extends Command { return; } - const whitelistedUsers = process.env.BOT_ADMINS!.split(","); - - if (!whitelistedUsers.find(x => x == interaction.user.id)) { - await interaction.reply("Only whitelisted users can use this command."); - return; - } - const cardNumber = interaction.options.get("cardnumber", true); const user = interaction.options.getUser("user", true); - AppLogger.LogSilly("Commands/Give", `Parameters: cardNumber=${cardNumber.value}, user=${user.id}`); + AppLogger.LogSilly("Commands/Give/GiveCard", `Parameters: cardNumber=${cardNumber.value}, user=${user.id}`); const card = CardDropHelperMetadata.GetCardByCardNumber(cardNumber.value!.toString()); @@ -66,6 +98,21 @@ export default class Give extends Command { await inventory.Save(Inventory, inventory); - await interaction.reply(`${card.card.name} given to ${user.username}, they now have ${inventory.Quantity}`); + await interaction.reply(`Card ${card.card.name} given to ${user.username}, they now have ${inventory.Quantity}`); + } + + private async GiveCurrency(interaction: CommandInteraction) { + const amount = interaction.options.get("amount", true); + const user = interaction.options.getUser("user", true); + + AppLogger.LogSilly("Commands/Give/GiveCurrency", `Parameters: amount=${amount.value} user=${user.id}`); + + const userEntity = await User.FetchOneById(User, user.id) || new User(user.id, 300); + + userEntity.AddCurrency(amount.value! as number); + + await userEntity.Save(User, userEntity); + + await interaction.reply(`${amount.value} currency ${amount.value! as number >= 0 ? "given to" : "taken from"} ${user.username}, they now have ${userEntity.Currency}`); } } \ No newline at end of file diff --git a/src/database/entities/app/User.ts b/src/database/entities/app/User.ts index 649c1c1..4c5798d 100644 --- a/src/database/entities/app/User.ts +++ b/src/database/entities/app/User.ts @@ -17,6 +17,10 @@ export default class User extends AppBaseEntity { this.Currency = currency; } + public AddCurrency(amount: number) { + this.Currency += amount; + } + public RemoveCurrency(amount: number): boolean { if (this.Currency < amount) return false; diff --git a/src/type/command.ts b/src/type/command.ts index 20f5e3a..458a81e 100644 --- a/src/type/command.ts +++ b/src/type/command.ts @@ -1,7 +1,8 @@ -import { CommandInteraction, SlashCommandBuilder } from "discord.js"; +import { CommandInteraction } from "discord.js"; export abstract class Command { - public CommandBuilder: Omit; + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- CommandBuilder type is dynamic depending on options and can't be strictly typed + public CommandBuilder: any; abstract execute(interaction: CommandInteraction): Promise; } From b9264e23dbae45faefac9d3129927954368cbedc Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Mon, 13 May 2024 15:15:21 +0100 Subject: [PATCH 2/5] Update dependency @types/node to v20.12.11 (#221) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/node) ([source](https://github.com/DefinitelyTyped/DefinitelyTyped)) | devDependencies | patch | [`20.12.8` -> `20.12.11`](https://renovatebot.com/diffs/npm/@types%2fnode/20.12.8/20.12.11) | --- ### Configuration ๐Ÿ“… **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined). ๐Ÿšฆ **Automerge**: Disabled by config. Please merge this manually once you are satisfied. โ™ป **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. ๐Ÿ”• **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). Reviewed-on: https://git.vylpes.xyz/External/card-drop/pulls/221 Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9d65b45..d87e6be 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1900,9 +1900,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "20.12.8", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.8.tgz", - "integrity": "sha512-NU0rJLJnshZWdE/097cdCBbyW1h4hEg0xpovcoAQYHl8dnEyp/NAOiE45pvc+Bd1Dt+2r94v2eGFpQJ4R7g+2w==", + "version": "20.12.11", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.11.tgz", + "integrity": "sha512-vDg9PZ/zi+Nqp6boSOT7plNuthRugEKixDv5sFTIpkE89MmNtEArAShI4mxuX2+UrLEe9pxC1vm2cjm9YlWbJw==", "dependencies": { "undici-types": "~5.26.4" } From 5a8ec932b41e574187904cb38fb0d42d86e0cd92 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Mon, 13 May 2024 15:18:20 +0100 Subject: [PATCH 3/5] Update dependency glob to v10.3.15 (#222) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [glob](https://github.com/isaacs/node-glob) | dependencies | patch | [`10.3.12` -> `10.3.15`](https://renovatebot.com/diffs/npm/glob/10.3.12/10.3.15) | --- ### Release Notes
isaacs/node-glob (glob) ### [`v10.3.15`](https://github.com/isaacs/node-glob/compare/v10.3.14...v10.3.15) [Compare Source](https://github.com/isaacs/node-glob/compare/v10.3.14...v10.3.15) ### [`v10.3.14`](https://github.com/isaacs/node-glob/compare/v10.3.13...v10.3.14) [Compare Source](https://github.com/isaacs/node-glob/compare/v10.3.13...v10.3.14) ### [`v10.3.13`](https://github.com/isaacs/node-glob/compare/v10.3.12...v10.3.13) [Compare Source](https://github.com/isaacs/node-glob/compare/v10.3.12...v10.3.13)
--- ### Configuration ๐Ÿ“… **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined). ๐Ÿšฆ **Automerge**: Disabled by config. Please merge this manually once you are satisfied. โ™ป **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. ๐Ÿ”• **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). Reviewed-on: https://git.vylpes.xyz/External/card-drop/pulls/222 Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- package-lock.json | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index d87e6be..6d144ca 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5154,21 +5154,21 @@ "license": "MIT" }, "node_modules/glob": { - "version": "10.3.12", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.12.tgz", - "integrity": "sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg==", + "version": "10.3.15", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.15.tgz", + "integrity": "sha512-0c6RlJt1TICLyvJYIApxb8GsXoai0KUP7AxKKAtsYXdgJR1mGEUa7DgwShbdk1nly0PYoZj01xd4hzbq3fsjpw==", "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^2.3.6", "minimatch": "^9.0.1", "minipass": "^7.0.4", - "path-scurry": "^1.10.2" + "path-scurry": "^1.11.0" }, "bin": { "glob": "dist/esm/bin.mjs" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=16 || 14 >=14.18" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -9335,15 +9335,15 @@ "license": "MIT" }, "node_modules/path-scurry": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.2.tgz", - "integrity": "sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=16 || 14 >=14.18" }, "funding": { "url": "https://github.com/sponsors/isaacs" From 94ad819c95bb67c8c07f643db39c38a7835f3883 Mon Sep 17 00:00:00 2001 From: Ethan Lane Date: Thu, 16 May 2024 18:46:26 +0100 Subject: [PATCH 4/5] Add confirmation button event --- src/buttonEvents/Sacrifice.ts | 147 +++++++++++++++++++++++++ src/commands/sacrifice.ts | 2 +- src/database/entities/app/Inventory.ts | 6 + src/registry.ts | 2 + 4 files changed, 156 insertions(+), 1 deletion(-) create mode 100644 src/buttonEvents/Sacrifice.ts diff --git a/src/buttonEvents/Sacrifice.ts b/src/buttonEvents/Sacrifice.ts new file mode 100644 index 0000000..440e37b --- /dev/null +++ b/src/buttonEvents/Sacrifice.ts @@ -0,0 +1,147 @@ +import { ActionRowBuilder, ButtonBuilder, ButtonInteraction, ButtonStyle, EmbedBuilder } from "discord.js"; +import { ButtonEvent } from "../type/buttonEvent"; +import Inventory from "../database/entities/app/Inventory"; +import CardDropHelperMetadata from "../helpers/CardDropHelperMetadata"; +import { CardRarityToString, GetSacrificeAmount } from "../constants/CardRarity"; +import EmbedColours from "../constants/EmbedColours"; +import User from "../database/entities/app/User"; + +export default class Sacrifice extends ButtonEvent { + public override async execute(interaction: ButtonInteraction) { + const subcommand = interaction.customId.split(" ")[1]; + + switch(subcommand) { + case "confirm": + await this.confirm(interaction); + break; + case "cancel": + await this.cancel(interaction); + break; + } + } + + private async confirm(interaction: ButtonInteraction) { + const userId = interaction.customId.split(" ")[2]; + const cardNumber = interaction.customId.split(" ")[3]; + + const cardInInventory = await Inventory.FetchOneByCardNumberAndUserId(userId, cardNumber); + + if (!cardInInventory) { + await interaction.reply("Unable to find card in inventory."); + return; + } + + const cardData = CardDropHelperMetadata.GetCardByCardNumber(cardNumber); + + if (!cardData) { + await interaction.reply("Unable to find card in the database."); + return; + } + + const user = await User.FetchOneById(User, userId); + + if (!user) { + await interaction.reply("Unable to find user in database."); + return; + } + + cardInInventory.RemoveQuantity(1); + + await cardInInventory.Save(Inventory, cardInInventory); + + const cardValue = GetSacrificeAmount(cardData.card.type); + const cardRarityString = CardRarityToString(cardData.card.type); + + user.AddCurrency(cardValue); + + await user.Save(User, user); + + let description = [ + `Card: ${cardData.card.name}`, + `Series: ${cardData.series.name}`, + `Rarity: ${cardRarityString}`, + `Quantity Owned: ${cardInInventory.Quantity}`, + `Sacrifice Amount: ${cardValue}`, + ]; + + const embed = new EmbedBuilder() + .setTitle("Card Sacrificed") + .setDescription(description.join("\n")) + .setColor(EmbedColours.Ok) + .setFooter({ text: `${interaction.user.username} ยท ${cardData.card.name}` }); + + const row = new ActionRowBuilder() + .addComponents([ + new ButtonBuilder() + .setCustomId(`sacrifice confirm ${interaction.user.id} ${cardNumber}`) + .setLabel("Confirm") + .setStyle(ButtonStyle.Success) + .setDisabled(true), + new ButtonBuilder() + .setCustomId(`sacrifice cancel`) + .setLabel("Cancel") + .setStyle(ButtonStyle.Secondary) + .setDisabled(true), + ]); + + await interaction.update({ + embeds: [ embed ], + components: [ row ], + }); + } + + private async cancel(interaction: ButtonInteraction) { + const userId = interaction.customId.split(" ")[2]; + const cardNumber = interaction.customId.split(" ")[3]; + + const cardInInventory = await Inventory.FetchOneByCardNumberAndUserId(userId, cardNumber); + + if (!cardInInventory) { + await interaction.reply("Unable to find card in inventory."); + return; + } + + const cardData = CardDropHelperMetadata.GetCardByCardNumber(cardNumber); + + if (!cardData) { + await interaction.reply("Unable to find card in the database."); + return; + } + + const cardValue = GetSacrificeAmount(cardData.card.type); + const cardRarityString = CardRarityToString(cardData.card.type); + + let description = [ + `Card: ${cardData.card.name}`, + `Series: ${cardData.series.name}`, + `Rarity: ${cardRarityString}`, + `Quantity Owned: ${cardInInventory.Quantity}`, + `Sacrifice Amount: ${cardValue}`, + ]; + + const embed = new EmbedBuilder() + .setTitle("Sacrifice Cancelled") + .setDescription(description.join("\n")) + .setColor(EmbedColours.Error) + .setFooter({ text: `${interaction.user.username} ยท ${cardData.card.name}` }); + + const row = new ActionRowBuilder() + .addComponents([ + new ButtonBuilder() + .setCustomId(`sacrifice confirm ${interaction.user.id} ${cardNumber}`) + .setLabel("Confirm") + .setStyle(ButtonStyle.Success) + .setDisabled(true), + new ButtonBuilder() + .setCustomId(`sacrifice cancel`) + .setLabel("Cancel") + .setStyle(ButtonStyle.Secondary) + .setDisabled(true), + ]); + + await interaction.update({ + embeds: [ embed ], + components: [ row ], + }); + } +} \ No newline at end of file diff --git a/src/commands/sacrifice.ts b/src/commands/sacrifice.ts index 96b3bf4..fb54c76 100644 --- a/src/commands/sacrifice.ts +++ b/src/commands/sacrifice.ts @@ -60,7 +60,7 @@ export default class Sacrifice extends Command { .setLabel("Confirm") .setStyle(ButtonStyle.Success), new ButtonBuilder() - .setCustomId(`sacrifice cancel`) + .setCustomId(`sacrifice cancel ${interaction.user.id} ${cardnumber.value!}`) .setLabel("Cancel") .setStyle(ButtonStyle.Secondary), ]); diff --git a/src/database/entities/app/Inventory.ts b/src/database/entities/app/Inventory.ts index bde4450..a5d0026 100644 --- a/src/database/entities/app/Inventory.ts +++ b/src/database/entities/app/Inventory.ts @@ -29,6 +29,12 @@ export default class Inventory extends AppBaseEntity { this.Quantity = quantity; } + public RemoveQuantity(amount: number) { + if (this.Quantity < amount) return; + + this.Quantity -= amount; + } + public AddClaim(claim: Claim) { this.Claims.push(claim); } diff --git a/src/registry.ts b/src/registry.ts index 1694e47..8e6a713 100644 --- a/src/registry.ts +++ b/src/registry.ts @@ -21,6 +21,7 @@ import Droprarity from "./commands/stage/droprarity"; import Claim from "./buttonEvents/Claim"; import InventoryButtonEvent from "./buttonEvents/Inventory"; import Reroll from "./buttonEvents/Reroll"; +import SacrificeButtonEvent from "./buttonEvents/Sacrifice"; import SeriesEvent from "./buttonEvents/Series"; import TradeButtonEvent from "./buttonEvents/Trade"; @@ -51,6 +52,7 @@ export default class Registry { CoreClient.RegisterButtonEvent("claim", new Claim()); CoreClient.RegisterButtonEvent("inventory", new InventoryButtonEvent()); CoreClient.RegisterButtonEvent("reroll", new Reroll()); + CoreClient.RegisterButtonEvent("sacrifice", new SacrificeButtonEvent()); CoreClient.RegisterButtonEvent("series", new SeriesEvent()); CoreClient.RegisterButtonEvent("trade", new TradeButtonEvent()); } From 43998fbfc83c24045fe49e7ef2703397f32cfee8 Mon Sep 17 00:00:00 2001 From: Ethan Lane Date: Thu, 16 May 2024 18:47:43 +0100 Subject: [PATCH 5/5] Fix linting issues --- src/buttonEvents/Sacrifice.ts | 8 ++++---- src/commands/sacrifice.ts | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/buttonEvents/Sacrifice.ts b/src/buttonEvents/Sacrifice.ts index 440e37b..6c4a1a6 100644 --- a/src/buttonEvents/Sacrifice.ts +++ b/src/buttonEvents/Sacrifice.ts @@ -56,7 +56,7 @@ export default class Sacrifice extends ButtonEvent { await user.Save(User, user); - let description = [ + const description = [ `Card: ${cardData.card.name}`, `Series: ${cardData.series.name}`, `Rarity: ${cardRarityString}`, @@ -78,7 +78,7 @@ export default class Sacrifice extends ButtonEvent { .setStyle(ButtonStyle.Success) .setDisabled(true), new ButtonBuilder() - .setCustomId(`sacrifice cancel`) + .setCustomId("sacrifice cancel") .setLabel("Cancel") .setStyle(ButtonStyle.Secondary) .setDisabled(true), @@ -111,7 +111,7 @@ export default class Sacrifice extends ButtonEvent { const cardValue = GetSacrificeAmount(cardData.card.type); const cardRarityString = CardRarityToString(cardData.card.type); - let description = [ + const description = [ `Card: ${cardData.card.name}`, `Series: ${cardData.series.name}`, `Rarity: ${cardRarityString}`, @@ -133,7 +133,7 @@ export default class Sacrifice extends ButtonEvent { .setStyle(ButtonStyle.Success) .setDisabled(true), new ButtonBuilder() - .setCustomId(`sacrifice cancel`) + .setCustomId("sacrifice cancel") .setLabel("Cancel") .setStyle(ButtonStyle.Secondary) .setDisabled(true), diff --git a/src/commands/sacrifice.ts b/src/commands/sacrifice.ts index fb54c76..c6dc7b3 100644 --- a/src/commands/sacrifice.ts +++ b/src/commands/sacrifice.ts @@ -39,7 +39,7 @@ export default class Sacrifice extends Command { const cardValue = GetSacrificeAmount(cardData.card.type); const cardRarityString = CardRarityToString(cardData.card.type); - let description = [ + const description = [ `Card: ${cardData.card.name}`, `Series: ${cardData.series.name}`, `Rarity: ${cardRarityString}`,