Skip to content

Commit

Permalink
feat: match history command
Browse files Browse the repository at this point in the history
  • Loading branch information
yHSJ committed Dec 17, 2024
1 parent d165776 commit 75d41a6
Show file tree
Hide file tree
Showing 4 changed files with 251 additions and 1 deletion.
5 changes: 4 additions & 1 deletion app/src/main/java/fi/sundae/bot/Bot.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package fi.sundae.bot;

import com.jagrosh.jdautilities.command.CommandClientBuilder;
import fi.sundae.bot.commands.MatchHistoryCommand;
import fi.sundae.bot.commands.MatchmakeCommand;
import fi.sundae.bot.commands.ReadyCommand;
import fi.sundae.bot.tournament.Match;
Expand Down Expand Up @@ -31,7 +32,9 @@ public Bot(String token, String ownerId, String channelId, String adminRoleId) {
.setOwnerId(ownerId)
.setStatus(OnlineStatus.ONLINE)
.addSlashCommands(
new ReadyCommand(MATCHMAKER), new MatchmakeCommand(adminRoleId, MATCHMAKER));
new ReadyCommand(MATCHMAKER),
new MatchmakeCommand(adminRoleId, MATCHMAKER),
new MatchHistoryCommand(channelId, adminRoleId));
jdaBuilder.addEventListeners(commandBuilder.build(), new Listener(MATCHMAKER));

JDA jda = jdaBuilder.build();
Expand Down
14 changes: 14 additions & 0 deletions app/src/main/java/fi/sundae/bot/api/MatchResultSerializer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package fi.sundae.bot.api;

import com.google.gson.JsonElement;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import java.lang.reflect.Type;

public class MatchResultSerializer implements JsonSerializer<MatchResult> {
@Override
public JsonElement serialize(MatchResult src, Type typeOfSrc, JsonSerializationContext context) {
return new JsonPrimitive(src.getSerializedName());
}
}
137 changes: 137 additions & 0 deletions app/src/main/java/fi/sundae/bot/commands/MatchHistoryCommand.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
package fi.sundae.bot.commands;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.jagrosh.jdautilities.command.SlashCommand;
import com.jagrosh.jdautilities.command.SlashCommandEvent;
import fi.sundae.bot.api.MatchResult;
import fi.sundae.bot.api.MatchResultSerializer;
import fi.sundae.bot.tournament.MatchResultEmbed;
import java.awt.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
import net.dv8tion.jda.api.EmbedBuilder;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.MessageEmbed;
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class MatchHistoryCommand extends SlashCommand {

private final String CHANNEL_ID;
private final String ADMIN_ROLE_ID;
private final Logger LOGGER = LoggerFactory.getLogger(MatchHistoryCommand.class);

public MatchHistoryCommand(String channelId, String adminRoleId) {
this.CHANNEL_ID = channelId;
this.ADMIN_ROLE_ID = adminRoleId;
this.name = "match_history";
this.help = "View parsed match history in JSON";
}

@Override
protected void execute(SlashCommandEvent event) {
event.deferReply(true).queue();
if (Objects.requireNonNull(event.getMember()).getRoles().stream()
.noneMatch(role -> role.getId().equals(ADMIN_ROLE_ID))) {
event.getHook().editOriginalEmbeds(getNotAllowedEmbed()).queue();
return;
}

TextChannel channel = event.getJDA().getTextChannelById(CHANNEL_ID);
fetchAllMessages(channel)
.thenAccept(
messages -> {
List<MatchResultEmbed> matchResults = new ArrayList<>();
for (Message message : messages) {
if (!message.getContentRaw().isEmpty() || message.getEmbeds().isEmpty()) {
continue;
}

MessageEmbed embed = message.getEmbeds().get(0);
matchResults.add(new MatchResultEmbed(embed));
}

Gson gson =
new GsonBuilder()
.registerTypeAdapter(MatchResult.class, new MatchResultSerializer())
.create();

event.getHook().editOriginal("```" + gson.toJson(matchResults) + "```").queue();
});
}

public CompletableFuture<List<Message>> fetchAllMessages(TextChannel channel) {
CompletableFuture<List<Message>> future = new CompletableFuture<>();
List<Message> messages = new ArrayList<>();

fetchMessagesBefore(channel, null, messages, future);
return future;
}

private void fetchMessagesBefore(
TextChannel channel,
String lastMessageId,
List<Message> messages,
CompletableFuture<List<Message>> future) {
int fetchLimit = 100; // limit imposed by Discord
if (lastMessageId == null) {
channel
.getHistory()
.retrievePast(fetchLimit)
.queue(
retrievedMessages -> {
List<Message> filteredMessages =
retrievedMessages.stream()
.filter(
message -> channel.getJDA().getSelfUser().equals(message.getAuthor()))
.collect(Collectors.toCollection(ArrayList::new));
messages.addAll(filteredMessages);

if (filteredMessages.size() < fetchLimit) {
future.complete(messages);
} else {
String oldestMessageId =
retrievedMessages.get(retrievedMessages.size() - 1).getId();
fetchMessagesBefore(channel, oldestMessageId, messages, future);
}
},
error -> {
LOGGER.error("failed to fetch messages", error);
future.completeExceptionally(error);
});
} else {
channel
.getHistoryBefore(lastMessageId, fetchLimit)
.queue(
history -> {
List<Message> retrievedMessages = history.getRetrievedHistory();
messages.addAll(retrievedMessages);

if (retrievedMessages.size() < fetchLimit) {
future.complete(messages);
} else {
String oldestMessageId =
retrievedMessages.get(retrievedMessages.size() - 1).getId();
fetchMessagesBefore(channel, oldestMessageId, messages, future);
}
},
error -> {
LOGGER.error("failed to fetch messages", error);
future.completeExceptionally(error);
});
}
}

private MessageEmbed getNotAllowedEmbed() {
return new EmbedBuilder()
.setColor(Color.RED)
.setTitle("Permission Denied")
.setDescription("Only a tournament admin can use this command.")
.build();
}
}
96 changes: 96 additions & 0 deletions app/src/main/java/fi/sundae/bot/tournament/MatchResultEmbed.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package fi.sundae.bot.tournament;

