diff --git a/src/main/java/com/alttd/commandManager/commands/CommandAuction.java b/src/main/java/com/alttd/commandManager/commands/CommandAuction.java index 464ef11..f807261 100644 --- a/src/main/java/com/alttd/commandManager/commands/CommandAuction.java +++ b/src/main/java/com/alttd/commandManager/commands/CommandAuction.java @@ -4,11 +4,9 @@ import com.alttd.AltitudeBot; import com.alttd.commandManager.CommandManager; import com.alttd.commandManager.DiscordCommand; import com.alttd.database.queries.QueriesAuctions.Auction; -import com.alttd.database.queries.QueriesAuctions.QueriesAuction; import com.alttd.database.queries.commandOutputChannels.CommandOutputChannels; import com.alttd.database.queries.commandOutputChannels.OutputType; import com.alttd.schedulers.AuctionScheduler; -import com.alttd.selectMenuManager.DiscordSelectMenu; import com.alttd.selectMenuManager.SelectMenuManager; import com.alttd.util.Logger; import com.alttd.util.Util; @@ -28,13 +26,11 @@ import net.dv8tion.jda.api.interactions.commands.OptionType; import net.dv8tion.jda.api.interactions.commands.build.CommandData; import net.dv8tion.jda.api.interactions.commands.build.Commands; import net.dv8tion.jda.api.interactions.components.selections.SelectMenu; -import net.dv8tion.jda.api.interactions.components.selections.SelectOption; import net.dv8tion.jda.api.requests.RestAction; import net.dv8tion.jda.api.requests.restaction.interactions.ReplyCallbackAction; import net.dv8tion.jda.api.utils.AttachedFile; import java.awt.*; -import java.io.File; import java.nio.file.Path; import java.time.Instant; import java.util.UUID; @@ -94,77 +90,69 @@ public class CommandAuction extends DiscordCommand { if (messageEmbed == null) return; ReplyCallbackAction replyCallbackAction = event.deferReply(true); - textChannel.sendMessageEmbeds(messageEmbed).queue(success -> { - Message.Attachment screenshot = event.getOption("screenshot", OptionMapping::getAsAttachment); - if (screenshot != null) { - String dataFolder = AltitudeBot.getInstance().getDataFolder(); - Path parent = Path.of(dataFolder).getParent(); - Path path = Path.of(parent.toString() + UUID.randomUUID() + "." + screenshot.getFileExtension()); - screenshot.getProxy().downloadToFile(path.toFile()).whenComplete((file, throwable) -> - success.editMessageAttachments(AttachedFile.fromData(file)).queue(done -> file.delete(), failed -> { + textChannel.sendMessageEmbeds(messageEmbed).queue( + success -> onSuccessSendAuctionMessage(event, success, replyCallbackAction, minimumIncrease), + error -> replyCallbackAction.setEmbeds(Util.genericErrorEmbed("Error", "Unable to send your auction to the auction channel")) + .queue()); + } + + private void onSuccessSendAuctionMessage(SlashCommandInteractionEvent event, Message message, ReplyCallbackAction replyCallbackAction, int minimumIncrease) { + Message.Attachment screenshot = event.getOption("screenshot", OptionMapping::getAsAttachment); + if (screenshot != null) + addScreenshot(screenshot, message); + + Integer startingPrice = event.getOption("starting-price", OptionMapping::getAsInt); + if (startingPrice == null) { + Logger.severe("Starting price magically became null"); + replyCallbackAction.setEmbeds(Util.genericSuccessEmbed("Error", "Failed to store auction")) + .queue(); + return; + } + + Auction auction = new Auction( + event.getUser().getIdLong(), + message, + message.getChannel().getIdLong(), + message.getGuild().getIdLong(), + startingPrice, + Instant.now().toEpochMilli() + TimeUnit.DAYS.toMillis(1), + minimumIncrease, + event.getOption("insta-buy", OptionMapping::getAsInt)); + + SelectMenu selectMenu = auction.getSelectMenu(selectMenuManager, false); + if (selectMenu == null) { + replyCallbackAction.setEmbeds(Util.genericErrorEmbed("Error", "Unable to find select menu for your auction, removing message...")) + .queue(); + message.delete().queue(); + return; + } + + message.editMessageComponents().setActionRow(selectMenu).queue(); + + AuctionScheduler auctionScheduler = AuctionScheduler.getInstance(); + if (auctionScheduler == null) { + replyCallbackAction.setEmbeds(Util.genericSuccessEmbed("Error", "Failed to store auction in scheduler")) + .queue(); + return; + } + auctionScheduler.addAuction(auction); + replyCallbackAction.setEmbeds(Util.genericSuccessEmbed("Success", "Your auction was created")) + .queue(); + } + + private void addScreenshot(Message.Attachment screenshot, Message message) { + String dataFolder = AltitudeBot.getInstance().getDataFolder(); + Path parent = Path.of(dataFolder).getParent(); + Path path = Path.of(parent.toString() + UUID.randomUUID() + "." + screenshot.getFileExtension()); + screenshot.getProxy().downloadToFile(path.toFile()).whenComplete((file, throwable) -> + message.editMessageAttachments(AttachedFile.fromData(file)).queue(done -> file.delete(), failed -> { Util.handleFailure(failed); file.delete(); })) - .exceptionally(e -> { - e.printStackTrace(); - return null; - }); - } - DiscordSelectMenu discordAuction = selectMenuManager.getDiscordSelectMenuFor("auction"); - if (discordAuction == null) { - replyCallbackAction.setEmbeds(Util.genericErrorEmbed("Error", "Unable to find select menu for your auction, removing message...")) - .queue(); - success.delete().queue(); - return; - } - SelectMenu selectMenu; - Integer instaBuy = event.getOption("insta-buy", OptionMapping::getAsInt); - int mediumIncrease = minimumIncrease * 5; - if (instaBuy != null) { - if (mediumIncrease == instaBuy) - mediumIncrease += 1; - selectMenu = discordAuction.getSelectMenu( - SelectOption.of("Increase bid by: " + minimumIncrease, "" + minimumIncrease), - SelectOption.of("Increase bid by: " + mediumIncrease, "" + mediumIncrease), - SelectOption.of("Insta Buy: " + instaBuy, "" + instaBuy) - ); - } else { - selectMenu = discordAuction.getSelectMenu( - SelectOption.of("Increase bid by: " + minimumIncrease, "" + minimumIncrease), - SelectOption.of("Increase bid by: " + mediumIncrease, "" + mediumIncrease) - ); - } - - if (selectMenu == null) { - replyCallbackAction.setEmbeds(Util.genericErrorEmbed("Error", "Unable to add select menu to your auction, removing message...")) - .queue(); - success.delete().queue(); - return; - } - success.editMessageComponents().setActionRow(selectMenu).queue(); - Integer startingPrice = event.getOption("starting-price", OptionMapping::getAsInt); - if (startingPrice == null) { - Logger.severe("Starting price magically became null"); - replyCallbackAction.setEmbeds(Util.genericSuccessEmbed("Error", "Failed to store auction")) - .queue(); - return; - } - AuctionScheduler auctionScheduler = AuctionScheduler.getInstance(); - if (auctionScheduler == null) { - replyCallbackAction.setEmbeds(Util.genericSuccessEmbed("Error", "Failed to store auction in scheduler")) - .queue(); - return; - } - auctionScheduler.addAuction(new Auction( - success, - success.getChannel().getIdLong(), - success.getGuild().getIdLong(), - startingPrice, - Instant.now().toEpochMilli() + TimeUnit.DAYS.toMillis(1))); - replyCallbackAction.setEmbeds(Util.genericSuccessEmbed("Success", "Your auction was created")) - .queue(); - }, error -> replyCallbackAction.setEmbeds(Util.genericErrorEmbed("Error", "Unable to send your auction to the auction channel")) - .queue()); + .exceptionally(e -> { + e.printStackTrace(); + return null; + }); } private MessageEmbed buildAuctionEmbed(SlashCommandInteractionEvent event, int minimumIncrease) { @@ -183,18 +171,18 @@ public class CommandAuction extends DiscordCommand { Integer amount = event.getOption("amount", OptionMapping::getAsInt); if (amount == null) return handleBuildEmbedError(event, "Missing required amount option"); - embedBuilder.appendDescription("\n**Amount**: " + amount); + embedBuilder.appendDescription("\n**Amount**: " + Util.formatNumber(amount)); Integer startingPrice = event.getOption("starting-price", OptionMapping::getAsInt); if (startingPrice == null) return handleBuildEmbedError(event, "Missing required starting price option"); - embedBuilder.appendDescription("\n**Starting Price**: $" + startingPrice); + embedBuilder.appendDescription("\n**Starting Price**: $" + Util.formatNumber(startingPrice)); Integer instaBuy = event.getOption("insta-buy", OptionMapping::getAsInt); if (instaBuy != null) { if (instaBuy == minimumIncrease) return handleBuildEmbedError(event, "Insta buy can't be the same as minimum increase"); - embedBuilder.appendDescription("\n**Insta Buy**: $" + instaBuy); + embedBuilder.appendDescription("\n**Insta Buy**: $" + Util.formatNumber(instaBuy)); } diff --git a/src/main/java/com/alttd/database/DatabaseTables.java b/src/main/java/com/alttd/database/DatabaseTables.java index 8a6ca85..cce08de 100644 --- a/src/main/java/com/alttd/database/DatabaseTables.java +++ b/src/main/java/com/alttd/database/DatabaseTables.java @@ -137,11 +137,14 @@ public class DatabaseTables { private void createAuctionTable() { String sql = "CREATE TABLE IF NOT EXISTS auctions(" + + "user_id BIGINT NOT NULL, " + "message_id BIGINT NOT NULL, " + "channel_id BIGINT NOT NULL, " + "guild_id BIGINT NOT NULL, " + "starting_price INT NOT NULL, " + "expire_time BIGINT NOT NULL, " + + "minimum_increase INT NOT NULL, " + + "insta_buy INT NULL, " + "PRIMARY KEY (message_id)" + ")"; try { @@ -152,6 +155,23 @@ public class DatabaseTables { } } + private void createAuctionActionsTable() { + String sql = "CREATE TABLE IF NOT EXISTS auction_actions(" + + "message_id BIGINT NOT NULL, " + + "action_type VARCHAR(32) NOT NULL, " + + "user_id BIGINT NOT NULL, " + + "price INT NOT NULL, " + + "action_time BIGINT NOT NULL, " + + "PRIMARY KEY (message_id, action_time)" + + ")"; + try { + connection.prepareStatement(sql).executeUpdate(); + } catch (SQLException e) { + Logger.sql(e); + Logger.severe("Unable to create auction action table, shutting down..."); + } + } + public static void createTables(Connection connection) { if (instance == null) instance = new DatabaseTables(connection); diff --git a/src/main/java/com/alttd/database/queries/QueriesAuctionActions/AuctionAction.java b/src/main/java/com/alttd/database/queries/QueriesAuctionActions/AuctionAction.java new file mode 100644 index 0000000..c970c11 --- /dev/null +++ b/src/main/java/com/alttd/database/queries/QueriesAuctionActions/AuctionAction.java @@ -0,0 +1,11 @@ +package com.alttd.database.queries.QueriesAuctionActions; + +import org.jetbrains.annotations.NotNull; + +public record AuctionAction(AuctionType auctionType, long userId, long messageId, int price, long time) implements Comparable { + + @Override + public int compareTo(@NotNull AuctionAction auctionAction) { + return Long.compare(this.time(), auctionAction.time()); + } +} diff --git a/src/main/java/com/alttd/database/queries/QueriesAuctionActions/AuctionType.java b/src/main/java/com/alttd/database/queries/QueriesAuctionActions/AuctionType.java new file mode 100644 index 0000000..2b4e405 --- /dev/null +++ b/src/main/java/com/alttd/database/queries/QueriesAuctionActions/AuctionType.java @@ -0,0 +1,7 @@ +package com.alttd.database.queries.QueriesAuctionActions; + +public enum AuctionType { + BID, + STARTING_BID, + INSTA_BUY +} diff --git a/src/main/java/com/alttd/database/queries/QueriesAuctionActions/QueriesAuctionAction.java b/src/main/java/com/alttd/database/queries/QueriesAuctionActions/QueriesAuctionAction.java new file mode 100644 index 0000000..75a8a91 --- /dev/null +++ b/src/main/java/com/alttd/database/queries/QueriesAuctionActions/QueriesAuctionAction.java @@ -0,0 +1,65 @@ +package com.alttd.database.queries.QueriesAuctionActions; + +import com.alttd.database.Database; +import com.alttd.database.queries.QueriesAuctions.Auction; +import com.alttd.util.Logger; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.LinkedList; + +public class QueriesAuctionAction { + public static boolean saveAuctionAction(AuctionAction auctionAction) { + String sql = "INSERT INTO auction_actions " + + "(message_id, action_type, user_id, price, action_time) " + + "VALUES (?, ?, ?, ?, ?) "; + try { + PreparedStatement statement = Database.getDatabase().getConnection().prepareStatement(sql); + statement.setLong(1, auctionAction.messageId()); + statement.setString(2, auctionAction.auctionType().toString()); + statement.setLong(3, auctionAction.userId()); + statement.setInt(4, auctionAction.price()); + statement.setLong(5, auctionAction.time()); + + return statement.executeUpdate() == 1; + } catch (SQLException exception) { + exception.printStackTrace(); + } + return false; + } + + public static boolean loadAuctionAction(Auction auction) { + String sql = "SELECT * FROM auction_actions " + + "WHERE message_id = ?"; + long messageId = auction.getMessageId(); + + try { + PreparedStatement statement = Database.getDatabase().getConnection().prepareStatement(sql); + statement.setLong(1, messageId); + ResultSet resultSet = statement.executeQuery(); + + LinkedList actions = new LinkedList<>(); + while (resultSet.next()) { + long userId = resultSet.getLong("user_id"); + long actionTime = resultSet.getLong("action_time"); + int price = resultSet.getInt("price"); + String actionTypeString = resultSet.getString("action_type"); + AuctionType auctionType; + + try { + auctionType = AuctionType.valueOf(actionTypeString); + } catch (IllegalArgumentException e) { + Logger.warning("Invalid auction type found in database for message: % at time: %", messageId + "", actionTime + ""); + continue; + } + actions.add(new AuctionAction(auctionType, userId, messageId, price, actionTime)); + } + auction.resetActions(actions); + return true; + } catch (SQLException exception) { + exception.printStackTrace(); + } + return false; + } +} diff --git a/src/main/java/com/alttd/database/queries/QueriesAuctions/Auction.java b/src/main/java/com/alttd/database/queries/QueriesAuctions/Auction.java index 46cd639..5606ce2 100644 --- a/src/main/java/com/alttd/database/queries/QueriesAuctions/Auction.java +++ b/src/main/java/com/alttd/database/queries/QueriesAuctions/Auction.java @@ -1,36 +1,76 @@ package com.alttd.database.queries.QueriesAuctions; import com.alttd.AltitudeBot; +import com.alttd.database.queries.QueriesAuctionActions.AuctionAction; +import com.alttd.database.queries.QueriesAuctionActions.QueriesAuctionAction; +import com.alttd.selectMenuManager.DiscordSelectMenu; +import com.alttd.selectMenuManager.SelectMenuManager; +import com.alttd.util.Util; import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Message; import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; +import net.dv8tion.jda.api.interactions.components.selections.SelectMenu; +import net.dv8tion.jda.api.interactions.components.selections.SelectOption; import org.jetbrains.annotations.NotNull; import javax.annotation.Nullable; import java.time.Instant; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; public class Auction implements Comparable { - private final long messageId, channelId, guildId; - private final int startingPrice; + private final long userId, messageId, channelId, guildId; + private final Integer instaBuy; + private final int startingPrice, minimumIncrease; private long expireTime; - public Auction(long messageId, long channelId, long guildId, int startingPrice, long expireTime) { + private final LinkedList actions = new LinkedList<>(); + + public Auction(long userId, long messageId, long channelId, long guildId, int startingPrice, long expireTime, int minimumIncrease, Integer instaBuy) { + this.userId = userId; this.messageId = messageId; this.channelId = channelId; this.guildId = guildId; this.startingPrice = startingPrice; this.expireTime = expireTime; + this.minimumIncrease = minimumIncrease; + this.instaBuy = instaBuy; } - public Auction(@NotNull Message message, long channelId, long guildId, int startingPrice, long expireTime) { + public Auction(long userId, @NotNull Message message, long channelId, long guildId, int startingPrice, long expireTime, int minimumIncrease, Integer instaBuy) { + this.userId = userId; this.messageId = message.getIdLong(); this.channelId = channelId; this.guildId = guildId; this.startingPrice = startingPrice; this.expireTime = expireTime; + this.minimumIncrease = minimumIncrease; + this.instaBuy = instaBuy; + } + + public void resetActions(LinkedList newAuctionActions) { + actions.clear(); + actions.addAll(newAuctionActions); + actions.sort(AuctionAction::compareTo); + } + + public void addAction(AuctionAction auctionAction) { + actions.add(auctionAction); + QueriesAuctionAction.saveAuctionAction(auctionAction); + } + + public AuctionAction getLastAction() { + if (actions.size() == 0) + return null; + return actions.getLast(); + } + + public LinkedList getActions() { + return actions; } public void updateMessage(Consumer success, @Nullable Consumer failure) { @@ -54,6 +94,10 @@ public class Auction implements Comparable { }); } + public long getUserId() { + return userId; + } + public long getExpireTime() { return expireTime; } @@ -74,6 +118,14 @@ public class Auction implements Comparable { return startingPrice; } + public int getMinimumIncrease() { + return minimumIncrease; + } + + public Integer getInstaBuy() { + return instaBuy; + } + public boolean updateExpiry() { long future = Instant.now().toEpochMilli() + TimeUnit.MINUTES.toMillis(5); if (expireTime < future) { @@ -88,4 +140,28 @@ public class Auction implements Comparable { Auction o1 = (Auction) o; return Long.compare(expireTime, o1.getExpireTime()); } + + public SelectMenu getSelectMenu(SelectMenuManager selectMenuManager, boolean startingBidFinished) { + DiscordSelectMenu discordAuction = selectMenuManager.getDiscordSelectMenuFor("auction"); + if (discordAuction == null) + return null; + int mediumIncrease = minimumIncrease * 5; + List selectOptionList = new ArrayList<>(); + + if (startingBidFinished) + selectOptionList.add(SelectOption.of("Increase bid by: $" + Util.formatNumber(minimumIncrease), "" + minimumIncrease)); + else + selectOptionList.add(SelectOption.of("Starting Bid: $" + Util.formatNumber(startingPrice), "" + startingPrice)); + if (instaBuy != null) { + if (mediumIncrease == instaBuy) + mediumIncrease += 1; + if (startingBidFinished) + selectOptionList.add(SelectOption.of("Increase bid by: $" + Util.formatNumber(mediumIncrease), "" + mediumIncrease)); + selectOptionList.add(SelectOption.of("Insta Buy: $" + Util.formatNumber(instaBuy), "" + instaBuy)); + } else if (startingBidFinished) { + selectOptionList.add(SelectOption.of("Increase bid by: $" + Util.formatNumber(mediumIncrease), "" + mediumIncrease)); + } + + return discordAuction.getSelectMenu(selectOptionList); + } } diff --git a/src/main/java/com/alttd/database/queries/QueriesAuctions/QueriesAuction.java b/src/main/java/com/alttd/database/queries/QueriesAuctions/QueriesAuction.java index b472664..6759d81 100644 --- a/src/main/java/com/alttd/database/queries/QueriesAuctions/QueriesAuction.java +++ b/src/main/java/com/alttd/database/queries/QueriesAuctions/QueriesAuction.java @@ -1,6 +1,7 @@ package com.alttd.database.queries.QueriesAuctions; import com.alttd.database.Database; +import com.alttd.database.queries.QueriesAuctionActions.QueriesAuctionAction; import java.sql.PreparedStatement; import java.sql.ResultSet; @@ -20,9 +21,15 @@ public class QueriesAuction { long messageId = resultSet.getLong("message_id"); long channelId = resultSet.getLong("channel_id"); long guildId = resultSet.getLong("guild_id"); + long userId = resultSet.getLong("user_id"); int startingPrice = resultSet.getInt("starting_price"); long expireTime = resultSet.getLong("expire_time"); - auctions.put(messageId, new Auction(messageId, channelId, guildId, startingPrice, expireTime)); + int minimumIncrease = resultSet.getInt("minimum_increase"); + Integer instaBuy = resultSet.getInt("insta_buy"); + instaBuy = instaBuy == -1 ? null : instaBuy; //Make sure the object is null if price was null or 0 + Auction auction = new Auction(userId, messageId, channelId, guildId, startingPrice, expireTime, minimumIncrease, instaBuy); + QueriesAuctionAction.loadAuctionAction(auction); + auctions.put(messageId, auction); } } catch (SQLException exception) { exception.printStackTrace(); @@ -33,17 +40,20 @@ public class QueriesAuction { public static boolean saveAuction(Auction auction) { String sql = "INSERT INTO auctions " + - "(message_id, channel_id, guild_id, starting_price, expire_time) " + - "VALUES (?, ?, ?, ?, ?) " + + "(message_id, channel_id, guild_id, user_id, starting_price, expire_time, minimum_increase, insta_buy) " + + "VALUES (?, ?, ?, ?, ?, ?, ?, ?) " + "ON DUPLICATE KEY UPDATE expire_time = ?"; try { PreparedStatement statement = Database.getDatabase().getConnection().prepareStatement(sql); statement.setLong(1, auction.getMessageId()); statement.setLong(2, auction.getChannelId()); statement.setLong(3, auction.getGuildId()); - statement.setInt(4, auction.getStartingPrice()); - statement.setLong(5, auction.getExpireTime()); + statement.setLong(4, auction.getUserId()); + statement.setInt(5, auction.getStartingPrice()); statement.setLong(6, auction.getExpireTime()); + statement.setInt(7, auction.getMinimumIncrease()); + statement.setInt(8, auction.getInstaBuy() == null ? -1 : auction.getInstaBuy()); + statement.setLong(9, auction.getExpireTime()); return statement.executeUpdate() == 1; } catch (SQLException exception) { diff --git a/src/main/java/com/alttd/schedulers/AuctionScheduler.java b/src/main/java/com/alttd/schedulers/AuctionScheduler.java index a725b8f..b9af1a9 100644 --- a/src/main/java/com/alttd/schedulers/AuctionScheduler.java +++ b/src/main/java/com/alttd/schedulers/AuctionScheduler.java @@ -14,6 +14,7 @@ import net.dv8tion.jda.api.entities.channel.middleman.GuildMessageChannel; import javax.annotation.Nullable; import java.awt.*; +import java.time.Instant; import java.util.*; import java.util.List; import java.util.concurrent.Executors; @@ -108,6 +109,7 @@ public class AuctionScheduler { embedBuilder.addField("Winning Bid", "Insta bought by " + instaBuy.getAsMention(), false); else if (!fields.isEmpty()) { MessageEmbed.Field field = fields.get(0); + if (field.getName() != null && field.getName().equals("Current Bid") && field.getValue() != null) { embedBuilder.addField("Winning Bid", field.getValue(), false); } else if (fields.size() == 2) { @@ -116,6 +118,13 @@ public class AuctionScheduler { embedBuilder.addField("Winning Bid", field.getValue(), false); } } + + String description = embed.getDescription(); + if (description != null) { + embedBuilder.setDescription(description.substring(0, description.lastIndexOf("Closes"))); + embedBuilder.appendDescription("Closed "); + } + if (embedBuilder.getFields().size() != 0) embedBuilder.setColor(Color.GREEN); else diff --git a/src/main/java/com/alttd/selectMenuManager/DiscordSelectMenu.java b/src/main/java/com/alttd/selectMenuManager/DiscordSelectMenu.java index a206b6f..9d3b5e8 100644 --- a/src/main/java/com/alttd/selectMenuManager/DiscordSelectMenu.java +++ b/src/main/java/com/alttd/selectMenuManager/DiscordSelectMenu.java @@ -1,16 +1,16 @@ package com.alttd.selectMenuManager; -import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent; import net.dv8tion.jda.api.events.interaction.component.SelectMenuInteractionEvent; -import net.dv8tion.jda.api.interactions.components.buttons.Button; import net.dv8tion.jda.api.interactions.components.selections.SelectMenu; import net.dv8tion.jda.api.interactions.components.selections.SelectOption; +import java.util.List; + public abstract class DiscordSelectMenu { public abstract String getSelectMenuId(); public abstract void execute(SelectMenuInteractionEvent event); - public abstract SelectMenu getSelectMenu(SelectOption ...selectOptions); + public abstract SelectMenu getSelectMenu(List selectOptions); } diff --git a/src/main/java/com/alttd/selectMenuManager/SelectMenuManager.java b/src/main/java/com/alttd/selectMenuManager/SelectMenuManager.java index c8c0a75..6392bfe 100644 --- a/src/main/java/com/alttd/selectMenuManager/SelectMenuManager.java +++ b/src/main/java/com/alttd/selectMenuManager/SelectMenuManager.java @@ -18,7 +18,7 @@ public class SelectMenuManager extends ListenerAdapter { private final List buttons; public SelectMenuManager() { - buttons = List.of(new SelectMenuAuction()); + buttons = List.of(new SelectMenuAuction(this)); } @Override diff --git a/src/main/java/com/alttd/selectMenuManager/selectMenus/SelectMenuAuction.java b/src/main/java/com/alttd/selectMenuManager/selectMenus/SelectMenuAuction.java index a440dce..1c9f0ed 100644 --- a/src/main/java/com/alttd/selectMenuManager/selectMenus/SelectMenuAuction.java +++ b/src/main/java/com/alttd/selectMenuManager/selectMenus/SelectMenuAuction.java @@ -1,26 +1,33 @@ package com.alttd.selectMenuManager.selectMenus; +import com.alttd.database.queries.QueriesAuctionActions.AuctionAction; +import com.alttd.database.queries.QueriesAuctionActions.AuctionType; import com.alttd.database.queries.QueriesAuctions.Auction; -import com.alttd.database.queries.QueriesAuctions.QueriesAuction; import com.alttd.schedulers.AuctionScheduler; import com.alttd.selectMenuManager.DiscordSelectMenu; +import com.alttd.selectMenuManager.SelectMenuManager; import com.alttd.util.Util; import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.entities.Member; -import net.dv8tion.jda.api.entities.Mentions; import net.dv8tion.jda.api.entities.MessageEmbed; import net.dv8tion.jda.api.events.interaction.component.SelectMenuInteractionEvent; import net.dv8tion.jda.api.interactions.components.selections.SelectMenu; import net.dv8tion.jda.api.interactions.components.selections.SelectOption; import net.dv8tion.jda.api.requests.restaction.interactions.ReplyCallbackAction; -import java.awt.*; import java.time.Instant; import java.util.List; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; public class SelectMenuAuction extends DiscordSelectMenu { + + private final SelectMenuManager selectMenuManager; + + public SelectMenuAuction(SelectMenuManager selectMenuManager) { + this.selectMenuManager = selectMenuManager; + } + @Override public String getSelectMenuId() { return "auction"; @@ -73,32 +80,33 @@ public class SelectMenuAuction extends DiscordSelectMenu { MessageEmbed messageEmbed = getMessageEmbed(event.getMessage().getEmbeds(), event); if (messageEmbed == null) return; - if (messageEmbed.getAuthor() != null && messageEmbed.getAuthor().getName() != null && messageEmbed.getAuthor().getName().equalsIgnoreCase(member.getEffectiveName())) { + if (member.getIdLong() == auction.getUserId()) { event.replyEmbeds(Util.genericErrorEmbed("Error", "You own this auction so you can not bid on it.")) .setEphemeral(true).queue(); - //TODO store auction owner so this can be done better return; } - BidFieldInfo bidFieldInfo = getPreviousBid(messageEmbed, event); - if (bidFieldInfo == null) - return; - int prevBid; - if (bidFieldInfo.bid() == 0) - prevBid = auction.getStartingPrice(); - else - prevBid = bidFieldInfo.bid(); - - int currentBid = prevBid + bid; + Instant now = Instant.now(); if (selectOption.getLabel().startsWith("Insta Buy")) { + Integer instaBuy = auction.getInstaBuy(); + auction.addAction(new AuctionAction(AuctionType.INSTA_BUY, member.getIdLong(), auction.getMessageId(), instaBuy == null ? -1 : instaBuy, now.toEpochMilli())); auctionScheduler.finishAuction(auction, member); - event.replyEmbeds(Util.genericSuccessEmbed("Success", "You successfully insta bought the item for " + bid + "!")) + event.replyEmbeds(Util.genericSuccessEmbed("Success", "You successfully insta bought the item for $" + Util.formatNumber(bid) + "!")) .setEphemeral(true).queue(); return; } + AuctionAction lastAction = auction.getLastAction(); + int prevBid = (lastAction == null ? 0 : lastAction.price()); + int currentBid = prevBid + bid; + + if (selectOption.getLabel().startsWith("Starting Bid")) + auction.addAction(new AuctionAction(AuctionType.STARTING_BID, member.getIdLong(), auction.getMessageId(), currentBid, now.toEpochMilli())); + else + auction.addAction(new AuctionAction(AuctionType.BID, member.getIdLong(), auction.getMessageId(), currentBid, now.toEpochMilli())); + EmbedBuilder embedBuilder = new EmbedBuilder(messageEmbed) .clearFields() - .setTimestamp(Instant.now()); + .setTimestamp(now); if (auction.updateExpiry()) { auctionScheduler.updateAuction(auction); String description = messageEmbed.getDescription(); @@ -106,102 +114,28 @@ public class SelectMenuAuction extends DiscordSelectMenu { embedBuilder.setDescription(description.substring(0, description.lastIndexOf("Closes "); } - if (bidFieldInfo.member() != null) - embedBuilder.addField("Previous Bid", "$" + bidFieldInfo.bid() + " by <@" + bidFieldInfo.member() + ">", false); - embedBuilder.addField("Current Bid", "$" + currentBid + " by " + event.getMember().getAsMention(), false); + if (lastAction != null) + embedBuilder.addField("Previous Bid", "$" + Util.formatNumber(prevBid) + " by <@" + lastAction.userId() + ">", false); + embedBuilder.addField("Current Bid", "$" + Util.formatNumber(currentBid) + " by " + event.getMember().getAsMention(), false); ReplyCallbackAction replyCallbackAction = event.deferReply(true); - int finalPrevBid = prevBid; event.getMessage().editMessageEmbeds(embedBuilder.build()).queue( success -> { if (auction.updateExpiry()) auctionScheduler.updateAuction(auction); - replyCallbackAction.setEmbeds(Util.genericSuccessEmbed("Success", "You successfully increased the bid from " + finalPrevBid + " to " + currentBid + "!")) + if (!selectOption.getLabel().startsWith("Starting Bid")) { + replyCallbackAction.setEmbeds(Util.genericSuccessEmbed("Success", "You successfully increased the bid from $" + + Util.formatNumber(prevBid) + " to $" + Util.formatNumber(currentBid) + "!")) + .queue(); + return; + } + replyCallbackAction.setEmbeds(Util.genericSuccessEmbed("Success", "You successfully made the first bid on this item ($" + Util.formatNumber(currentBid) + ")!")) .queue(); + success.editMessageComponents().setActionRow(auction.getSelectMenu(selectMenuManager, true)).queue(); }, error -> replyCallbackAction.setEmbeds(Util.genericErrorEmbed("Error", "Unable to finish your bid")).queue()); } - /** - * Expecting to find: - * a: No fields - * b: One field containing the current bid - * c: Two fields, one containing the current bid and one for the previous bid - *

- * option a: return BidFieldInfo with the bid set to 0 and member to null - * option b: check if field matches roughly this: - * Name: Current Bid - * Value: 160 by <@212303885988134914> - * if it does set the BidFieldInfo value to 160, and attempt ot get the user from the <@ string, - * set member to the member belonging to that user id or null if we can't find it - * option c: Find the field with the name Current Bid and proceed with that field as if it was option b - * @param messageEmbed Embed to find the fields for - * @param event Event that we need to respond to for errors - * @return BidFieldInfo or null if there was an error - */ - private BidFieldInfo getPreviousBid(MessageEmbed messageEmbed, SelectMenuInteractionEvent event) { - List fields = messageEmbed.getFields(); - if (fields.size() > 2) { - event.replyEmbeds(Util.genericErrorEmbed("Error", "This auction embed has the wrong number of fields")) - .setEphemeral(true).queue(); - return null; - } - - if (fields.isEmpty()) - return new BidFieldInfo(0, null); - - MessageEmbed.Field field = fields.get(0); - if (field.getName() == null) { - event.replyEmbeds(Util.genericErrorEmbed("Error", "Found field with no name")) - .setEphemeral(true).queue(); - return null; - } - - if (!field.getName().equals("Current Bid")) { - if (fields.size() != 2) { - event.replyEmbeds(Util.genericErrorEmbed("Error", "Found one field and it's not the right one")) - .setEphemeral(true).queue(); - return null; - } - field = fields.get(1); - if (field.getName() == null || !field.getName().equals("Current Bid")) { - event.replyEmbeds(Util.genericErrorEmbed("Error", "Unable to find current bid but it should be there")) - .setEphemeral(true).queue(); - return null; - } - } - String value1 = field.getValue(); - if (value1 == null) { - event.replyEmbeds(Util.genericErrorEmbed("Error", "Received default input")) - .setEphemeral(true).queue(); - return null; - } - String[] s = value1.split(" "); - if (s.length < 3) { - event.replyEmbeds(Util.genericErrorEmbed("Error", "Field has incorrect input")) - .setEphemeral(true).queue(); - return null; - } - - int bid; - try { - bid = Integer.parseInt(s[0].substring(1)); - } catch (NumberFormatException e) { - event.replyEmbeds(Util.genericErrorEmbed("Error", "Field had the wrong value")) - .setEphemeral(true).queue(); - return null; - } - - String substring = s[2].substring(2, s[2].length() - 1); - try { - long l = Long.parseLong(substring); - - return new BidFieldInfo(bid, l); - } catch (NumberFormatException e) { - return new BidFieldInfo(bid, null); - } - } - private MessageEmbed getMessageEmbed(List embeds, SelectMenuInteractionEvent event) { if (embeds.isEmpty()) { event.replyEmbeds(Util.genericErrorEmbed("Error", "Input came from message with no embeds")) @@ -212,12 +146,10 @@ public class SelectMenuAuction extends DiscordSelectMenu { } @Override - public SelectMenu getSelectMenu(SelectOption... selectOptions) { + public SelectMenu getSelectMenu(List selectOptions) { SelectMenu.Builder builder = SelectMenu.create(getSelectMenuId()); if (selectOptions != null) builder.addOptions(selectOptions); return builder.build(); } } - -record BidFieldInfo(int bid, Long member) {} diff --git a/src/main/java/com/alttd/util/Util.java b/src/main/java/com/alttd/util/Util.java index 31fefec..576c990 100644 --- a/src/main/java/com/alttd/util/Util.java +++ b/src/main/java/com/alttd/util/Util.java @@ -216,4 +216,16 @@ public class Util { return str.toUpperCase(); return str.toUpperCase().charAt(0) + str.toLowerCase().substring(1); } + + public static String formatNumber(int price) { + String priceString = new StringBuilder(String.valueOf(price)).reverse().toString(); + StringBuilder sb = new StringBuilder(); + int i = 0; + while (i + 3 < priceString.length()) { + sb.append(priceString, i, i + 3).append(","); + i += 3; + } + sb.append(priceString.substring(i)).reverse(); + return "" + sb; + } }