Added auction command
This commit is contained in:
parent
df47c7a709
commit
d6eb332d83
|
|
@ -3,7 +3,7 @@ package com.alttd.buttonManager.buttons.remindMeConfirm;
|
|||
import com.alttd.buttonManager.DiscordButton;
|
||||
import com.alttd.database.queries.QueriesReminders.QueriesReminders;
|
||||
import com.alttd.database.queries.QueriesReminders.Reminder;
|
||||
import com.alttd.reminders.ReminderScheduler;
|
||||
import com.alttd.schedulers.ReminderScheduler;
|
||||
import com.alttd.util.Util;
|
||||
import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;
|
||||
import net.dv8tion.jda.api.interactions.InteractionHook;
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import com.alttd.contextMenuManager.ContextMenuManager;
|
|||
import com.alttd.database.Database;
|
||||
import com.alttd.listeners.ChatListener;
|
||||
import com.alttd.modalManager.ModalManager;
|
||||
import com.alttd.selectMenuManager.SelectMenuManager;
|
||||
import com.alttd.util.Logger;
|
||||
import net.dv8tion.jda.api.EmbedBuilder;
|
||||
import net.dv8tion.jda.api.JDA;
|
||||
|
|
@ -31,7 +32,7 @@ public class CommandManager extends ListenerAdapter {
|
|||
private final List<DiscordCommand> commands;
|
||||
private final HashMap<String, List<ScopeInfo>> commandList = new HashMap<>();
|
||||
|
||||
public CommandManager(JDA jda, ModalManager modalManager, ContextMenuManager contextMenuManager, ChatListener chatListener) {
|
||||
public CommandManager(JDA jda, ModalManager modalManager, ContextMenuManager contextMenuManager, ChatListener chatListener, SelectMenuManager selectMenuManager) {
|
||||
commandList.put("manage", new ArrayList<>(List.of(new ScopeInfo(CommandScope.GLOBAL, 0))));
|
||||
loadCommands();
|
||||
Logger.info("Loading commands...");
|
||||
|
|
@ -51,7 +52,8 @@ public class CommandManager extends ListenerAdapter {
|
|||
commandSetToggleableRoles,
|
||||
new CommandToggleRole(commandSetToggleableRoles, jda, this),
|
||||
new CommandRemindMe(jda, this, modalManager),
|
||||
new CommandSoftLock(jda, this, chatListener));
|
||||
new CommandSoftLock(jda, this, chatListener),
|
||||
new CommandAuction(jda, this, selectMenuManager));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -0,0 +1,254 @@
|
|||
package com.alttd.commandManager.commands;
|
||||
|
||||
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;
|
||||
import net.dv8tion.jda.api.EmbedBuilder;
|
||||
import net.dv8tion.jda.api.JDA;
|
||||
import net.dv8tion.jda.api.entities.Guild;
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
import net.dv8tion.jda.api.entities.Message;
|
||||
import net.dv8tion.jda.api.entities.MessageEmbed;
|
||||
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
|
||||
import net.dv8tion.jda.api.entities.channel.middleman.GuildChannel;
|
||||
import net.dv8tion.jda.api.events.interaction.command.CommandAutoCompleteInteractionEvent;
|
||||
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
|
||||
import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions;
|
||||
import net.dv8tion.jda.api.interactions.commands.OptionMapping;
|
||||
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;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class CommandAuction extends DiscordCommand {
|
||||
|
||||
private final CommandData commandData;
|
||||
private final SelectMenuManager selectMenuManager;
|
||||
public CommandAuction(JDA jda, CommandManager commandManager, SelectMenuManager selectMenuManager) {
|
||||
commandData = Commands.slash(getName(), "Create an auction")
|
||||
.addOption(OptionType.STRING, "item", "The name (and type) of the item you're selling", true)
|
||||
.addOption(OptionType.INTEGER, "amount", "How many of the item you're selling", true)
|
||||
.addOption(OptionType.INTEGER, "starting-price", "How much the bids should start at (the minimum price you're willing to accept for the item", true)
|
||||
.addOption(OptionType.INTEGER, "minimum-increase", "The minimum amount of money bids should increase by", true)
|
||||
.addOption(OptionType.INTEGER, "insta-buy", "(optional) A price you're willing to sell the item for immediately (ends the auction)", false)
|
||||
.addOption(OptionType.STRING, "description", "(optional) A further explanation of the item", false)
|
||||
.addOption(OptionType.ATTACHMENT, "screenshot", "(optional) A screenshot of the item you're selling if needed", false)
|
||||
.setDefaultPermissions(DefaultMemberPermissions.ENABLED);
|
||||
|
||||
this.selectMenuManager = selectMenuManager;
|
||||
Util.registerCommand(commandManager, jda, commandData, getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "auction";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(SlashCommandInteractionEvent event) {
|
||||
Guild guild = event.getGuild();
|
||||
if (guild == null) {
|
||||
handleError(event, "This command can only be executed within a guild");
|
||||
return;
|
||||
}
|
||||
|
||||
GuildChannel outputChannel = CommandOutputChannels.getOutputChannel(guild, OutputType.AUCTION);
|
||||
if (outputChannel == null) {
|
||||
handleError(event, "This guild does not have an Auction channel set");
|
||||
return;
|
||||
}
|
||||
|
||||
TextChannel textChannel = validTextChannel(event, outputChannel);
|
||||
if (textChannel == null) {
|
||||
handleError(event, "This guild has an invalid Auction channel");
|
||||
return;
|
||||
}
|
||||
|
||||
Integer minimumIncrease = event.getOption("minimum-increase", OptionMapping::getAsInt);
|
||||
if (minimumIncrease == null) {
|
||||
handleError(event, "Missing required minimum increase option");
|
||||
return;
|
||||
}
|
||||
|
||||
MessageEmbed messageEmbed = buildAuctionEmbed(event, minimumIncrease);
|
||||
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 path = Path.of(dataFolder + File.separator + UUID.randomUUID() + "." + screenshot.getFileExtension());
|
||||
screenshot.getProxy().downloadToFile(path.toFile()).thenAccept(file ->
|
||||
success.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());
|
||||
}
|
||||
|
||||
private MessageEmbed buildAuctionEmbed(SlashCommandInteractionEvent event, int minimumIncrease) {
|
||||
Member member = event.getMember();
|
||||
if (member == null) {
|
||||
return handleBuildEmbedError(event, "You are not a member of this guild");
|
||||
}
|
||||
EmbedBuilder embedBuilder = new EmbedBuilder();
|
||||
String item = event.getOption("item", OptionMapping::getAsString);
|
||||
if (item == null)
|
||||
return handleBuildEmbedError(event, "Missing required item option");
|
||||
if (item.length() > 128)
|
||||
return handleBuildEmbedError(event, "Your item name is too long");
|
||||
embedBuilder.appendDescription("**Item**: " + item);
|
||||
|
||||
Integer amount = event.getOption("amount", OptionMapping::getAsInt);
|
||||
if (amount == null)
|
||||
return handleBuildEmbedError(event, "Missing required amount option");
|
||||
embedBuilder.appendDescription("\n**Amount**: " + 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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
String description = event.getOption("description", OptionMapping::getAsString);
|
||||
if (description != null)
|
||||
embedBuilder.appendDescription("\n**Description**: " + description);
|
||||
|
||||
embedBuilder
|
||||
.setAuthor(member.getEffectiveName(), null, member.getAvatarUrl())
|
||||
.setTitle("Auction")
|
||||
.setColor(Color.ORANGE)
|
||||
.appendDescription("\n\nCloses <t:" + (Instant.now().getEpochSecond() + TimeUnit.DAYS.toSeconds(1)) + ":R>");
|
||||
return embedBuilder.build();
|
||||
}
|
||||
|
||||
private MessageEmbed handleBuildEmbedError(SlashCommandInteractionEvent event, String s) {
|
||||
event.replyEmbeds(Util.genericErrorEmbed("Error", s))
|
||||
.setEphemeral(true)
|
||||
.queue();
|
||||
return null;
|
||||
}
|
||||
|
||||
private void handleError(SlashCommandInteractionEvent event, String s) {
|
||||
event.replyEmbeds(Util.genericErrorEmbed("Error", s))
|
||||
.setEphemeral(true)
|
||||
.queue();
|
||||
}
|
||||
|
||||
private TextChannel validTextChannel(SlashCommandInteractionEvent event, GuildChannel guildChannel) {
|
||||
if (guildChannel == null) {
|
||||
event.replyEmbeds(Util.genericErrorEmbed("Error", "This server does not have a valid auction channel"))
|
||||
.setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!(guildChannel instanceof TextChannel channel)) {
|
||||
event.replyEmbeds(Util.genericErrorEmbed("Error", "A auction channel can't be of type: " + guildChannel.getType().name()))
|
||||
.setEphemeral(true).queue(RestAction.getDefaultSuccess(), Util::handleFailure);
|
||||
return null;
|
||||
}
|
||||
return channel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void suggest(CommandAutoCompleteInteractionEvent event) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHelpMessage() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandData getCommandData() {
|
||||
return commandData;
|
||||
}
|
||||
}
|
||||
|
|
@ -135,6 +135,23 @@ public class DatabaseTables {
|
|||
}
|
||||
}
|
||||
|
||||
private void createAuctionTable() {
|
||||
String sql = "CREATE TABLE IF NOT EXISTS auctions(" +
|
||||
"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, " +
|
||||
"PRIMARY KEY (message_id)" +
|
||||
")";
|
||||
try {
|
||||
connection.prepareStatement(sql).executeUpdate();
|
||||
} catch (SQLException e) {
|
||||
Logger.sql(e);
|
||||
Logger.severe("Unable to create auction table, shutting down...");
|
||||
}
|
||||
}
|
||||
|
||||
public static void createTables(Connection connection) {
|
||||
if (instance == null)
|
||||
instance = new DatabaseTables(connection);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,91 @@
|
|||
package com.alttd.database.queries.QueriesAuctions;
|
||||
|
||||
import com.alttd.AltitudeBot;
|
||||
import net.dv8tion.jda.api.entities.Guild;
|
||||
import net.dv8tion.jda.api.entities.Message;
|
||||
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.time.Instant;
|
||||
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 long expireTime;
|
||||
|
||||
public Auction(long messageId, long channelId, long guildId, int startingPrice, long expireTime) {
|
||||
this.messageId = messageId;
|
||||
this.channelId = channelId;
|
||||
this.guildId = guildId;
|
||||
this.startingPrice = startingPrice;
|
||||
this.expireTime = expireTime;
|
||||
}
|
||||
|
||||
public Auction(@NotNull Message message, long channelId, long guildId, int startingPrice, long expireTime) {
|
||||
this.messageId = message.getIdLong();
|
||||
this.channelId = channelId;
|
||||
this.guildId = guildId;
|
||||
this.startingPrice = startingPrice;
|
||||
this.expireTime = expireTime;
|
||||
}
|
||||
|
||||
public void updateMessage(Consumer<? super Message> success, @Nullable Consumer<? super String> failure) {
|
||||
Guild guild = AltitudeBot.getInstance().getJDA().getGuildById(guildId);
|
||||
if (guild == null) {
|
||||
if (failure != null)
|
||||
failure.accept("Unable to retrieve auction message due to invalid guild");
|
||||
return;
|
||||
}
|
||||
|
||||
TextChannel textChannel = guild.getTextChannelById(channelId);
|
||||
if (textChannel == null) {
|
||||
if (failure != null)
|
||||
failure.accept("Unable to retrieve auction message due to invalid text channel");
|
||||
return;
|
||||
}
|
||||
|
||||
textChannel.retrieveMessageById(messageId).queue(success, b -> {
|
||||
if (failure != null)
|
||||
failure.accept("Unable to retrieve the auction message");
|
||||
});
|
||||
}
|
||||
|
||||
public long getExpireTime() {
|
||||
return expireTime;
|
||||
}
|
||||
|
||||
public long getMessageId() {
|
||||
return messageId;
|
||||
}
|
||||
|
||||
public long getChannelId() {
|
||||
return channelId;
|
||||
}
|
||||
|
||||
public long getGuildId() {
|
||||
return guildId;
|
||||
}
|
||||
|
||||
public int getStartingPrice() {
|
||||
return startingPrice;
|
||||
}
|
||||
|
||||
public boolean updateExpiry() {
|
||||
long future = Instant.now().toEpochMilli() + TimeUnit.MINUTES.toMillis(5);
|
||||
if (expireTime < future) {
|
||||
expireTime = future;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(@NotNull Object o) {
|
||||
Auction o1 = (Auction) o;
|
||||
return Long.compare(expireTime, o1.getExpireTime());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
package com.alttd.database.queries.QueriesAuctions;
|
||||
|
||||
import com.alttd.database.Database;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.HashMap;
|
||||
|
||||
public class QueriesAuction {
|
||||
|
||||
public static HashMap<Long, Auction> getAuctions() {
|
||||
HashMap<Long, Auction> auctions = new HashMap<>();
|
||||
String sql = "SELECT * FROM auctions";
|
||||
try {
|
||||
PreparedStatement statement = Database.getDatabase().getConnection().prepareStatement(sql);
|
||||
|
||||
ResultSet resultSet = statement.executeQuery();
|
||||
while (resultSet.next()) {
|
||||
long messageId = resultSet.getLong("message_id");
|
||||
long channelId = resultSet.getLong("channel_id");
|
||||
long guildId = resultSet.getLong("guild_id");
|
||||
int startingPrice = resultSet.getInt("starting_price");
|
||||
long expireTime = resultSet.getLong("expire_time");
|
||||
auctions.put(messageId, new Auction(messageId, channelId, guildId, startingPrice, expireTime));
|
||||
}
|
||||
} catch (SQLException exception) {
|
||||
exception.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
return auctions;
|
||||
}
|
||||
|
||||
public static boolean saveAuction(Auction auction) {
|
||||
String sql = "INSERT INTO auctions " +
|
||||
"(message_id, channel_id, guild_id, starting_price, expire_time) " +
|
||||
"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(6, auction.getExpireTime());
|
||||
|
||||
return statement.executeUpdate() == 1;
|
||||
} catch (SQLException exception) {
|
||||
exception.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean removeAuction(Auction auction) {
|
||||
String sql = "DELETE FROM auctions " +
|
||||
"WHERE message_id = ?";
|
||||
try {
|
||||
PreparedStatement statement = Database.getDatabase().getConnection().prepareStatement(sql);
|
||||
statement.setLong(1, auction.getMessageId());
|
||||
return statement.executeUpdate() == 1;
|
||||
} catch (SQLException exception) {
|
||||
exception.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -5,5 +5,7 @@ public enum OutputType {
|
|||
SUGGESTION_REVIEW,
|
||||
MOD_LOG,
|
||||
EVIDENCE,
|
||||
AUCTION,
|
||||
AUCTION_LOG,
|
||||
CRATE_TEAM
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,8 +4,10 @@ import com.alttd.buttonManager.ButtonManager;
|
|||
import com.alttd.commandManager.CommandManager;
|
||||
import com.alttd.contextMenuManager.ContextMenuManager;
|
||||
import com.alttd.modalManager.ModalManager;
|
||||
import com.alttd.reminders.ReminderScheduler;
|
||||
import com.alttd.schedulers.AuctionScheduler;
|
||||
import com.alttd.schedulers.ReminderScheduler;
|
||||
import com.alttd.request.RequestManager;
|
||||
import com.alttd.selectMenuManager.SelectMenuManager;
|
||||
import com.alttd.util.Logger;
|
||||
import net.dv8tion.jda.api.JDA;
|
||||
import net.dv8tion.jda.api.events.ReadyEvent;
|
||||
|
|
@ -30,15 +32,23 @@ public class JDAListener extends ListenerAdapter {
|
|||
ButtonManager buttonManager = new ButtonManager();
|
||||
ModalManager modalManager = new ModalManager(buttonManager);
|
||||
ContextMenuManager contextMenuManager = new ContextMenuManager(modalManager);
|
||||
CommandManager commandManager = new CommandManager(jda, modalManager, contextMenuManager, chatListener);
|
||||
jda.addEventListener(buttonManager, modalManager, commandManager, contextMenuManager, chatListener);
|
||||
ReminderScheduler reminderScheduler = ReminderScheduler.getInstance(jda);
|
||||
if (reminderScheduler == null) {
|
||||
Logger.severe("Unable to start reminder scheduler!");
|
||||
}
|
||||
SelectMenuManager selectMenuManager = new SelectMenuManager();
|
||||
CommandManager commandManager = new CommandManager(jda, modalManager, contextMenuManager, chatListener, selectMenuManager);
|
||||
jda.addEventListener(buttonManager, modalManager, commandManager, contextMenuManager, chatListener, selectMenuManager);
|
||||
startSchedulers();
|
||||
// RequestManager.init();
|
||||
}
|
||||
|
||||
private void startSchedulers() {
|
||||
ReminderScheduler reminderScheduler = ReminderScheduler.getInstance(jda);
|
||||
if (reminderScheduler == null)
|
||||
Logger.severe("Unable to start reminder scheduler!");
|
||||
|
||||
AuctionScheduler auctionScheduler = AuctionScheduler.getInstance();
|
||||
if (auctionScheduler == null)
|
||||
Logger.severe("Unable to start auction scheduler!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSelectMenuInteraction(@NotNull SelectMenuInteractionEvent event) {
|
||||
String s = event.getComponentId();
|
||||
|
|
|
|||
135
src/main/java/com/alttd/schedulers/AuctionScheduler.java
Normal file
135
src/main/java/com/alttd/schedulers/AuctionScheduler.java
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
package com.alttd.schedulers;
|
||||
|
||||
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.util.Logger;
|
||||
import com.alttd.util.Util;
|
||||
import net.dv8tion.jda.api.EmbedBuilder;
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
import net.dv8tion.jda.api.entities.MessageEmbed;
|
||||
import net.dv8tion.jda.api.entities.channel.middleman.GuildChannel;
|
||||
import net.dv8tion.jda.api.entities.channel.middleman.GuildMessageChannel;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.awt.*;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class AuctionScheduler {
|
||||
private static AuctionScheduler instance = null;
|
||||
private final HashMap<Long, Auction> auctions;
|
||||
private Auction nextAuction;
|
||||
|
||||
private AuctionScheduler() {
|
||||
instance = this;
|
||||
auctions = QueriesAuction.getAuctions();
|
||||
if (auctions == null) {
|
||||
Logger.severe("Unable to retrieve auctions");
|
||||
instance = null;
|
||||
return;
|
||||
}
|
||||
setNextAuction();
|
||||
ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
|
||||
scheduledExecutorService.scheduleWithFixedDelay(new AuctionScheduler.AuctionRun(), 0, 1, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
private void setNextAuction() {
|
||||
Optional<Auction> first = auctions.values().stream().sorted().findFirst();
|
||||
if (first.isEmpty())
|
||||
nextAuction = null;
|
||||
else
|
||||
nextAuction = first.get();
|
||||
}
|
||||
|
||||
public static AuctionScheduler getInstance() {
|
||||
if (instance == null)
|
||||
instance = new AuctionScheduler();
|
||||
return instance;
|
||||
}
|
||||
|
||||
public synchronized void addAuction(Auction auction) {
|
||||
if (!QueriesAuction.saveAuction(auction))
|
||||
Logger.warning("Unable to save auction %", auction.getMessageId() + "");
|
||||
auctions.put(auction.getMessageId(), auction);
|
||||
setNextAuction();
|
||||
}
|
||||
|
||||
public synchronized void updateAuction(Auction auction) {
|
||||
QueriesAuction.saveAuction(auction);
|
||||
setNextAuction();
|
||||
}
|
||||
|
||||
public synchronized void removeAuction(Auction auction) {
|
||||
auctions.remove(auction.getMessageId());
|
||||
setNextAuction();
|
||||
if (!QueriesAuction.removeAuction(auction))
|
||||
Logger.warning("Unable to remove auction %", auction.getMessageId() + "");
|
||||
}
|
||||
|
||||
public Auction getAuction(long messageId) {
|
||||
return auctions.getOrDefault(messageId, null);
|
||||
}
|
||||
|
||||
public synchronized void finishAuction(Auction auction, @Nullable Member instaBuy) {
|
||||
auction.updateMessage(success -> {
|
||||
List<MessageEmbed> embeds = success.getEmbeds();
|
||||
if (embeds.isEmpty()) {
|
||||
Logger.warning("Received auction with no embed contents");
|
||||
return;
|
||||
}
|
||||
GuildChannel outputChannel = CommandOutputChannels.getOutputChannel(success.getGuild(), OutputType.AUCTION_LOG);
|
||||
if (outputChannel != null) {
|
||||
if (!(outputChannel instanceof GuildMessageChannel channel)) {
|
||||
Logger.warning("Error" + outputChannel.getType().name() + " is not a valid crate auction log channel type");
|
||||
return;
|
||||
}
|
||||
if (!channel.canTalk()) {
|
||||
Logger.warning("Error can't talk in auction log channel");
|
||||
return;
|
||||
}
|
||||
if (sendEmbed(embeds.get(0), channel, instaBuy))
|
||||
success.delete().queue();
|
||||
} else
|
||||
success.delete().queue();
|
||||
}, Logger::warning);
|
||||
removeAuction(auction);
|
||||
}
|
||||
|
||||
private boolean sendEmbed(MessageEmbed embed, GuildMessageChannel textChannel, Member instaBuy) {
|
||||
EmbedBuilder embedBuilder = new EmbedBuilder(embed)
|
||||
.clearFields();
|
||||
List<MessageEmbed.Field> fields = embed.getFields();
|
||||
if (instaBuy != null)
|
||||
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) {
|
||||
field = fields.get(1);
|
||||
if (field.getName() != null && field.getName().equals("Current Bid") && field.getValue() != null)
|
||||
embedBuilder.addField("Winning Bid", field.getValue(), false);
|
||||
}
|
||||
}
|
||||
if (embedBuilder.getFields().size() != 0)
|
||||
embedBuilder.setColor(Color.GREEN);
|
||||
else
|
||||
embedBuilder.setColor(Color.RED);
|
||||
textChannel.sendMessageEmbeds(embedBuilder.build()).queue(Util::ignoreSuccess, failure -> Logger.warning("Failed to log auction result"));
|
||||
return true;
|
||||
}
|
||||
private class AuctionRun implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
long time = new Date().getTime();
|
||||
while (nextAuction != null && time > nextAuction.getExpireTime()) {
|
||||
finishAuction(nextAuction, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.alttd.reminders;
|
||||
package com.alttd.schedulers;
|
||||
|
||||
import com.alttd.database.queries.QueriesReminders.QueriesReminders;
|
||||
import com.alttd.database.queries.QueriesReminders.Reminder;
|
||||
|
|
@ -10,7 +10,6 @@ import net.dv8tion.jda.api.entities.Guild;
|
|||
import net.dv8tion.jda.api.entities.Member;
|
||||
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
|
||||
import net.dv8tion.jda.api.requests.RestAction;
|
||||
import net.dv8tion.jda.api.utils.TimeUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
|
|
@ -0,0 +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;
|
||||
|
||||
public abstract class DiscordSelectMenu {
|
||||
|
||||
public abstract String getSelectMenuId();
|
||||
|
||||
public abstract void execute(SelectMenuInteractionEvent event);
|
||||
|
||||
public abstract SelectMenu getSelectMenu(SelectOption ...selectOptions);
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
package com.alttd.selectMenuManager;
|
||||
|
||||
import com.alttd.selectMenuManager.selectMenus.SelectMenuAuction;
|
||||
import com.alttd.util.Util;
|
||||
import net.dv8tion.jda.api.EmbedBuilder;
|
||||
import net.dv8tion.jda.api.events.interaction.component.SelectMenuInteractionEvent;
|
||||
import net.dv8tion.jda.api.hooks.ListenerAdapter;
|
||||
import net.dv8tion.jda.api.requests.RestAction;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public class SelectMenuManager extends ListenerAdapter {
|
||||
|
||||
private final List<DiscordSelectMenu> buttons;
|
||||
|
||||
public SelectMenuManager() {
|
||||
buttons = List.of(new SelectMenuAuction());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSelectMenuInteraction(@NotNull SelectMenuInteractionEvent event) {
|
||||
String selectMenuId = event.getSelectMenu().getId();
|
||||
Optional<DiscordSelectMenu> first = buttons.stream()
|
||||
.filter(discordModal -> discordModal.getSelectMenuId().equalsIgnoreCase(selectMenuId))
|
||||
.findFirst();
|
||||
if (first.isEmpty()) {
|
||||
event.replyEmbeds(new EmbedBuilder()
|
||||
.setTitle("Invalid command")
|
||||
.setDescription("Unable to process select menu with id: [" + selectMenuId + "], please report this issue to a Teri")
|
||||
.setColor(Color.RED)
|
||||
.build())
|
||||
.setEphemeral(true)
|
||||
.queue(RestAction.getDefaultSuccess(), Util::handleFailure);
|
||||
return;
|
||||
}
|
||||
first.get().execute(event);
|
||||
}
|
||||
|
||||
public @Nullable DiscordSelectMenu getDiscordSelectMenuFor(String buttonId) {
|
||||
Optional<DiscordSelectMenu> first = buttons.stream()
|
||||
.filter(discordSelectMenu -> discordSelectMenu.getSelectMenuId().equalsIgnoreCase(buttonId))
|
||||
.findFirst();
|
||||
if (first.isEmpty())
|
||||
return null;
|
||||
return first.get();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,217 @@
|
|||
package com.alttd.selectMenuManager.selectMenus;
|
||||
|
||||
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.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 {
|
||||
@Override
|
||||
public String getSelectMenuId() {
|
||||
return "auction";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(SelectMenuInteractionEvent event) {
|
||||
Member member = event.getMember();
|
||||
if (member == null) {
|
||||
event.replyEmbeds(Util.genericErrorEmbed("Error", "This can only be used in a guild"))
|
||||
.setEphemeral(true).queue();
|
||||
return;
|
||||
}
|
||||
AuctionScheduler auctionScheduler = AuctionScheduler.getInstance();
|
||||
if (auctionScheduler == null) {
|
||||
event.replyEmbeds(Util.genericErrorEmbed("Error", "Unable to get scheduler"))
|
||||
.setEphemeral(true).queue();
|
||||
return;
|
||||
}
|
||||
Auction auction = auctionScheduler.getAuction(event.getMessage().getIdLong());
|
||||
if (auction == null) {
|
||||
event.replyEmbeds(Util.genericErrorEmbed("Error", "Unable to find this auction in the database, please try again"))
|
||||
.setEphemeral(true).queue();
|
||||
return;
|
||||
}
|
||||
|
||||
List<SelectOption> collect = event.getInteraction().getSelectedOptions().stream().filter(opt -> !opt.isDefault()).collect(Collectors.toList());
|
||||
if (collect.isEmpty()) {
|
||||
event.replyEmbeds(Util.genericErrorEmbed("Error", "Received default input"))
|
||||
.setEphemeral(true).queue();
|
||||
return;
|
||||
}
|
||||
if (collect.size() != 1) {
|
||||
event.replyEmbeds(Util.genericErrorEmbed("Error", "Received invalid number of inputs, can only handle one"))
|
||||
.setEphemeral(true).queue();
|
||||
return;
|
||||
}
|
||||
|
||||
SelectOption selectOption = collect.get(0);
|
||||
String value = selectOption.getValue();
|
||||
int bid;
|
||||
try {
|
||||
bid = Integer.parseInt(value);
|
||||
} catch (NumberFormatException e) {
|
||||
event.replyEmbeds(Util.genericErrorEmbed("Error", "Received invalid input"))
|
||||
.setEphemeral(true).queue();
|
||||
return;
|
||||
}
|
||||
|
||||
MessageEmbed messageEmbed = getMessageEmbed(event.getMessage().getEmbeds(), event);
|
||||
if (messageEmbed == null)
|
||||
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;
|
||||
if (selectOption.getLabel().startsWith("Insta Buy")) {
|
||||
auctionScheduler.finishAuction(auction, member);
|
||||
event.replyEmbeds(Util.genericSuccessEmbed("Success", "You successfully insta bought the item for " + bid + "!"))
|
||||
.setEphemeral(true).queue();
|
||||
return;
|
||||
}
|
||||
|
||||
EmbedBuilder embedBuilder = new EmbedBuilder(messageEmbed)
|
||||
.clearFields()
|
||||
.setTimestamp(Instant.now());
|
||||
if (auction.updateExpiry()) {
|
||||
auctionScheduler.updateAuction(auction);
|
||||
String description = messageEmbed.getDescription();
|
||||
if (description != null)
|
||||
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);
|
||||
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 + "!"))
|
||||
.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"))
|
||||
.setEphemeral(true).queue();
|
||||
return null;
|
||||
}
|
||||
return embeds.get(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SelectMenu getSelectMenu(SelectOption... selectOptions) {
|
||||
SelectMenu.Builder builder = SelectMenu.create(getSelectMenuId());
|
||||
if (selectOptions != null)
|
||||
builder.addOptions(selectOptions);
|
||||
return builder.build();
|
||||
}
|
||||
}
|
||||
|
||||
record BidFieldInfo(int bid, Long member) {}
|
||||
Loading…
Reference in New Issue
Block a user