diff --git a/.gitignore b/.gitignore
index 3dff92f..8d7990d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -43,4 +43,5 @@ out/
## Project-specific ignores
##############################
/src/main/resources/config-local.properties
-/src/main/resources/config-prod.properties
\ No newline at end of file
+/src/main/resources/config-prod.properties
+/logs/
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 1f1c642..4b0d9a1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -75,6 +75,12 @@
rkon-core
1.1.2
+
+ ch.qos.logback
+ logback-classic
+ 1.5.18
+ runtime
+
@@ -149,6 +155,7 @@
StartUp
+
diff --git a/src/main/java/StartUp.java b/src/main/java/StartUp.java
index 17c59ee..738a152 100644
--- a/src/main/java/StartUp.java
+++ b/src/main/java/StartUp.java
@@ -7,22 +7,21 @@
import net.dv8tion.jda.api.requests.GatewayIntent;
import net.dv8tion.jda.api.utils.ChunkingFilter;
import net.dv8tion.jda.api.utils.MemberCachePolicy;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.InputStream;
-import java.time.LocalDateTime;
-import java.time.format.DateTimeFormatter;
import java.util.Locale;
import java.util.Properties;
import java.util.ResourceBundle;
public class StartUp {
+ private static final Logger log = LoggerFactory.getLogger(StartUp.class);
+
public static void main(String[] args) {
try {
-
- System.out.println("[CSBot - StartUp - " + LocalDateTime.now().format(DateTimeFormatter.ofPattern("dd.MM.yyyy - HH:mm:ss")) + "] - Application Started");
-
InputStream inputStream = StartUp.class.getClassLoader().getResourceAsStream("config.properties");
Properties properties = new Properties();
properties.load(inputStream);
@@ -44,10 +43,8 @@ public static void main(String[] args) {
.queue();
jda.awaitReady();
- } catch (InterruptedException ex) {
- System.out.println("[CSBot - StartUp - " + LocalDateTime.now().format(DateTimeFormatter.ofPattern("dd.MM.yyyy - HH:mm:ss")) + "] InterruptedException thrown: " + ex.getMessage());
- } catch (IOException ex) {
- System.out.println("[CSBot - StartUp - " + LocalDateTime.now().format(DateTimeFormatter.ofPattern("dd.MM.yyyy - HH:mm:ss")) + "] IOException thrown: " + ex.getMessage());
+ } catch (InterruptedException | IOException ex) {
+ log.error(ex.getMessage(), ex);
}
}
}
diff --git a/src/main/java/ch/yoinc/http/CarthageConnection.java b/src/main/java/ch/yoinc/http/CarthageConnection.java
index f22bcef..3d42f3d 100644
--- a/src/main/java/ch/yoinc/http/CarthageConnection.java
+++ b/src/main/java/ch/yoinc/http/CarthageConnection.java
@@ -101,7 +101,7 @@ private String extractString(JsonObject responseBody, String field) {
if (responseBody.has(field) && !responseBody.get(field).isJsonNull()) {
return responseBody.get(field).getAsString();
}
- return null;
+ return "";
}
private List extractList(JsonObject responseBody, String field, Type type) {
diff --git a/src/main/java/ch/yoinc/http/ConnectionBuilder.java b/src/main/java/ch/yoinc/http/ConnectionBuilder.java
deleted file mode 100644
index 4c1a849..0000000
--- a/src/main/java/ch/yoinc/http/ConnectionBuilder.java
+++ /dev/null
@@ -1,45 +0,0 @@
-package ch.yoinc.http;
-
-import com.google.gson.*;
-import ch.yoinc.model.steam.ResponseData;
-
-import java.io.IOException;
-import java.net.URI;
-import java.net.http.HttpClient;
-import java.net.http.HttpRequest;
-import java.net.http.HttpResponse;
-import java.util.Properties;
-
-public class ConnectionBuilder {
-
- Properties properties;
-
- private final String STEAM_API = "https://api.steampowered.com";
-
- public ConnectionBuilder(Properties properties) {
- this.properties = properties;
- }
-
- public ResponseData fetchSteamUserStats(String steamID) throws InterruptedException, IOException {
-
- HttpClient client = HttpClient.newHttpClient();
-
- HttpRequest request;
- ResponseData responseData;
-
- request = HttpRequest.newBuilder()
- .uri(URI.create(STEAM_API + "/ISteamUser/GetPlayerSummaries/v0002/?key=" + properties.get("steam.api") + "&steamids=" + steamID))
- .build();
- HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString());
-
- responseData = new Gson().fromJson(response.body(), ResponseData.class);
-
- request = HttpRequest.newBuilder()
- .uri(URI.create(STEAM_API + "/ISteamUserStats/GetUserStatsForGame/v0002/?key=" + properties.get("steam.api") + "&appid=730&steamid=" + steamID))
- .build();
- response = client.send(request, HttpResponse.BodyHandlers.ofString());
- responseData.setPlayerstats(new Gson().fromJson(response.body(), ResponseData.class).getPlayerstats());
-
- return responseData;
- }
-}
diff --git a/src/main/java/ch/yoinc/http/ExternalApiConnection.java b/src/main/java/ch/yoinc/http/ExternalApiConnection.java
new file mode 100644
index 0000000..a2c6c4c
--- /dev/null
+++ b/src/main/java/ch/yoinc/http/ExternalApiConnection.java
@@ -0,0 +1,127 @@
+package ch.yoinc.http;
+
+import ch.yoinc.model.leetify.LeetifyMatchResponse;
+import ch.yoinc.model.leetify.LeetifyProfileResponse;
+import com.google.gson.*;
+import ch.yoinc.model.steam.ResponseData;
+import com.google.gson.reflect.TypeToken;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonToken;
+import com.google.gson.stream.JsonWriter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+import java.time.Instant;
+import java.util.List;
+import java.util.Properties;
+
+public class ExternalApiConnection {
+
+ private static final Logger log = LoggerFactory.getLogger(ExternalApiConnection.class);
+
+ private final String apiKey;
+ private final HttpClient client;
+ private final Gson gson;
+ private final Properties properties;
+ private final String LEETIFY_API = "https://api-public.cs-prod.leetify.com";
+ private final String STEAM_API = "https://api.steampowered.com";
+
+ public ExternalApiConnection(Properties properties) {
+ this.properties = properties;
+ this.apiKey = properties.getProperty("leetify.apiToken");
+ gson = new GsonBuilder()
+ .registerTypeAdapter(Instant.class, new TypeAdapter() {
+ @Override
+ public void write(JsonWriter out, Instant value) throws IOException {
+ if (value == null) {
+ out.nullValue();
+ } else {
+ out.value(value.toString());
+ }
+ }
+
+ @Override
+ public Instant read(JsonReader in) throws IOException {
+ if (in.peek() == JsonToken.NULL) {
+ in.nextNull();
+ return null;
+ }
+ return Instant.parse(in.nextString());
+ }
+ })
+ .create();
+ this.client = HttpClient.newHttpClient();
+ }
+
+ public ResponseData fetchSteamUserStats(String steamID) throws InterruptedException, IOException {
+
+ HttpClient client = HttpClient.newHttpClient();
+
+ HttpRequest request;
+ ResponseData responseData;
+
+ request = HttpRequest.newBuilder()
+ .uri(URI.create(STEAM_API + "/ISteamUser/GetPlayerSummaries/v0002/?key=" + properties.get("steam.api") + "&steamids=" + steamID))
+ .build();
+ HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString());
+
+ responseData = new Gson().fromJson(response.body(), ResponseData.class);
+
+ request = HttpRequest.newBuilder()
+ .uri(URI.create(STEAM_API + "/ISteamUserStats/GetUserStatsForGame/v0002/?key=" + properties.get("steam.api") + "&appid=730&steamid=" + steamID))
+ .build();
+ response = client.send(request, HttpResponse.BodyHandlers.ofString());
+ if (response.statusCode() == 200) {
+ responseData.setPlayerstats(new Gson().fromJson(response.body(), ResponseData.class).getPlayerstats());
+ }
+ if (response.statusCode() != 404) {
+ log.warn("fetchSteamUserStats for {} returned status {}, body: {}", steamID, response.statusCode(), response.body());
+ }
+
+ return responseData;
+ }
+
+ public LeetifyProfileResponse getPlayerProfile(String steam64ID, String leetifyID) throws InterruptedException, IOException {
+ String parameter = steam64ID == null ? "id=" + leetifyID : "steam64_id=" + steam64ID;
+ HttpRequest request;
+ request = HttpRequest.newBuilder()
+ .uri(URI.create(LEETIFY_API + "/v3/profile?" + parameter))
+ .header("Authorization", "Bearer " + apiKey)
+ .GET()
+ .build();
+
+ HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString());
+ if (response.statusCode() == 200) {
+ return gson.fromJson(response.body(), LeetifyProfileResponse.class);
+ }
+ if (response.statusCode() != 404) {
+ log.warn("getPlayerProfile for {} returned status {}, body: {}", parameter, response.statusCode(), response.body());
+ }
+ return null;
+ }
+
+ public List getPlayerMatchHistory(String steam64ID, String leetifyID) throws InterruptedException, IOException {
+ String parameter = steam64ID == null ? "id=" + leetifyID : "steam64_id=" + steam64ID;
+ HttpRequest request;
+ request = HttpRequest.newBuilder()
+ .uri(URI.create(LEETIFY_API + "/v3/profile/matches?" + parameter))
+ .header("Authorization", "Bearer " + apiKey)
+ .GET()
+ .build();
+
+ HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString());
+ if (response.statusCode() == 200) {
+ return gson.fromJson(response.body(), new TypeToken>() {
+ }.getType());
+ }
+ if (response.statusCode() != 404) {
+ log.warn("getPlayerMatchHistory for {} returned status {}, body: {}", parameter, response.statusCode(), response.body());
+ }
+ return null;
+ }
+}
diff --git a/src/main/java/ch/yoinc/http/LeetifyConnection.java b/src/main/java/ch/yoinc/http/LeetifyConnection.java
deleted file mode 100644
index ba84869..0000000
--- a/src/main/java/ch/yoinc/http/LeetifyConnection.java
+++ /dev/null
@@ -1,99 +0,0 @@
-package ch.yoinc.http;
-
-import ch.yoinc.model.leetify.LeetifyMatchResponse;
-import ch.yoinc.model.leetify.LeetifyProfileResponse;
-import com.google.gson.Gson;
-import com.google.gson.GsonBuilder;
-import com.google.gson.TypeAdapter;
-import com.google.gson.reflect.TypeToken;
-import com.google.gson.stream.JsonReader;
-import com.google.gson.stream.JsonToken;
-import com.google.gson.stream.JsonWriter;
-
-import java.io.IOException;
-import java.net.URI;
-import java.net.http.HttpClient;
-import java.net.http.HttpRequest;
-import java.net.http.HttpResponse;
-import java.time.Instant;
-import java.time.LocalDateTime;
-import java.time.format.DateTimeFormatter;
-import java.util.List;
-import java.util.Properties;
-
-public class LeetifyConnection {
-
- private final String apiKey;
- private final HttpClient client;
- private final Gson gson;
- private final String LEETIFY_API = "https://api-public.cs-prod.leetify.com";
-
-
- public LeetifyConnection(Properties properties) {
- this.apiKey = properties.getProperty("leetify.apiToken");
- gson = new GsonBuilder()
- .registerTypeAdapter(Instant.class, new TypeAdapter() {
- @Override
- public void write(JsonWriter out, Instant value) throws IOException {
- if (value == null) {
- out.nullValue();
- } else {
- out.value(value.toString());
- }
- }
-
- @Override
- public Instant read(JsonReader in) throws IOException {
- if (in.peek() == JsonToken.NULL) {
- in.nextNull();
- return null;
- }
- return Instant.parse(in.nextString());
- }
- })
- .create();
- this.client = HttpClient.newHttpClient();
- }
-
- public LeetifyProfileResponse getPlayerProfile(String steam64ID, String leetifyID) {
- String parameter = steam64ID == null ? "id=" + leetifyID : "steam64_id=" + steam64ID;
- HttpRequest request;
- request = HttpRequest.newBuilder()
- .uri(URI.create(LEETIFY_API + "/v3/profile?" + parameter))
- .header("Authorization", "Bearer " + apiKey)
- .GET()
- .build();
-
- try {
- HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString());
- return gson.fromJson(response.body(), LeetifyProfileResponse.class);
- } catch (IOException | InterruptedException ex) {
- System.out.println("[CSBot - LeetifyConnection - " + LocalDateTime.now().format(DateTimeFormatter.ofPattern("dd.MM.yyyy - HH:mm:ss")) + "] IOException / InterruptedException thrown: " + ex.getMessage());
- }
- return null;
- }
-
- public List getPlayerMatchHistory(String steam64ID, String leetifyID) {
- String parameter = steam64ID == null ? "id=" + leetifyID : "steam64_id=" + steam64ID;
- HttpRequest request;
- request = HttpRequest.newBuilder()
- .uri(URI.create(LEETIFY_API + "/v3/profile/matches?" + parameter))
- .header("Authorization", "Bearer " + apiKey)
- .GET()
- .build();
-
- try {
- HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString());
- if (response.statusCode() == 200) {
- return gson.fromJson(response.body(), new TypeToken>() {
- }.getType());
- }
- if(response.statusCode() != 404) {
- System.out.println("[CSBot - LeetifyConnection - " + LocalDateTime.now().format(DateTimeFormatter.ofPattern("dd.MM.yyyy - HH:mm:ss")) + "] getPlayerMatchHistory for " + parameter + " returned status " + response.statusCode() + ", body: " + response.body());
- }
- } catch (IOException | InterruptedException ex) {
- System.out.println("[CSBot - LeetifyConnection - " + LocalDateTime.now().format(DateTimeFormatter.ofPattern("dd.MM.yyyy - HH:mm:ss")) + "] IOException / InterruptedException thrown: " + ex.getMessage());
- }
- return null;
- }
-}
diff --git a/src/main/java/ch/yoinc/services/CsStatsService.java b/src/main/java/ch/yoinc/services/CsStatsService.java
index a55e93d..4381565 100644
--- a/src/main/java/ch/yoinc/services/CsStatsService.java
+++ b/src/main/java/ch/yoinc/services/CsStatsService.java
@@ -1,29 +1,32 @@
package ch.yoinc.services;
-import com.google.gson.JsonSyntaxException;
import ch.yoinc.http.CarthageException;
-import ch.yoinc.http.ConnectionBuilder;
+import ch.yoinc.http.ExternalApiConnection;
import ch.yoinc.model.steam.ResponseData;
import net.dv8tion.jda.api.EmbedBuilder;
import net.dv8tion.jda.api.entities.MessageEmbed;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.codehaus.plexus.util.StringUtils;
import org.jetbrains.annotations.NotNull;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import java.io.IOException;
-import java.time.LocalDateTime;
-import java.time.format.DateTimeFormatter;
import java.util.*;
public class CsStatsService {
- ResourceBundle resourceBundle;
- ConnectionBuilder connectionBuilder;
+ private static final Logger log = LoggerFactory.getLogger(CsStatsService.class);
+
+ ResourceBundle resourceBundle;
+ ExternalApiConnection connection;
DataService dataService;
+ DiscordService discordService;
public CsStatsService(Properties properties, DataService dataService) {
this.dataService = dataService;
- connectionBuilder = new ConnectionBuilder(properties);
+ connection = new ExternalApiConnection(properties);
+ discordService = new DiscordService();
}
public EmbedBuilder handleStatsEvent(SlashCommandInteractionEvent event) {
@@ -32,14 +35,14 @@ public EmbedBuilder handleStatsEvent(SlashCommandInteractionEvent event) {
try {
ResponseData responseData = getUserResponseData(Objects.requireNonNull(event.getOption("player")).getAsMentionable().getId());
return responseData.getBasicInfo(resourceBundle);
- } catch (InterruptedException | IOException ex) {
- System.out.println("[CSBot - CsStatsService - " + LocalDateTime.now().format(DateTimeFormatter.ofPattern("dd.MM.yyyy - HH:mm:ss")) + "] InterruptedException / IOException thrown: " + ex.getMessage());
- return new EmbedBuilder().setTitle(resourceBundle.getString("error.interruptedexception"));
- } catch (NullPointerException | JsonSyntaxException ex) {
- System.out.println("[CSBot - CsStatsService - " + LocalDateTime.now().format(DateTimeFormatter.ofPattern("dd.MM.yyyy - HH:mm:ss")) + "] NullPointerException / JSonSyntaxException thrown: " + ex.getMessage());
+ } catch (IOException | InterruptedException ex) {
+ log.error(ex.getMessage(), ex);
+ return new EmbedBuilder().setTitle(resourceBundle.getString("error.connectionerror"));
+ } catch (NullPointerException ex) {
+ log.error(ex.getMessage(), ex);
return new EmbedBuilder().setTitle(resourceBundle.getString("error.privacysettings"));
} catch (CarthageException ex) {
- System.out.println("[CSBot - CsStatsService - " + LocalDateTime.now().format(DateTimeFormatter.ofPattern("dd.MM.yyyy - HH:mm:ss")) + "] CarthageException thrown: " + ex.getMessage());
+ log.error(ex.getMessage(), ex);
return new EmbedBuilder().setTitle(resourceBundle.getString("error.majorerror"));
}
}
@@ -47,17 +50,18 @@ public EmbedBuilder handleStatsEvent(SlashCommandInteractionEvent event) {
public EmbedBuilder handleCompareEvent(SlashCommandInteractionEvent event) {
resourceBundle = ResourceBundle.getBundle("localization", Locale.of("en"));
try {
- String requestedUserOneID = Objects.requireNonNull(event.getOption("playerone")).getAsMentionable().getId();
- String requestedUserTwoID = Objects.requireNonNull(event.getOption("playertwo")).getAsMentionable().getId();
- return comparePlayers(getUserResponseData(requestedUserOneID), getUserResponseData(requestedUserTwoID));
- } catch (NullPointerException ex) {
- System.out.println("[CSBot - CsStatsService - " + LocalDateTime.now().format(DateTimeFormatter.ofPattern("dd.MM.yyyy - HH:mm:ss")) + "] NullPointerException thrown: " + ex.getMessage());
- return new EmbedBuilder().setTitle(resourceBundle.getString("error.wrongqueryparameters"));
+ ResponseData userDataOne = getUserResponseData(Objects.requireNonNull(event.getOption("playerone")).getAsMentionable().getId());
+ ResponseData userDataTwo = getUserResponseData(Objects.requireNonNull(event.getOption("playertwo")).getAsMentionable().getId());
+
+ if(userDataOne == null || userDataTwo == null) {
+ return new EmbedBuilder().setTitle(resourceBundle.getString("error.privacysettings"));
+ }
+ return comparePlayers(userDataOne, userDataTwo);
} catch (InterruptedException | IOException ex) {
- System.out.println("[CSBot - CsStatsService - " + LocalDateTime.now().format(DateTimeFormatter.ofPattern("dd.MM.yyyy - HH:mm:ss")) + "] InterruptedException / IOException thrown: " + ex.getMessage());
- return new EmbedBuilder().setTitle(resourceBundle.getString("error.interruptedexception"));
+ log.error(ex.getMessage(), ex);
+ return new EmbedBuilder().setTitle(resourceBundle.getString("error.connectionerror"));
} catch (CarthageException ex) {
- System.out.println("[CSBot - CsStatsService - " + LocalDateTime.now().format(DateTimeFormatter.ofPattern("dd.MM.yyyy - HH:mm:ss")) + "] CarthageException thrown: " + ex.getMessage());
+ log.error(ex.getMessage(), ex);
return new EmbedBuilder().setTitle(resourceBundle.getString("error.majorerror"));
}
}
@@ -106,12 +110,12 @@ private String getString(long playerOneLong, long playerTwoLong, boolean playerO
}
}
- private ResponseData getUserResponseData(String discordID) throws NullPointerException, InterruptedException, IOException, CarthageException {
+ private ResponseData getUserResponseData(String discordID) throws IOException, InterruptedException, CarthageException {
ResponseData responseData = null;
String steamID = dataService.getSteamIDForDiscordID(discordID);
if (StringUtils.isNotEmpty(steamID)) {
- responseData = connectionBuilder.fetchSteamUserStats(steamID);
+ responseData = connection.fetchSteamUserStats(steamID);
}
return responseData;
}
diff --git a/src/main/java/ch/yoinc/services/DataService.java b/src/main/java/ch/yoinc/services/DataService.java
index 87206d2..b8071fa 100644
--- a/src/main/java/ch/yoinc/services/DataService.java
+++ b/src/main/java/ch/yoinc/services/DataService.java
@@ -6,8 +6,6 @@
import ch.yoinc.model.leetify.LeetifyMatchResponse;
import java.io.IOException;
-import java.time.LocalDateTime;
-import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Properties;
@@ -23,31 +21,18 @@ public void setBotID(String botID) {
this.botID = botID;
}
- public String getSteamIDForDiscordID(String discordID) throws CarthageException {
- try {
- return carthageConnection.getSteamIDForDiscordID(botID, discordID);
- } catch (IOException | InterruptedException ex) {
- throw new CarthageException("Failed to fetch steamID for discordID " + discordID + " from carthage: " + ex.getMessage());
- }
+ public String getSteamIDForDiscordID(String discordID) throws IOException, InterruptedException, CarthageException {
+ return carthageConnection.getSteamIDForDiscordID(botID, discordID);
}
- public List getAllSteamUsers() {
- try {
- return carthageConnection.getAllSteamUsers(botID);
- } catch (IOException | InterruptedException | CarthageException ex) {
- System.out.println("[CSBot - DataService - " + LocalDateTime.now().format(DateTimeFormatter.ofPattern("dd.MM.yyyy - HH:mm:ss")) + "] InterruptedException / IOException / CarthageException thrown: " + ex.getMessage());
- }
- return List.of();
+ public List getAllSteamUsers() throws IOException, InterruptedException, CarthageException {
+ return carthageConnection.getAllSteamUsers(botID);
}
- public List insertAndGetNewMatches(List matches, Integer userID) {
- try {
- if (matches != null && !matches.isEmpty()) {
- List matchIDs = matches.stream().map(match -> match.id).toList();
- return carthageConnection.insertAndGetNewMatches(matchIDs, userID, botID);
- }
- } catch (IOException | InterruptedException | CarthageException ex) {
- System.out.println("[CSBot - DataService - " + LocalDateTime.now().format(DateTimeFormatter.ofPattern("dd.MM.yyyy - HH:mm:ss")) + "] InterruptedException / IOException / CarthageException thrown: " + ex.getMessage());
+ public List insertAndGetNewMatches(List matches, Integer userID) throws IOException, InterruptedException, CarthageException {
+ if (matches != null && !matches.isEmpty()) {
+ List matchIDs = matches.stream().map(match -> match.id).toList();
+ return carthageConnection.insertAndGetNewMatches(matchIDs, userID, botID);
}
return List.of();
}
diff --git a/src/main/java/ch/yoinc/services/DiscordService.java b/src/main/java/ch/yoinc/services/DiscordService.java
index 5fabdfe..1afef30 100644
--- a/src/main/java/ch/yoinc/services/DiscordService.java
+++ b/src/main/java/ch/yoinc/services/DiscordService.java
@@ -2,6 +2,8 @@
import net.dv8tion.jda.api.EmbedBuilder;
+import java.util.Locale;
+
public class DiscordService {
public EmbedBuilder createEmbedBuilder(String title, String description, String imageUrl, String footer) {
@@ -12,6 +14,13 @@ public EmbedBuilder createEmbedBuilder(String title, String description, String
.setFooter(footer);
}
+ public String formatRating(Double rating) {
+ if (rating == null) {
+ return "n/a";
+ }
+ return String.format(Locale.US, "%.2f", rating * 100.0);
+ }
+
public static class YoincEmbedBuilder extends EmbedBuilder {
public YoincEmbedBuilder() {
super();
diff --git a/src/main/java/ch/yoinc/tasks/LeetifyTask.java b/src/main/java/ch/yoinc/tasks/LeetifyTask.java
index 51cb963..a6e6db4 100644
--- a/src/main/java/ch/yoinc/tasks/LeetifyTask.java
+++ b/src/main/java/ch/yoinc/tasks/LeetifyTask.java
@@ -1,6 +1,7 @@
package ch.yoinc.tasks;
-import ch.yoinc.http.LeetifyConnection;
+import ch.yoinc.http.CarthageException;
+import ch.yoinc.http.ExternalApiConnection;
import ch.yoinc.model.internal.InternalUser;
import ch.yoinc.model.leetify.LeetifyMatchResponse;
import ch.yoinc.model.leetify.LeetifyPlayerStatsResponse;
@@ -8,102 +9,112 @@
import ch.yoinc.services.DiscordService;
import net.dv8tion.jda.api.EmbedBuilder;
import net.dv8tion.jda.api.JDA;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import java.awt.*;
+import java.io.IOException;
import java.util.*;
import java.util.List;
-import java.util.Locale;
public class LeetifyTask implements ScheduledTask {
+ private static final Logger log = LoggerFactory.getLogger(LeetifyTask.class);
+
private DataService dataService;
- private LeetifyConnection leetifyConnection;
+ private DiscordService discordService;
+ private ExternalApiConnection connection;
@Override
public void execute(JDA jda, Properties properties) {
if (dataService == null) {
dataService = new DataService(properties);
}
- if (leetifyConnection == null) {
- leetifyConnection = new LeetifyConnection(properties);
+ if (discordService == null) {
+ discordService = new DiscordService();
+ }
+ if (connection == null) {
+ connection = new ExternalApiConnection(properties);
}
dataService.setBotID(jda.getSelfUser().getId());
- List internalUsers = dataService.getAllSteamUsers();
-
- //this map contains all newly played matches found during the next run of the
- //leetify task. its identifier is the match ID and the list contains all
- //match responses with this id. why? because that way it's easier to see if
- //for example a group has played in the same match. this would allow a different message
- //to be sent to the discord instead of the generic "person x has played a match."
- HashMap> newlyPlayedMatches = setNewlyPlayedMatches(internalUsers);
-
- for (String matchID : newlyPlayedMatches.keySet()) {
- List matches = newlyPlayedMatches.get(matchID);
- EmbedBuilder matchEmbed = new EmbedBuilder();
- LeetifyMatchResponse match = matches.getFirst();
- boolean hasWon = match.stats.getFirst().rounds_won >= match.stats.getFirst().rounds_lost; //tie is a victory, change my mind
- if (matches.size() > 1) {
- List playerNames = new ArrayList<>();
- for (LeetifyMatchResponse playerMatch : matches) {
- playerNames.add(playerMatch.stats.getFirst().name);
- }
- String players = String.join(", ", playerNames);
-
- matchEmbed = switch (match.data_source) {
- case "faceit" -> returnFilledEmbed("New Faceit Match",
- Color.ORANGE, players + " played a Faceit match together and " + ((hasWon) ? "**won**." : "**lost**."),
- match.map_name, matchID, "Finished at " + match.finished_at);
- case "matchmaking_wingman" -> returnFilledEmbed("New Wingman Match",
- Color.GREEN, players + " played a Wingman match together and " + ((hasWon) ? "**won**." : "**lost**."),
- match.map_name, matchID, "Finished at " + match.finished_at);
- case "matchmaking" -> returnFilledEmbed("New Competitive Match",
- Color.YELLOW, players + " played a Competitive match together and " + ((hasWon) ? "**won**." : "**lost**."),
- match.map_name, matchID, "Finished at " + match.finished_at);
- default -> matchEmbed;
- };
-
- for (LeetifyMatchResponse playerMatch : matches) {
- LeetifyPlayerStatsResponse stats = playerMatch.stats.getFirst();
- matchEmbed.addField(
- stats.name,
- "Kills: " + stats.total_kills +
- "\nDeaths: " + stats.total_deaths +
- "\nADR: " + stats.dpr +
- "\nRating: " + formatRating(stats.leetify_rating) +
- "\nCT Rating: " + formatRating(stats.ct_leetify_rating) +
- "\nT Rating: " + formatRating(stats.t_leetify_rating),
- true
- );
+ try {
+ List internalUsers = dataService.getAllSteamUsers();
+
+ //this map contains all newly played matches found during the next run of the
+ //leetify task. its identifier is the match ID and the list contains all
+ //match responses with this id. why? because that way it's easier to see if
+ //for example a group has played in the same match. this would allow a different message
+ //to be sent to the discord instead of the generic "person x has played a match."
+ HashMap> newlyPlayedMatches = setNewlyPlayedMatches(internalUsers);
+
+ for (String matchID : newlyPlayedMatches.keySet()) {
+ List matches = newlyPlayedMatches.get(matchID);
+ EmbedBuilder matchEmbed = new EmbedBuilder();
+ LeetifyMatchResponse match = matches.getFirst();
+ boolean hasWon = match.stats.getFirst().rounds_won >= match.stats.getFirst().rounds_lost; //tie is a victory, change my mind
+ if (matches.size() > 1) {
+ List playerNames = new ArrayList<>();
+ for (LeetifyMatchResponse playerMatch : matches) {
+ playerNames.add(playerMatch.stats.getFirst().name);
+ }
+ String players = String.join(", ", playerNames);
+
+ matchEmbed = switch (match.data_source) {
+ case "faceit" -> returnFilledEmbed("New Faceit Match",
+ Color.ORANGE, players + " played a Faceit match together and " + ((hasWon) ? "**won**." : "**lost**."),
+ match.map_name, matchID, "Finished at " + match.finished_at);
+ case "matchmaking_wingman" -> returnFilledEmbed("New Wingman Match",
+ Color.GREEN, players + " played a Wingman match together and " + ((hasWon) ? "**won**." : "**lost**."),
+ match.map_name, matchID, "Finished at " + match.finished_at);
+ case "matchmaking" -> returnFilledEmbed("New Competitive Match",
+ Color.YELLOW, players + " played a Competitive match together and " + ((hasWon) ? "**won**." : "**lost**."),
+ match.map_name, matchID, "Finished at " + match.finished_at);
+ default -> matchEmbed;
+ };
+
+ for (LeetifyMatchResponse playerMatch : matches) {
+ LeetifyPlayerStatsResponse stats = playerMatch.stats.getFirst();
+ matchEmbed.addField(
+ stats.name,
+ "Kills: " + stats.total_kills +
+ "\nDeaths: " + stats.total_deaths +
+ "\nADR: " + stats.dpr +
+ "\nRating: " + discordService.formatRating(stats.leetify_rating) +
+ "\nCT Rating: " + discordService.formatRating(stats.ct_leetify_rating) +
+ "\nT Rating: " + discordService.formatRating(stats.t_leetify_rating),
+ true
+ );
+ }
+ } else {
+ matchEmbed = switch (match.data_source) {
+ case "faceit" -> returnFilledEmbed("New Faceit Match", Color.ORANGE,
+ match.stats.getFirst().name + " played a Faceit match and " + ((hasWon) ? "**won**." : "**lost**."),
+ match.map_name, matchID, "Finished at " + match.finished_at);
+ case "matchmaking_wingman" -> returnFilledEmbed("New Wingman Match", Color.GREEN,
+ match.stats.getFirst().name + " played a Wingman match and " + ((hasWon) ? "**won**." : "**lost**."),
+ match.map_name, matchID, "Finished at " + match.finished_at);
+ case "matchmaking" -> returnFilledEmbed("New Competitive Match", Color.YELLOW,
+ match.stats.getFirst().name + " played a Competitive match and " + ((hasWon) ? "**won**." : "**lost**."),
+ match.map_name, matchID, "Finished at " + match.finished_at);
+ default -> matchEmbed;
+ };
+ matchEmbed
+ .addField("Kills", Integer.toString(match.stats.getFirst().total_kills), true)
+ .addField("Deaths", Integer.toString(match.stats.getFirst().total_deaths), true)
+ .addField("ADR", Double.toString(match.stats.getFirst().dpr), true)
+ .addField("Rating", discordService.formatRating(match.stats.getFirst().leetify_rating), true)
+ .addField("CT Rating", discordService.formatRating(match.stats.getFirst().ct_leetify_rating), true)
+ .addField("T Rating", discordService.formatRating(match.stats.getFirst().t_leetify_rating), true);
}
- } else {
- matchEmbed = switch (match.data_source) {
- case "faceit" -> returnFilledEmbed("New Faceit Match", Color.ORANGE,
- match.stats.getFirst().name + " played a Faceit match and " + ((hasWon) ? "**won**." : "**lost**."),
- match.map_name, matchID, "Finished at " + match.finished_at);
- case "matchmaking_wingman" -> returnFilledEmbed("New Wingman Match", Color.GREEN,
- match.stats.getFirst().name + " played a Wingman match and " + ((hasWon) ? "**won**." : "**lost**."),
- match.map_name, matchID, "Finished at " + match.finished_at);
- case "matchmaking" -> returnFilledEmbed("New Competitive Match", Color.YELLOW,
- match.stats.getFirst().name + " played a Competitive match and " + ((hasWon) ? "**won**." : "**lost**."),
- match.map_name, matchID, "Finished at " + match.finished_at);
- default -> matchEmbed;
- };
- matchEmbed
- .addField("Kills", Integer.toString(match.stats.getFirst().total_kills), true)
- .addField("Deaths", Integer.toString(match.stats.getFirst().total_deaths), true)
- .addField("ADR", Double.toString(match.stats.getFirst().dpr), true)
- .addField("Rating", formatRating(match.stats.getFirst().leetify_rating), true)
- .addField("CT Rating", formatRating(match.stats.getFirst().ct_leetify_rating), true)
- .addField("T Rating", formatRating(match.stats.getFirst().t_leetify_rating), true);
+ Objects.requireNonNull(jda.getTextChannelById(properties.getProperty("discord.channelID"))).sendMessageEmbeds(matchEmbed.build()).queue();
}
- Objects.requireNonNull(jda.getTextChannelById(properties.getProperty("discord.channelID"))).sendMessageEmbeds(matchEmbed.build()).queue();
+ } catch (InterruptedException | IOException | CarthageException ex) {
+ log.error(ex.getMessage(), ex);
}
}
private EmbedBuilder returnFilledEmbed(String title, Color color, String description, String map_name, String matchID, String footer) {
- DiscordService discordService = new DiscordService();
-
String DEFAULT_LEETIFY_URL = "https://leetify.com/app/match-details/%s/overview";
String DEFAULT_MAP_LOGO_URL = "https://raw.githubusercontent.com/MurkyYT/cs2-map-icons/main/images/%s.png";
String DEFAULT_MAP_URL = "https://raw.githubusercontent.com/MurkyYT/cs2-map-icons/main/images/thumbs/%s_1_png.png";
@@ -120,27 +131,31 @@ private EmbedBuilder returnFilledEmbed(String title, Color color, String descrip
private HashMap> setNewlyPlayedMatches(List steamInternalUsers) {
HashMap> results = new HashMap<>();
- for (InternalUser user : steamInternalUsers) {
- List matches = leetifyConnection.getPlayerMatchHistory(user.steamID, null);
+ try {
+ for (InternalUser user : steamInternalUsers) {
+ List matches = connection.getPlayerMatchHistory(user.steamID, null);
- if (matches != null && !matches.isEmpty()) {
- List newMatches = dataService.insertAndGetNewMatches(matches, user.userID);
+ if (matches != null && !matches.isEmpty()) {
+ List newMatches = dataService.insertAndGetNewMatches(matches, user.userID);
- for (String matchID : newMatches) {
- for (LeetifyMatchResponse match : matches) {
- if (match.id.equals(matchID)) {
- results.computeIfAbsent(matchID, k -> new ArrayList<>()).add(match);
+ for (String matchID : newMatches) {
+ for (LeetifyMatchResponse match : matches) {
+ if (match.id.equals(matchID)) {
+ results.computeIfAbsent(matchID, k -> new ArrayList<>()).add(match);
+ }
}
}
}
- }
- try {
- Thread.sleep(10000);
- } catch (InterruptedException ex) {
- Thread.currentThread().interrupt();
- return results;
+ try {
+ Thread.sleep(10000);
+ } catch (InterruptedException ex) {
+ Thread.currentThread().interrupt();
+ return results;
+ }
}
+ } catch (InterruptedException | IOException | CarthageException ex) {
+ log.error(ex.getMessage(), ex);
}
return results;
}
@@ -149,11 +164,4 @@ private HashMap> setNewlyPlayedMatches(List
+
+
+ %d{dd.MM.yyyy - HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n
+
+
+
+
+ logs/csbot.log
+
+ logs/csbot.%d{yyyy-MM-dd}.%i.log
+ 10MB
+ 14
+ 200MB
+
+
+ %d{dd.MM.yyyy - HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/test/java/ch/yoinc/tasks/LeetifyTaskTest.java b/src/test/java/ch/yoinc/tasks/LeetifyTaskTest.java
index c2fdf80..3671a99 100644
--- a/src/test/java/ch/yoinc/tasks/LeetifyTaskTest.java
+++ b/src/test/java/ch/yoinc/tasks/LeetifyTaskTest.java
@@ -1,10 +1,11 @@
package ch.yoinc.tasks;
-import ch.yoinc.http.LeetifyConnection;
+import ch.yoinc.http.ExternalApiConnection;
import ch.yoinc.model.internal.InternalUser;
import ch.yoinc.model.leetify.LeetifyMatchResponse;
import ch.yoinc.model.leetify.LeetifyPlayerStatsResponse;
import ch.yoinc.services.DataService;
+import ch.yoinc.services.DiscordService;
import net.dv8tion.jda.api.EmbedBuilder;
import net.dv8tion.jda.api.entities.MessageEmbed;
import org.junit.jupiter.api.BeforeEach;
@@ -37,8 +38,9 @@ class LeetifyTaskTest {
private LeetifyTask task;
@BeforeEach
- void setUp() {
+ void setUp() throws Exception {
task = new LeetifyTask();
+ setPrivateField("discordService", new DiscordService());
}
// ---------------------------------------------------------------------
@@ -108,9 +110,9 @@ void formatRating_returnsPlaceholder_whenRatingIsMissing() throws Exception {
@Timeout(value = 5, unit = TimeUnit.SECONDS)
void setNewlyPlayedMatches_returnsEmptyMap_whenUserHasNoMatchHistory() throws Exception {
DataService dataService = mock(DataService.class);
- LeetifyConnection leetifyConnection = mock(LeetifyConnection.class);
- when(leetifyConnection.getPlayerMatchHistory(eq("STEAM1"), isNull())).thenReturn(null);
- injectDependencies(dataService, leetifyConnection);
+ ExternalApiConnection connection = mock(ExternalApiConnection.class);
+ when(connection.getPlayerMatchHistory(eq("STEAM1"), isNull())).thenReturn(null);
+ injectDependencies(dataService, connection);
// the method sleeps 10s (real) after every user; pre-interrupting this thread makes
// that Thread.sleep() throw immediately instead of actually waiting
@@ -127,14 +129,14 @@ void setNewlyPlayedMatches_returnsEmptyMap_whenUserHasNoMatchHistory() throws Ex
@Timeout(value = 5, unit = TimeUnit.SECONDS)
void setNewlyPlayedMatches_onlyIncludesMatchesReturnedAsNew() throws Exception {
DataService dataService = mock(DataService.class);
- LeetifyConnection leetifyConnection = mock(LeetifyConnection.class);
+ ExternalApiConnection connection = mock(ExternalApiConnection.class);
LeetifyMatchResponse oldMatch = match("old-match", "Alice");
LeetifyMatchResponse newMatch = match("new-match", "Alice");
List history = List.of(oldMatch, newMatch);
- when(leetifyConnection.getPlayerMatchHistory(eq("STEAM1"), isNull())).thenReturn(history);
+ when(connection.getPlayerMatchHistory(eq("STEAM1"), isNull())).thenReturn(history);
when(dataService.insertAndGetNewMatches(history, 1)).thenReturn(List.of("new-match"));
- injectDependencies(dataService, leetifyConnection);
+ injectDependencies(dataService, connection);
Thread.currentThread().interrupt();
@@ -150,18 +152,18 @@ void setNewlyPlayedMatches_onlyIncludesMatchesReturnedAsNew() throws Exception {
@Timeout(value = 12, unit = TimeUnit.SECONDS)
void setNewlyPlayedMatches_groupsMatchesFromDifferentUsersUnderSameMatchId() throws Exception {
DataService dataService = mock(DataService.class);
- LeetifyConnection leetifyConnection = mock(LeetifyConnection.class);
+ ExternalApiConnection connection = mock(ExternalApiConnection.class);
LeetifyMatchResponse aliceMatch = match("shared-match", "Alice");
LeetifyMatchResponse bobMatch = match("shared-match", "Bob");
List aliceHistory = List.of(aliceMatch);
List bobHistory = List.of(bobMatch);
- when(leetifyConnection.getPlayerMatchHistory(eq("STEAM1"), isNull())).thenReturn(aliceHistory);
- when(leetifyConnection.getPlayerMatchHistory(eq("STEAM2"), isNull())).thenReturn(bobHistory);
+ when(connection.getPlayerMatchHistory(eq("STEAM1"), isNull())).thenReturn(aliceHistory);
+ when(connection.getPlayerMatchHistory(eq("STEAM2"), isNull())).thenReturn(bobHistory);
when(dataService.insertAndGetNewMatches(aliceHistory, 1)).thenReturn(List.of("shared-match"));
when(dataService.insertAndGetNewMatches(bobHistory, 2)).thenReturn(List.of("shared-match"));
- injectDependencies(dataService, leetifyConnection);
+ injectDependencies(dataService, connection);
// the production loop sleeps 10s (real) between users, so the first sleep has to be
// waited out for the second user to be processed at all; once it's back we interrupt
@@ -200,10 +202,8 @@ private MessageEmbed invokeReturnFilledEmbed(String title, Color color, String d
return builder.build();
}
- private String invokeFormatRating(Double rating) throws Exception {
- Method method = LeetifyTask.class.getDeclaredMethod("formatRating", Double.class);
- method.setAccessible(true);
- return (String) method.invoke(null, rating);
+ private String invokeFormatRating(Double rating) {
+ return new DiscordService().formatRating(rating);
}
@SuppressWarnings("unchecked")
@@ -217,9 +217,9 @@ private HashMap> invokeSetNewlyPlayedMatches(
}
}
- private void injectDependencies(DataService dataService, LeetifyConnection leetifyConnection) throws Exception {
+ private void injectDependencies(DataService dataService, ExternalApiConnection connection) throws Exception {
setPrivateField("dataService", dataService);
- setPrivateField("leetifyConnection", leetifyConnection);
+ setPrivateField("connection", connection);
}
private void setPrivateField(String name, Object value) throws Exception {
diff --git a/src/test/resources/logback-test.xml b/src/test/resources/logback-test.xml
new file mode 100644
index 0000000..8781576
--- /dev/null
+++ b/src/test/resources/logback-test.xml
@@ -0,0 +1,4 @@
+
+
+
+