Improved auctions

- Added Starting Bid (only for first bid)
- Fixed missing $'s in messages
- Improved the way numbers are displayed in auctions for readability
- Added AuctionActions to store bids by users
- Removed as much code that was relying on text being in embeds as possible
- Improved structure of code related to auctions
This commit is contained in:
Teriuihi 2022-10-30 02:05:16 +01:00
parent 5d8368d0e5
commit 723847a3be
12 changed files with 323 additions and 193 deletions

View File

@ -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));
}

View File

@ -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);

View File

@ -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<AuctionAction> {
@Override
public int compareTo(@NotNull AuctionAction auctionAction) {
return Long.compare(this.time(), auctionAction.time());
}
}

View File

@ -0,0 +1,7 @@
package com.alttd.database.queries.QueriesAuctionActions;
public enum AuctionType {
BID,
STARTING_BID,
INSTA_BUY
}

View File

@ -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<AuctionAction> 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;
}
}

View File

@ -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<AuctionAction> 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<AuctionAction> 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<AuctionAction> getActions() {
return actions;
}
public void updateMessage(Consumer<? super Message> success, @Nullable Consumer<? super String> 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<SelectOption> 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);
}
}

View File

@ -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) {

View File

@ -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 <t:" + Instant.now().getEpochSecond() + ":R>");
}
if (embedBuilder.getFields().size() != 0)
embedBuilder.setColor(Color.GREEN);
else

View File

@ -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<SelectOption> selectOptions);
}

View File

@ -18,7 +18,7 @@ public class SelectMenuManager extends ListenerAdapter {
private final List<DiscordSelectMenu> buttons;
public SelectMenuManager() {
buttons = List.of(new SelectMenuAuction());
buttons = List.of(new SelectMenuAuction(this));
}
@Override

View File

@ -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 <t:")))
.appendDescription("Closes <t:" + TimeUnit.MILLISECONDS.toSeconds(auction.getExpireTime()) + ":R>");
}
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
* <p>
* 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<MessageEmbed.Field> 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<MessageEmbed> 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<SelectOption> selectOptions) {
SelectMenu.Builder builder = SelectMenu.create(getSelectMenuId());
if (selectOptions != null)
builder.addOptions(selectOptions);
return builder.build();
}
}
record BidFieldInfo(int bid, Long member) {}

View File

@ -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;
}
}