From 08e99ee2d7dd60fa7587586544868192b20e8e75 Mon Sep 17 00:00:00 2001 From: Ethan Lane Date: Sun, 3 Sep 2023 19:54:16 +0100 Subject: [PATCH] Claim button --- src/bot.ts | 1 + src/buttonEvents/Claim.ts | 38 +++++++++++++++ src/client/client.ts | 24 ++++++++++ src/client/events.ts | 25 +++------- src/client/interactionCreate/Button.ts | 17 +++++++ .../interactionCreate/ChatInputCommand.ts | 27 +++++++++++ src/commands/drop.ts | 19 +++++++- src/contracts/IButtonEventItem.ts | 6 +++ src/database/entities/app/.gitkeep | 0 src/database/entities/app/Inventory.ts | 47 +++++++++++++++++++ src/registry.ts | 6 +++ src/type/buttonEvent.ts | 7 +++ 12 files changed, 197 insertions(+), 20 deletions(-) create mode 100644 src/buttonEvents/Claim.ts create mode 100644 src/client/interactionCreate/Button.ts create mode 100644 src/client/interactionCreate/ChatInputCommand.ts create mode 100644 src/contracts/IButtonEventItem.ts delete mode 100644 src/database/entities/app/.gitkeep create mode 100644 src/database/entities/app/Inventory.ts create mode 100644 src/type/buttonEvent.ts diff --git a/src/bot.ts b/src/bot.ts index baca574..e8c1dd3 100644 --- a/src/bot.ts +++ b/src/bot.ts @@ -32,5 +32,6 @@ const client = new CoreClient([ Registry.RegisterCommands(); Registry.RegisterEvents(); +Registry.RegisterButtonEvents(); client.start(); \ No newline at end of file diff --git a/src/buttonEvents/Claim.ts b/src/buttonEvents/Claim.ts new file mode 100644 index 0000000..5412db8 --- /dev/null +++ b/src/buttonEvents/Claim.ts @@ -0,0 +1,38 @@ +import { ButtonInteraction } from "discord.js"; +import { ButtonEvent } from "../type/buttonEvent"; +import Inventory from "../database/entities/app/Inventory"; +import { CoreClient } from "../client/client"; + +export default class Claim extends ButtonEvent { + public override async execute(interaction: ButtonInteraction) { + if (!interaction.guild || !interaction.guildId) return; + + const cardNumber = interaction.customId.split(' ')[1]; + const claimId = interaction.customId.split(' ')[2]; + const userId = interaction.user.id; + + const claimed = await Inventory.FetchOneByClaimId(claimId); + + if (claimed) { + await interaction.reply('This card has already been claimed'); + return; + } + + if (claimId != CoreClient.ClaimId) { + await interaction.reply('This card has expired'); + return; + } + + let inventory = await Inventory.FetchOneByCardNumberAndUserId(userId, cardNumber); + + if (!inventory) { + inventory = new Inventory(userId, cardNumber, 1, claimId); + } else { + inventory.SetQuantity(inventory.Quantity + 1); + } + + await inventory.Save(Inventory, inventory); + + await interaction.reply('Card claimed'); + } +} \ No newline at end of file diff --git a/src/client/client.ts b/src/client/client.ts index 04f00d0..b517a28 100644 --- a/src/client/client.ts +++ b/src/client/client.ts @@ -10,15 +10,21 @@ import { Util } from "./util"; import CardSetupFunction from "../Functions/CardSetupFunction"; import CardDataSource from "../database/dataSources/cardDataSource"; import CardDropHelper from "../helpers/CardDropHelper"; +import IButtonEventItem from "../contracts/IButtonEventItem"; +import { ButtonEvent } from "../type/buttonEvent"; +import AppDataSource from "../database/dataSources/appDataSource"; export class CoreClient extends Client { private static _commandItems: ICommandItem[]; private static _eventItems: IEventItem[]; + private static _buttonEvents: IButtonEventItem[]; private _events: Events; private _util: Util; private _cardSetupFunc: CardSetupFunction; + public static ClaimId: string; + public static get commandItems(): ICommandItem[] { return this._commandItems; } @@ -27,12 +33,17 @@ export class CoreClient extends Client { return this._eventItems; } + public static get buttonEvents(): IButtonEventItem[] { + return this._buttonEvents; + } + constructor(intents: number[]) { super({ intents: intents }); dotenv.config(); CoreClient._commandItems = []; CoreClient._eventItems = []; + CoreClient._buttonEvents = []; this._events = new Events(); this._util = new Util(); @@ -45,6 +56,10 @@ export class CoreClient extends Client { return; } + await AppDataSource.initialize() + .then(() => console.log("App Data Source Initialised")) + .catch(err => console.error("Error initialising App Data Source", err)); + await CardDataSource.initialize() .then(() => console.log("Card Data Source Initialised")) .catch(err => console.error("Error initialising Card Data Source", err)); @@ -78,4 +93,13 @@ export class CoreClient extends Client { CoreClient._eventItems.push(item); } + + public static RegisterButtonEvent(buttonId: string, event: ButtonEvent) { + const item: IButtonEventItem = { + ButtonId: buttonId, + Event: event, + }; + + CoreClient._buttonEvents.push(item); + } } diff --git a/src/client/events.ts b/src/client/events.ts index bfbe7ec..db6cfc4 100644 --- a/src/client/events.ts +++ b/src/client/events.ts @@ -1,29 +1,18 @@ import { Interaction } from "discord.js"; -import ICommandItem from "../contracts/ICommandItem"; -import { CoreClient } from "./client"; +import ChatInputCommand from "./interactionCreate/ChatInputCommand"; +import Button from "./interactionCreate/Button"; export class Events { public async onInteractionCreate(interaction: Interaction) { - if (!interaction.isChatInputCommand()) return; if (!interaction.guildId) return; - const item = CoreClient.commandItems.find(x => x.Name == interaction.commandName && !x.ServerId); - const itemForServer = CoreClient.commandItems.find(x => x.Name == interaction.commandName && x.ServerId == interaction.guildId); - - let itemToUse: ICommandItem; - - if (!itemForServer) { - if (!item) { - await interaction.reply('Command not found'); - return; - } - - itemToUse = item; - } else { - itemToUse = itemForServer; + if (interaction.isChatInputCommand()) { + ChatInputCommand.onChatInput(interaction); } - itemToUse.Command.execute(interaction); + if (interaction.isButton()) { + Button.onButtonClicked(interaction); + } } // Emit when bot is logged in and ready to use diff --git a/src/client/interactionCreate/Button.ts b/src/client/interactionCreate/Button.ts new file mode 100644 index 0000000..165e426 --- /dev/null +++ b/src/client/interactionCreate/Button.ts @@ -0,0 +1,17 @@ +import { ButtonInteraction, Interaction } from "discord.js"; +import { CoreClient } from "../client"; + +export default class Button { + public static async onButtonClicked(interaction: ButtonInteraction) { + if (!interaction.isButton) return; + + const item = CoreClient.buttonEvents.find(x => x.ButtonId == interaction.customId.split(' ')[0]); + + if (!item) { + await interaction.reply('Event not found'); + return; + } + + item.Event.execute(interaction); + } +} \ No newline at end of file diff --git a/src/client/interactionCreate/ChatInputCommand.ts b/src/client/interactionCreate/ChatInputCommand.ts new file mode 100644 index 0000000..d483f1d --- /dev/null +++ b/src/client/interactionCreate/ChatInputCommand.ts @@ -0,0 +1,27 @@ +import { Interaction } from "discord.js"; +import { CoreClient } from "../client"; +import ICommandItem from "../../contracts/ICommandItem"; + +export default class ChatInputCommand { + public static async onChatInput(interaction: Interaction) { + if (!interaction.isChatInputCommand()) return; + + const item = CoreClient.commandItems.find(x => x.Name == interaction.commandName && !x.ServerId); + const itemForServer = CoreClient.commandItems.find(x => x.Name == interaction.commandName && x.ServerId == interaction.guildId); + + let itemToUse: ICommandItem; + + if (!itemForServer) { + if (!item) { + await interaction.reply('Command not found'); + return; + } + + itemToUse = item; + } else { + itemToUse = itemForServer; + } + + itemToUse.Command.execute(interaction); + } +} \ No newline at end of file diff --git a/src/commands/drop.ts b/src/commands/drop.ts index 2b95365..9d24d46 100644 --- a/src/commands/drop.ts +++ b/src/commands/drop.ts @@ -1,8 +1,10 @@ -import { AttachmentBuilder, CommandInteraction, EmbedBuilder, SlashCommandBuilder } from "discord.js"; +import { ActionRowBuilder, AttachmentBuilder, ButtonBuilder, ButtonStyle, CommandInteraction, EmbedBuilder, SlashCommandBuilder } from "discord.js"; import { Command } from "../type/command"; import CardDropHelper from "../helpers/CardDropHelper"; import { CardRarityToColour, CardRarityToString } from "../constants/CardRarity"; import { readFileSync } from "fs"; +import { CoreClient } from "../client/client"; +import { v4 } from "uuid"; export default class Drop extends Command { constructor() { @@ -27,9 +29,22 @@ export default class Drop extends Command { .setColor(CardRarityToColour(randomCard.Rarity)) .setImage(`attachment://${randomCard.Id}.png`); - await interaction.reply({ + const row = new ActionRowBuilder(); + + const claimId = v4(); + + row.addComponents( + new ButtonBuilder() + .setCustomId(`claim ${randomCard.CardNumber} ${claimId}`) + .setLabel("Claim") + .setStyle(ButtonStyle.Primary)); + + const message = await interaction.reply({ embeds: [ embed ], files: [ attachment ], + components: [ row ], }); + + CoreClient.ClaimId = claimId; } } \ No newline at end of file diff --git a/src/contracts/IButtonEventItem.ts b/src/contracts/IButtonEventItem.ts new file mode 100644 index 0000000..6be9f6e --- /dev/null +++ b/src/contracts/IButtonEventItem.ts @@ -0,0 +1,6 @@ +import { ButtonEvent } from "../type/buttonEvent"; + +export default interface IButtonEventItem { + ButtonId: string, + Event: ButtonEvent, +} \ No newline at end of file diff --git a/src/database/entities/app/.gitkeep b/src/database/entities/app/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/src/database/entities/app/Inventory.ts b/src/database/entities/app/Inventory.ts new file mode 100644 index 0000000..c6319a9 --- /dev/null +++ b/src/database/entities/app/Inventory.ts @@ -0,0 +1,47 @@ +import { Column, Entity } from "typeorm"; +import AppBaseEntity from "../../../contracts/AppBaseEntity"; +import AppDataSource from "../../dataSources/appDataSource"; + +@Entity() +export default class Inventory extends AppBaseEntity { + constructor(userId: string, cardNumber: string, quantity: number, claimId: string) { + super(); + + this.UserId = userId; + this.CardNumber = cardNumber; + this.Quantity = quantity; + this.ClaimId = claimId; + } + + @Column() + UserId: string; + + @Column() + CardNumber: string; + + @Column() + Quantity: number; + + @Column() + ClaimId: string; + + public SetQuantity(quantity: number) { + this.Quantity = quantity; + } + + public static async FetchOneByCardNumberAndUserId(userId: string, cardNumber: string): Promise { + const repository = AppDataSource.getRepository(Inventory); + + const single = await repository.findOne({ where: { UserId: userId, CardNumber: cardNumber }}); + + return single; + } + + public static async FetchOneByClaimId(claimId: string): Promise { + const repository = AppDataSource.getRepository(Inventory); + + const single = await repository.findOne({ where: { ClaimId: claimId }}); + + return single; + } +} \ No newline at end of file diff --git a/src/registry.ts b/src/registry.ts index 8514119..2cc953d 100644 --- a/src/registry.ts +++ b/src/registry.ts @@ -3,6 +3,8 @@ import { CoreClient } from "./client/client"; import About from "./commands/about"; import Drop from "./commands/drop"; +import Claim from "./buttonEvents/Claim"; + export default class Registry { public static RegisterCommands() { CoreClient.RegisterCommand('about', new About()); @@ -12,4 +14,8 @@ export default class Registry { public static RegisterEvents() { } + + public static RegisterButtonEvents() { + CoreClient.RegisterButtonEvent('claim', new Claim()); + } } \ No newline at end of file diff --git a/src/type/buttonEvent.ts b/src/type/buttonEvent.ts new file mode 100644 index 0000000..3a691cf --- /dev/null +++ b/src/type/buttonEvent.ts @@ -0,0 +1,7 @@ +import { ButtonInteraction } from "discord.js"; + +export class ButtonEvent { + public execute(interaction: ButtonInteraction) { + + } +} \ No newline at end of file