import fi.sundae.bot.api.MatchResult;
import java.awt.Color;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import net.dv8tion.jda.api.entities.MessageEmbed;

public class MatchResultEmbed {
private String playerOne;
private String playerTwo;
private String nodeId;
private String gameId;
private int playerOneKills, playerTwoKills;
private MatchResult result;

public MatchResultEmbed(MessageEmbed embed) {
Color embedColor = embed.getColor();
if (Color.RED.equals(embedColor)) {
String title = Objects.requireNonNull(embed.getTitle()).toLowerCase();
if (title.contains("disagreement")) result = MatchResult.DISAGREEMENT;
else if (title.contains("disconnect")) result = MatchResult.DISCONNECT;
else if (title.contains("timed out")) result = MatchResult.TIMEOUT;

String description = Objects.requireNonNull(embed.getDescription());
String regex = "<@(\\d+)>";

Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(description);
List<String> discordIds;
discordIds = new ArrayList<>();
while (matcher.find()) {
discordIds.add(matcher.group(1));
}
playerOne = discordIds.get(0);
playerTwo = discordIds.get(1);
} else result = MatchResult.FINISHED;

List<MessageEmbed.Field> fields = embed.getFields();
for (MessageEmbed.Field field : fields) {
if ("Player 1".equals(field.getName())) {
playerOne = Objects.requireNonNull(field.getValue()).replace("<@", "").replace(">", "");
} else if ("Player 2".equals(field.getName())) {
playerTwo = Objects.requireNonNull(field.getValue()).replace("<@", "").replace(">", "");
} else if ("Node ID".equals(field.getName())) {
nodeId = Objects.requireNonNull(field.getValue()).replace("`", "");
} else if ("Game ID".equals(field.getName())) {
gameId = Objects.requireNonNull(field.getValue()).replace("`", "");
} else if ("Kill Counts".equals(field.getName())) {
String regex = ":(\\s*\\d+)";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(Objects.requireNonNull(field.getValue()));

List<Integer> numbers = new ArrayList<>();
while (matcher.find()) {
int number = Integer.parseInt(matcher.group(1).trim());
numbers.add(number);
}

playerOneKills = numbers.get(0);
playerTwoKills = numbers.get(1);
}
}
}

public String getPlayerOne() {
return playerOne;
}

public String getPlayerTwo() {
return playerTwo;
}

public String getNodeId() {
return nodeId;
}

public String getGameId() {
return gameId;
}

public int getPlayerOneKills() {
return playerOneKills;
}

public int getPlayerTwoKills() {
return playerTwoKills;
}

public MatchResult getResult() {
return result;
}
}

0 comments on commit 75d41a6

Please sign in to comment.