From 201bbacd6a5733cfdd3c04eea5d452d050589c26 Mon Sep 17 00:00:00 2001 From: RappyTV Date: Fri, 25 Apr 2025 21:16:22 +0200 Subject: [PATCH 01/10] Add getter for MessageSchema#message --- .../wrapper/http/schemas/ApiKeyCreationSchema.java | 9 +++++---- .../wrapper/http/schemas/ApiKeyRegenSchema.java | 3 ++- .../wrapper/http/schemas/GiftCodeCreationSchema.java | 6 ++++-- .../wrapper/http/schemas/GiftCodeRedeemSchema.java | 5 ++++- .../wrapper/http/schemas/MessageSchema.java | 12 +++++++++++- 5 files changed, 26 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/rappytv/globaltags/wrapper/http/schemas/ApiKeyCreationSchema.java b/src/main/java/com/rappytv/globaltags/wrapper/http/schemas/ApiKeyCreationSchema.java index 2b7adf0..ae47c8f 100644 --- a/src/main/java/com/rappytv/globaltags/wrapper/http/schemas/ApiKeyCreationSchema.java +++ b/src/main/java/com/rappytv/globaltags/wrapper/http/schemas/ApiKeyCreationSchema.java @@ -7,11 +7,12 @@ public class ApiKeyCreationSchema extends ApiKeyRegenSchema { private final String name; /** - * @param name The API key name - * @param key The regenerated key + * @param message The success message + * @param name The API key name + * @param key The regenerated key */ - public ApiKeyCreationSchema(@NotNull String name, @NotNull String key) { - super(key); + public ApiKeyCreationSchema(@NotNull String message, @NotNull String name, @NotNull String key) { + super(message, key); this.name = name; } diff --git a/src/main/java/com/rappytv/globaltags/wrapper/http/schemas/ApiKeyRegenSchema.java b/src/main/java/com/rappytv/globaltags/wrapper/http/schemas/ApiKeyRegenSchema.java index 422ec68..1233f4d 100644 --- a/src/main/java/com/rappytv/globaltags/wrapper/http/schemas/ApiKeyRegenSchema.java +++ b/src/main/java/com/rappytv/globaltags/wrapper/http/schemas/ApiKeyRegenSchema.java @@ -9,7 +9,8 @@ public class ApiKeyRegenSchema extends MessageSchema { /** * @param key The regenerated key */ - public ApiKeyRegenSchema(@NotNull String key) { + public ApiKeyRegenSchema(@NotNull String message, @NotNull String key) { + super(message); this.key = key; } diff --git a/src/main/java/com/rappytv/globaltags/wrapper/http/schemas/GiftCodeCreationSchema.java b/src/main/java/com/rappytv/globaltags/wrapper/http/schemas/GiftCodeCreationSchema.java index bd978d0..6d49730 100644 --- a/src/main/java/com/rappytv/globaltags/wrapper/http/schemas/GiftCodeCreationSchema.java +++ b/src/main/java/com/rappytv/globaltags/wrapper/http/schemas/GiftCodeCreationSchema.java @@ -7,9 +7,11 @@ public class GiftCodeCreationSchema extends MessageSchema { private final String code; /** - * @param code The gift code + * @param message The success message + * @param code The gift code */ - public GiftCodeCreationSchema(@NotNull String code) { + public GiftCodeCreationSchema(@NotNull String message, @NotNull String code) { + super(message); this.code = code; } diff --git a/src/main/java/com/rappytv/globaltags/wrapper/http/schemas/GiftCodeRedeemSchema.java b/src/main/java/com/rappytv/globaltags/wrapper/http/schemas/GiftCodeRedeemSchema.java index dde1cf7..2d0c4c2 100644 --- a/src/main/java/com/rappytv/globaltags/wrapper/http/schemas/GiftCodeRedeemSchema.java +++ b/src/main/java/com/rappytv/globaltags/wrapper/http/schemas/GiftCodeRedeemSchema.java @@ -1,6 +1,7 @@ package com.rappytv.globaltags.wrapper.http.schemas; import com.google.gson.annotations.SerializedName; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Date; @@ -11,9 +12,11 @@ public class GiftCodeRedeemSchema extends MessageSchema { private final Date expiresAt; /** + * @param message The success message * @param expiresAt When the gift expires */ - public GiftCodeRedeemSchema(@Nullable Date expiresAt) { + public GiftCodeRedeemSchema(@NotNull String message, @Nullable Date expiresAt) { + super(message); this.expiresAt = expiresAt; } diff --git a/src/main/java/com/rappytv/globaltags/wrapper/http/schemas/MessageSchema.java b/src/main/java/com/rappytv/globaltags/wrapper/http/schemas/MessageSchema.java index c6c1294..a146a55 100644 --- a/src/main/java/com/rappytv/globaltags/wrapper/http/schemas/MessageSchema.java +++ b/src/main/java/com/rappytv/globaltags/wrapper/http/schemas/MessageSchema.java @@ -1,6 +1,16 @@ package com.rappytv.globaltags.wrapper.http.schemas; +import org.jetbrains.annotations.NotNull; + public class MessageSchema { - public String message; + protected final String message; + + public MessageSchema(@NotNull String message) { + this.message = message; + } + + public String getMessage() { + return this.message; + } } From 32c1ab52b8e0ccd3c3b4edb252256c3f83502f73 Mon Sep 17 00:00:00 2001 From: RappyTV Date: Fri, 25 Apr 2025 21:16:59 +0200 Subject: [PATCH 02/10] Add formdata support --- .../globaltags/wrapper/http/ApiRequest.java | 55 ++++++--- .../wrapper/http/MultipartData.java | 112 ++++++++++++++++++ 2 files changed, 148 insertions(+), 19 deletions(-) create mode 100644 src/main/java/com/rappytv/globaltags/wrapper/http/MultipartData.java diff --git a/src/main/java/com/rappytv/globaltags/wrapper/http/ApiRequest.java b/src/main/java/com/rappytv/globaltags/wrapper/http/ApiRequest.java index bd4ab74..d1e7a9b 100644 --- a/src/main/java/com/rappytv/globaltags/wrapper/http/ApiRequest.java +++ b/src/main/java/com/rappytv/globaltags/wrapper/http/ApiRequest.java @@ -33,7 +33,8 @@ public class ApiRequest { private final String method; private final String path; private final GlobalTagsAPI api; - private final Map body; + private final HttpRequest.BodyPublisher body; + private final String contentType; private final Class responseType; /** @@ -45,11 +46,16 @@ public class ApiRequest { * @param responseType The class type for parsing the response */ public ApiRequest(GlobalTagsAPI api, String method, String path, Class responseType) { - this(api, method, path, null, responseType); + this.api = api; + this.method = method; + this.path = path; + this.body = HttpRequest.BodyPublishers.noBody(); + this.contentType = null; + this.responseType = responseType; } /** - * Builds a new request. + * Builds a new request with a json body. * * @param api An API instance * @param method The method @@ -61,7 +67,26 @@ public ApiRequest(GlobalTagsAPI api, String method, String path, Map api, String method, String path, MultipartData body, Class responseType) { + this.api = api; + this.method = method; + this.path = path; + this.body = body.getBodyPublisher(); + this.contentType = body.getContentType(); this.responseType = responseType; } @@ -72,12 +97,15 @@ public ApiRequest(GlobalTagsAPI api, String method, String path, Map> consumer) { try { - HttpRequest request = this.getBuilder() + HttpRequest.Builder builder = this.getBuilder() .uri(new URI(this.api.getUrls().getApiBase() + this.path)) - .method(this.method, this.getBodyPublisher()) - .build(); + .method(this.method, this.body); - client.sendAsync(request, HttpResponse.BodyHandlers.ofString()).thenAccept(response -> { + if(this.contentType != null) { + builder.header("Content-Type", this.contentType); + } + + client.sendAsync(builder.build(), HttpResponse.BodyHandlers.ofString()).thenAccept(response -> { boolean success = response.statusCode() >= 200 && response.statusCode() < 300; if(!success) { ErrorSchema body = gson.fromJson(response.body(), ErrorSchema.class); @@ -106,19 +134,8 @@ public void sendRequestAsync(Consumer<@NotNull ApiResponse> consumer) { */ private HttpRequest.Builder getBuilder() { return HttpRequest.newBuilder() - .header("Content-Type", "application/json") .header("Authorization", this.api.getAuthorizationHeader()) .header("X-Language", this.api.getLanguageCode()) .header("X-Agent", this.api.getAgent().toString()); } - - /** - * Get the {@link HttpRequest.BodyPublisher} from the data parameter. - * - * @return The {@link HttpRequest.BodyPublisher} from the data parameter - */ - private HttpRequest.BodyPublisher getBodyPublisher() { - if (this.body == null || this.body.isEmpty()) return HttpRequest.BodyPublishers.noBody(); - return HttpRequest.BodyPublishers.ofString(gson.toJson(this.body)); - } } diff --git a/src/main/java/com/rappytv/globaltags/wrapper/http/MultipartData.java b/src/main/java/com/rappytv/globaltags/wrapper/http/MultipartData.java new file mode 100644 index 0000000..fca126b --- /dev/null +++ b/src/main/java/com/rappytv/globaltags/wrapper/http/MultipartData.java @@ -0,0 +1,112 @@ +package com.rappytv.globaltags.wrapper.http; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.math.BigInteger; +import java.net.http.HttpRequest; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; + +@SuppressWarnings("unused") +public class MultipartData { + + private String boundary; + private HttpRequest.BodyPublisher bodyPublisher; + + private MultipartData() { + } + + public static Builder newBuilder() { + return new Builder(); + } + + public HttpRequest.BodyPublisher getBodyPublisher() { + return this.bodyPublisher; + } + + public String getContentType() { + return "multipart/form-data; boundary=" + this.boundary; + } + + public static class Builder { + + private final String boundary; + private final Charset charset = StandardCharsets.UTF_8; + private final List files = new ArrayList<>(); + private final Map texts = new LinkedHashMap<>(); + + private Builder() { + this.boundary = new BigInteger(128, new Random()).toString(); + } + + public Builder addFile(String name, Path path, String mimeType) { + this.files.add(new MimedFile(name, path, mimeType)); + return this; + } + + public Builder addText(String name, String text) { + this.texts.put(name, text); + return this; + } + + public MultipartData build() throws IOException { + MultipartData mimeMultipartData = new MultipartData(); + mimeMultipartData.boundary = this.boundary; + + byte[] newline = "\r\n".getBytes(this.charset); + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + for (MimedFile file : this.files) { + byteArrayOutputStream.write(("--" + this.boundary).getBytes(this.charset)); + byteArrayOutputStream.write(newline); + byteArrayOutputStream.write(("Content-Disposition: form-data; name=\"" + file.name + "\"; filename=\"" + file.path.getFileName() + "\"").getBytes(this.charset)); + byteArrayOutputStream.write(newline); + byteArrayOutputStream.write(("Content-Type: " + file.mimeType).getBytes(this.charset)); + byteArrayOutputStream.write(newline); + byteArrayOutputStream.write(newline); + byteArrayOutputStream.write(Files.readAllBytes(file.path)); + byteArrayOutputStream.write(newline); + } + for (Map.Entry entry : this.texts.entrySet()) { + byteArrayOutputStream.write(("--" + this.boundary).getBytes(this.charset)); + byteArrayOutputStream.write(newline); + byteArrayOutputStream.write(("Content-Disposition: form-data; name=\"" + entry.getKey() + "\"").getBytes(this.charset)); + byteArrayOutputStream.write(newline); + byteArrayOutputStream.write(newline); + byteArrayOutputStream.write(entry.getValue().getBytes(this.charset)); + byteArrayOutputStream.write(newline); + } + byteArrayOutputStream.write(("--" + this.boundary + "--").getBytes(this.charset)); + + mimeMultipartData.bodyPublisher = HttpRequest.BodyPublishers.ofByteArray(byteArrayOutputStream.toByteArray()); + return mimeMultipartData; + } + + public static class MimedFile { + + private final String name; + private final Path path; + private final String mimeType; + + public MimedFile(String name, Path path, String mimeType) { + this.name = name; + this.path = path; + this.mimeType = mimeType; + } + + public String getName() { + return this.name; + } + + public Path getPath() { + return this.path; + } + + public String getMimeType() { + return this.mimeType; + } + } + } +} \ No newline at end of file From 874b9bad2eee064c9291cf2f8aae0615d5189753 Mon Sep 17 00:00:00 2001 From: RappyTV Date: Fri, 25 Apr 2025 21:17:14 +0200 Subject: [PATCH 03/10] Add icon upload request --- .../globaltags/wrapper/http/ApiHandler.java | 83 ++++++++++++++----- .../globaltags/wrapper/http/Routes.java | 13 +++ .../http/schemas/IconUploadSchema.java | 25 ++++++ 3 files changed, 101 insertions(+), 20 deletions(-) create mode 100644 src/main/java/com/rappytv/globaltags/wrapper/http/schemas/IconUploadSchema.java diff --git a/src/main/java/com/rappytv/globaltags/wrapper/http/ApiHandler.java b/src/main/java/com/rappytv/globaltags/wrapper/http/ApiHandler.java index 77fe417..8e7cc3c 100644 --- a/src/main/java/com/rappytv/globaltags/wrapper/http/ApiHandler.java +++ b/src/main/java/com/rappytv/globaltags/wrapper/http/ApiHandler.java @@ -10,6 +10,9 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.io.File; +import java.io.IOException; +import java.nio.file.Path; import java.util.*; import java.util.function.Consumer; @@ -226,7 +229,7 @@ public void setTag(@NotNull UUID uuid, @NotNull String tag, @NotNull Consumer - consumer.accept(new ApiResponse<>(true, response.getData().message, null)) + consumer.accept(new ApiResponse<>(true, response.getData().getMessage(), null)) ); }); } @@ -266,7 +269,7 @@ public void setPosition(@NotNull UUID uuid, @NotNull GlobalPosition position, @N return; } this.api.getCache().renew(uuid, (info) -> - consumer.accept(new ApiResponse<>(true, response.getData().message, null)) + consumer.accept(new ApiResponse<>(true, response.getData().getMessage(), null)) ); }); } @@ -306,7 +309,47 @@ public void setIcon(@NotNull UUID uuid, @NotNull GlobalIcon icon, @NotNull Consu return; } this.api.getCache().renew(uuid, (info) -> - consumer.accept(new ApiResponse<>(true, response.getData().message, null)) + consumer.accept(new ApiResponse<>(true, response.getData().getMessage(), null)) + ); + }); + } + + /** + * A request to upload a custom icon for a specific uuid + * + * @param uuid The uuid you want to upload the icon for + * @param file The image file you want to upload + * @param consumer The action to be executed on response. + */ + public void uploadIcon(@NotNull UUID uuid, @NotNull File file, @NotNull Consumer> consumer) throws IOException { + Objects.requireNonNull(file); + this.uploadIcon(uuid, file.toPath(), consumer); + } + + /** + * A request to upload a custom icon for a specific uuid + * + * @param uuid The uuid you want to upload the icon for + * @param path The image file path you want to upload + * @param consumer The action to be executed on response. + */ + public void uploadIcon(@NotNull UUID uuid, @NotNull Path path, @NotNull Consumer> consumer) throws IOException { + Objects.requireNonNull(uuid); + Objects.requireNonNull(path); + Objects.requireNonNull(consumer); + new ApiRequest<>( + this.api, + "POST", + Routes.uploadIcon(uuid), + MultipartData.newBuilder().addFile("image", path, "image/png").build(), + IconUploadSchema.class + ).sendRequestAsync((response) -> { + if (!response.isSuccessful()) { + consumer.accept(new ApiResponse<>(false, null, response.getError())); + return; + } + this.api.getCache().renew(uuid, (info) -> + consumer.accept(new ApiResponse<>(true, response.getData(), null)) ); }); } @@ -344,7 +387,7 @@ public void setRoleIconVisibility(@NotNull UUID uuid, boolean visible, @NotNull return; } this.api.getCache().renew(uuid, (info) -> - consumer.accept(new ApiResponse<>(true, response.getData().message, null)) + consumer.accept(new ApiResponse<>(true, response.getData().getMessage(), null)) ); }); } @@ -380,7 +423,7 @@ public void resetTag(@NotNull UUID uuid, @NotNull Consumer> return; } this.api.getCache().renew(uuid, (info) -> - consumer.accept(new ApiResponse<>(true, response.getData().message, null)) + consumer.accept(new ApiResponse<>(true, response.getData().getMessage(), null)) ); }); } @@ -429,7 +472,7 @@ public void updateWatchlistStatus(@NotNull UUID uuid, boolean watched, @NotNull consumer.accept(new ApiResponse<>(false, null, response.getError())); return; } - consumer.accept(new ApiResponse<>(true, response.getData().message, null)); + consumer.accept(new ApiResponse<>(true, response.getData().getMessage(), null)); }); } @@ -557,7 +600,7 @@ public void deleteApiKey(@NotNull UUID uuid, @NotNull String name, @NotNull Cons consumer.accept(new ApiResponse<>(false, null, response.getError())); return; } - consumer.accept(new ApiResponse<>(true, response.getData().message, null)); + consumer.accept(new ApiResponse<>(true, response.getData().getMessage(), null)); }); } @@ -731,7 +774,7 @@ public void deleteGiftCode(@NotNull String code, @NotNull Consumer(false, null, response.getError())); return; } - consumer.accept(new ApiResponse<>(true, response.getData().message, null)); + consumer.accept(new ApiResponse<>(true, response.getData().getMessage(), null)); }); } @@ -755,7 +798,7 @@ public void referPlayer(@NotNull UUID uuid, @NotNull Consumer(false, null, response.getError())); return; } - consumer.accept(new ApiResponse<>(true, response.getData().message, null)); + consumer.accept(new ApiResponse<>(true, response.getData().getMessage(), null)); }); } @@ -781,7 +824,7 @@ public void reportPlayer(@NotNull UUID uuid, @NotNull String reason, @NotNull Co consumer.accept(new ApiResponse<>(false, null, response.getError())); return; } - consumer.accept(new ApiResponse<>(true, response.getData().message, null)); + consumer.accept(new ApiResponse<>(true, response.getData().getMessage(), null)); }); } @@ -918,7 +961,7 @@ public void banPlayer(@NotNull UUID uuid, @NotNull String reason, @Nullable Bool return; } this.api.getCache().renew(uuid, (info) -> - consumer.accept(new ApiResponse<>(true, response.getData().message, null)) + consumer.accept(new ApiResponse<>(true, response.getData().getMessage(), null)) ); }); } @@ -944,7 +987,7 @@ public void unbanPlayer(@NotNull UUID uuid, @NotNull Consumer - consumer.accept(new ApiResponse<>(true, response.getData().message, null)) + consumer.accept(new ApiResponse<>(true, response.getData().getMessage(), null)) ); }); } @@ -973,7 +1016,7 @@ public void editBan(@NotNull UUID uuid, @NotNull String reason, boolean appealab return; } this.api.getCache().renew(uuid, (info) -> - consumer.accept(new ApiResponse<>(true, response.getData().message, null)) + consumer.accept(new ApiResponse<>(true, response.getData().getMessage(), null)) ); }); } @@ -998,7 +1041,7 @@ public void appealBan(@NotNull String reason, @NotNull Consumer(false, null, response.getError())); return; } - consumer.accept(new ApiResponse<>(true, response.getData().message, null)); + consumer.accept(new ApiResponse<>(true, response.getData().getMessage(), null)); }); } @@ -1043,7 +1086,7 @@ public void unlinkDiscord(@NotNull Consumer> consumer) { return; } this.api.getCache().renewSelf((info) -> - consumer.accept(new ApiResponse<>(true, response.getData().message, null)) + consumer.accept(new ApiResponse<>(true, response.getData().getMessage(), null)) ); }); } @@ -1068,7 +1111,7 @@ public void linkEmail(@NotNull String email, @NotNull Consumer(false, null, response.getError())); return; } - consumer.accept(new ApiResponse<>(true, response.getData().message, null)); + consumer.accept(new ApiResponse<>(true, response.getData().getMessage(), null)); }); } @@ -1090,7 +1133,7 @@ public void unlinkEmail(@NotNull Consumer> consumer) { consumer.accept(new ApiResponse<>(false, null, response.getError())); return; } - consumer.accept(new ApiResponse<>(true, response.getData().message, null)); + consumer.accept(new ApiResponse<>(true, response.getData().getMessage(), null)); }); } @@ -1114,7 +1157,7 @@ public void verifyEmail(@NotNull String code, @NotNull Consumer(false, null, response.getError())); return; } - consumer.accept(new ApiResponse<>(true, response.getData().message, null)); + consumer.accept(new ApiResponse<>(true, response.getData().getMessage(), null)); }); } @@ -1185,7 +1228,7 @@ public void createNote(@NotNull UUID uuid, @NotNull String note, @NotNull Consum consumer.accept(new ApiResponse<>(false, null, response.getError())); return; } - consumer.accept(new ApiResponse<>(true, response.getData().message, null)); + consumer.accept(new ApiResponse<>(true, response.getData().getMessage(), null)); }); } @@ -1259,7 +1302,7 @@ public void deleteNote(@NotNull UUID uuid, @NotNull String noteId, @NotNull Cons consumer.accept(new ApiResponse<>(false, null, response.getError())); return; } - consumer.accept(new ApiResponse<>(true, response.getData().message, null)); + consumer.accept(new ApiResponse<>(true, response.getData().getMessage(), null)); }); } } diff --git a/src/main/java/com/rappytv/globaltags/wrapper/http/Routes.java b/src/main/java/com/rappytv/globaltags/wrapper/http/Routes.java index 4e3895d..9c5fa2c 100644 --- a/src/main/java/com/rappytv/globaltags/wrapper/http/Routes.java +++ b/src/main/java/com/rappytv/globaltags/wrapper/http/Routes.java @@ -118,6 +118,19 @@ public static String setIcon(UUID uuid) { return "/players/" + uuid + "/icon"; } + /** + *
+     * Route for
+     * - POST /players/{uuid}/icon/upload
+     * 
+ * + * @param uuid The {@link UUID} of the player you want to upload the icon for + * @return The HTTP route + */ + public static String uploadIcon(UUID uuid) { + return "/players/" + uuid + "/icon/upload"; + } + /** *
      * Route for
diff --git a/src/main/java/com/rappytv/globaltags/wrapper/http/schemas/IconUploadSchema.java b/src/main/java/com/rappytv/globaltags/wrapper/http/schemas/IconUploadSchema.java
new file mode 100644
index 0000000..f081ba6
--- /dev/null
+++ b/src/main/java/com/rappytv/globaltags/wrapper/http/schemas/IconUploadSchema.java
@@ -0,0 +1,25 @@
+package com.rappytv.globaltags.wrapper.http.schemas;
+
+import org.jetbrains.annotations.NotNull;
+
+public class IconUploadSchema extends MessageSchema {
+
+    private final String hash;
+
+    public IconUploadSchema(@NotNull String message, @NotNull String hash) {
+        super(message);
+        this.hash = hash;
+    }
+
+    public String getHash() {
+        return this.hash;
+    }
+
+    @Override
+    public String toString() {
+        return "IconUploadSchema{" +
+                "message='" + this.message + '\'' +
+                ", hash='" + this.hash + '\'' +
+                '}';
+    }
+}

From e3f0149b6fc69a07219304987265e16a1896f504 Mon Sep 17 00:00:00 2001
From: RappyTV 
Date: Sat, 26 Apr 2025 00:46:19 +0200
Subject: [PATCH 04/10] Implement some enum type adapters

---
 .../globaltags/wrapper/http/ApiRequest.java   |  9 ++++++--
 .../model/adapters/GlobalIconTypeAdapter.java | 21 +++++++++++++++++
 .../adapters/GlobalPermissionTypeAdapter.java | 23 +++++++++++++++++++
 .../adapters/GlobalPositionTypeAdapter.java   | 23 +++++++++++++++++++
 4 files changed, 74 insertions(+), 2 deletions(-)
 create mode 100644 src/main/java/com/rappytv/globaltags/wrapper/model/adapters/GlobalIconTypeAdapter.java
 create mode 100644 src/main/java/com/rappytv/globaltags/wrapper/model/adapters/GlobalPermissionTypeAdapter.java
 create mode 100644 src/main/java/com/rappytv/globaltags/wrapper/model/adapters/GlobalPositionTypeAdapter.java

diff --git a/src/main/java/com/rappytv/globaltags/wrapper/http/ApiRequest.java b/src/main/java/com/rappytv/globaltags/wrapper/http/ApiRequest.java
index d1e7a9b..80eed0f 100644
--- a/src/main/java/com/rappytv/globaltags/wrapper/http/ApiRequest.java
+++ b/src/main/java/com/rappytv/globaltags/wrapper/http/ApiRequest.java
@@ -3,9 +3,11 @@
 import com.google.gson.Gson;
 import com.google.gson.GsonBuilder;
 import com.rappytv.globaltags.wrapper.GlobalTagsAPI;
+import com.rappytv.globaltags.wrapper.enums.GlobalIcon;
+import com.rappytv.globaltags.wrapper.enums.GlobalPermission;
+import com.rappytv.globaltags.wrapper.enums.GlobalPosition;
 import com.rappytv.globaltags.wrapper.http.schemas.ErrorSchema;
-import com.rappytv.globaltags.wrapper.model.adapters.DateTypeAdapter;
-import com.rappytv.globaltags.wrapper.model.adapters.UUIDTypeAdapter;
+import com.rappytv.globaltags.wrapper.model.adapters.*;
 import org.jetbrains.annotations.NotNull;
 
 import java.net.URI;
@@ -27,6 +29,9 @@ public class ApiRequest {
     private static final HttpClient client = HttpClient.newHttpClient();
     private static final Gson gson = new GsonBuilder()
             .registerTypeAdapter(Date.class, new DateTypeAdapter())
+            .registerTypeAdapter(GlobalIcon.class, new GlobalIconTypeAdapter())
+            .registerTypeAdapter(GlobalPermission.class, new GlobalPermissionTypeAdapter())
+            .registerTypeAdapter(GlobalPosition.class, new GlobalPositionTypeAdapter())
             .registerTypeAdapter(UUID.class, new UUIDTypeAdapter())
             .create();
 
diff --git a/src/main/java/com/rappytv/globaltags/wrapper/model/adapters/GlobalIconTypeAdapter.java b/src/main/java/com/rappytv/globaltags/wrapper/model/adapters/GlobalIconTypeAdapter.java
new file mode 100644
index 0000000..d659509
--- /dev/null
+++ b/src/main/java/com/rappytv/globaltags/wrapper/model/adapters/GlobalIconTypeAdapter.java
@@ -0,0 +1,21 @@
+package com.rappytv.globaltags.wrapper.model.adapters;
+
+import com.google.gson.TypeAdapter;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonWriter;
+import com.rappytv.globaltags.wrapper.enums.GlobalIcon;
+
+import java.io.IOException;
+
+public class GlobalIconTypeAdapter extends TypeAdapter {
+
+    @Override
+    public void write(JsonWriter out, GlobalIcon icon) throws IOException {
+        out.value(icon.name());
+    }
+
+    @Override
+    public GlobalIcon read(JsonReader in) throws IOException {
+        return GlobalIcon.valueOf(in.nextString().toUpperCase());
+    }
+}
diff --git a/src/main/java/com/rappytv/globaltags/wrapper/model/adapters/GlobalPermissionTypeAdapter.java b/src/main/java/com/rappytv/globaltags/wrapper/model/adapters/GlobalPermissionTypeAdapter.java
new file mode 100644
index 0000000..13c0c69
--- /dev/null
+++ b/src/main/java/com/rappytv/globaltags/wrapper/model/adapters/GlobalPermissionTypeAdapter.java
@@ -0,0 +1,23 @@
+package com.rappytv.globaltags.wrapper.model.adapters;
+
+import com.google.gson.TypeAdapter;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonToken;
+import com.google.gson.stream.JsonWriter;
+import com.rappytv.globaltags.wrapper.enums.GlobalPermission;
+import com.rappytv.globaltags.wrapper.enums.GlobalPosition;
+
+import java.io.IOException;
+
+public class GlobalPermissionTypeAdapter extends TypeAdapter {
+
+    @Override
+    public void write(JsonWriter out, GlobalPermission permission) throws IOException {
+        out.value(permission.name());
+    }
+
+    @Override
+    public GlobalPermission read(JsonReader in) throws IOException {
+        return GlobalPermission.valueOf(in.nextString().toUpperCase());
+    }
+}
diff --git a/src/main/java/com/rappytv/globaltags/wrapper/model/adapters/GlobalPositionTypeAdapter.java b/src/main/java/com/rappytv/globaltags/wrapper/model/adapters/GlobalPositionTypeAdapter.java
new file mode 100644
index 0000000..60b2715
--- /dev/null
+++ b/src/main/java/com/rappytv/globaltags/wrapper/model/adapters/GlobalPositionTypeAdapter.java
@@ -0,0 +1,23 @@
+package com.rappytv.globaltags.wrapper.model.adapters;
+
+import com.google.gson.TypeAdapter;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonToken;
+import com.google.gson.stream.JsonWriter;
+import com.rappytv.globaltags.wrapper.enums.GlobalIcon;
+import com.rappytv.globaltags.wrapper.enums.GlobalPosition;
+
+import java.io.IOException;
+
+public class GlobalPositionTypeAdapter extends TypeAdapter {
+
+    @Override
+    public void write(JsonWriter out, GlobalPosition position) throws IOException {
+        out.value(position.name());
+    }
+
+    @Override
+    public GlobalPosition read(JsonReader in) throws IOException {
+        return GlobalPosition.valueOf(in.nextString().toUpperCase());
+    }
+}

From e5a915bdf0e125ad7150e589c5cb26cd0fc44d51 Mon Sep 17 00:00:00 2001
From: RappyTV 
Date: Sat, 26 Apr 2025 00:47:00 +0200
Subject: [PATCH 05/10] Improve player schema

---
 .../http/schemas/PlayerInfoSchema.java        |  7 ++-
 .../globaltags/wrapper/model/PlayerInfo.java  | 43 +++++++------------
 2 files changed, 21 insertions(+), 29 deletions(-)

diff --git a/src/main/java/com/rappytv/globaltags/wrapper/http/schemas/PlayerInfoSchema.java b/src/main/java/com/rappytv/globaltags/wrapper/http/schemas/PlayerInfoSchema.java
index 7f767f5..8f052b9 100644
--- a/src/main/java/com/rappytv/globaltags/wrapper/http/schemas/PlayerInfoSchema.java
+++ b/src/main/java/com/rappytv/globaltags/wrapper/http/schemas/PlayerInfoSchema.java
@@ -1,8 +1,11 @@
 package com.rappytv.globaltags.wrapper.http.schemas;
 
+import com.rappytv.globaltags.wrapper.enums.GlobalPermission;
 import com.rappytv.globaltags.wrapper.model.BanInfo;
 import com.rappytv.globaltags.wrapper.model.PlayerInfo;
 
+import java.util.List;
+
 public class PlayerInfoSchema {
 
     public String tag;
@@ -11,7 +14,7 @@ public class PlayerInfoSchema {
     public PlayerInfo.ReferralInfo referrals;
     public String roleIcon;
     public boolean hideRoleIcon;
-    public String[] roles;
-    public String[] permissions;
+    public List roles;
+    public List permissions;
     public BanInfo ban;
 }
diff --git a/src/main/java/com/rappytv/globaltags/wrapper/model/PlayerInfo.java b/src/main/java/com/rappytv/globaltags/wrapper/model/PlayerInfo.java
index e4c086a..2160f14 100644
--- a/src/main/java/com/rappytv/globaltags/wrapper/model/PlayerInfo.java
+++ b/src/main/java/com/rappytv/globaltags/wrapper/model/PlayerInfo.java
@@ -29,7 +29,7 @@ public class PlayerInfo {
     private final String roleIcon;
     private final boolean hideRoleIcon;
     private final List roles;
-    private final Map permissions;
+    private final List permissions;
     private final BanInfo banInfo;
 
     /**
@@ -56,8 +56,8 @@ public PlayerInfo(
             ReferralInfo referralInfo,
             @Nullable String roleIcon,
             boolean hideRoleIcon,
-            @NotNull String[] roles,
-            @NotNull String[] permissions,
+            @NotNull List roles,
+            @NotNull List permissions,
             @Nullable BanInfo banInfo
     ) {
         this.urls = api.getUrls();
@@ -69,19 +69,8 @@ public PlayerInfo(
         this.referralInfo = referralInfo;
         this.roleIcon = roleIcon;
         this.hideRoleIcon = hideRoleIcon;
-        this.roles = List.of(roles);
-        this.permissions = new HashMap<>();
-        List playerPermissions = new ArrayList<>();
-        for (String permission : permissions) {
-            GlobalPermission globalPermission;
-            try {
-                playerPermissions.add(GlobalPermission.valueOf(permission.toUpperCase()));
-            } catch (Exception ignored) {
-            }
-        }
-        for (GlobalPermission permission : GlobalPermission.values()) {
-            this.permissions.put(permission, playerPermissions.contains(permission));
-        }
+        this.roles = roles;
+        this.permissions = permissions;
         this.banInfo = banInfo;
     }
 
@@ -136,11 +125,7 @@ public GlobalPosition getPosition() {
      */
     @NotNull
     public GlobalIcon getGlobalIcon() {
-        try {
-            return GlobalIcon.valueOf(this.icon.type.toUpperCase());
-        } catch (Exception ignored) {
-            return GlobalIcon.NONE;
-        }
+        return this.icon.type;
     }
 
     /**
@@ -167,10 +152,14 @@ public boolean hasCustomGlobalIcon() {
      *
      * @return The URL of the player's global icon, either custom or default.
      */
-    @NotNull
+    @Nullable
     public String getIconUrl() {
+        GlobalIcon icon = this.getGlobalIcon();
         if (this.hasCustomGlobalIcon()) return this.urls.getCustomIcon(this.uuid, this.icon.hash);
-        return this.urls.getDefaultIcon(this.getGlobalIcon());
+        else if(icon == GlobalIcon.CUSTOM || icon == GlobalIcon.NONE) {
+            return null;
+        }
+        return this.urls.getDefaultIcon(icon);
     }
 
     /**
@@ -180,7 +169,7 @@ public String getIconUrl() {
      * @return {@code true} if the player has the specified permission; otherwise {@code false}.
      */
     public boolean hasPermission(GlobalPermission permission) {
-        return this.permissions.containsKey(permission) && this.permissions.get(permission);
+        return this.permissions.contains(permission);
     }
 
     /**
@@ -312,7 +301,7 @@ public String toString() {
      */
     public static class Icon {
 
-        private final String type;
+        private final GlobalIcon type;
         private final String hash;
 
         /**
@@ -321,7 +310,7 @@ public static class Icon {
          * @param type The {@link GlobalIcon} type.
          * @param hash The custom icon hash.
          */
-        public Icon(String type, String hash) {
+        public Icon(GlobalIcon type, String hash) {
             this.type = type;
             this.hash = hash;
         }
@@ -329,7 +318,7 @@ public Icon(String type, String hash) {
         @Override
         public String toString() {
             return "Icon{" +
-                    "type='" + this.type + '\'' +
+                    "name='" + this.type + '\'' +
                     ", hash='" + this.hash + '\'' +
                     '}';
         }

From b8794378ce8f4ca46c9e2c8e91f59ec1e36c05e7 Mon Sep 17 00:00:00 2001
From: RappyTV 
Date: Sat, 26 Apr 2025 00:47:14 +0200
Subject: [PATCH 06/10] Get custom icon url from base api url

---
 .../java/com/rappytv/globaltags/wrapper/GlobalTagsAPI.java  | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/src/main/java/com/rappytv/globaltags/wrapper/GlobalTagsAPI.java b/src/main/java/com/rappytv/globaltags/wrapper/GlobalTagsAPI.java
index 48e60f1..4b81cd7 100644
--- a/src/main/java/com/rappytv/globaltags/wrapper/GlobalTagsAPI.java
+++ b/src/main/java/com/rappytv/globaltags/wrapper/GlobalTagsAPI.java
@@ -16,6 +16,9 @@
  */
 public abstract class GlobalTagsAPI {
 
+    /*+
+     * Default urls
+     */
     private final Urls urls = new Urls();
     /**
      * Default cache options
@@ -229,7 +232,8 @@ public String getRoleIcon(String role) {
         @NotNull
         public String getCustomIcon(UUID uuid, String hash) {
             return String.format(
-                    "https://api.globaltags.xyz/players/%s/icon/%s",
+                    "%s/players/%s/icon/%s",
+                    this.getApiBase(),
                     uuid,
                     hash
             );

From 4a411f427807c0ff519085863c9d083f71ef96cd Mon Sep 17 00:00:00 2001
From: RappyTV 
Date: Sat, 26 Apr 2025 00:47:43 +0200
Subject: [PATCH 07/10] Optimize imports

---
 .../wrapper/model/adapters/GlobalPermissionTypeAdapter.java     | 2 --
 .../wrapper/model/adapters/GlobalPositionTypeAdapter.java       | 2 --
 2 files changed, 4 deletions(-)

diff --git a/src/main/java/com/rappytv/globaltags/wrapper/model/adapters/GlobalPermissionTypeAdapter.java b/src/main/java/com/rappytv/globaltags/wrapper/model/adapters/GlobalPermissionTypeAdapter.java
index 13c0c69..044e954 100644
--- a/src/main/java/com/rappytv/globaltags/wrapper/model/adapters/GlobalPermissionTypeAdapter.java
+++ b/src/main/java/com/rappytv/globaltags/wrapper/model/adapters/GlobalPermissionTypeAdapter.java
@@ -2,10 +2,8 @@
 
 import com.google.gson.TypeAdapter;
 import com.google.gson.stream.JsonReader;
-import com.google.gson.stream.JsonToken;
 import com.google.gson.stream.JsonWriter;
 import com.rappytv.globaltags.wrapper.enums.GlobalPermission;
-import com.rappytv.globaltags.wrapper.enums.GlobalPosition;
 
 import java.io.IOException;
 
diff --git a/src/main/java/com/rappytv/globaltags/wrapper/model/adapters/GlobalPositionTypeAdapter.java b/src/main/java/com/rappytv/globaltags/wrapper/model/adapters/GlobalPositionTypeAdapter.java
index 60b2715..74d4fa3 100644
--- a/src/main/java/com/rappytv/globaltags/wrapper/model/adapters/GlobalPositionTypeAdapter.java
+++ b/src/main/java/com/rappytv/globaltags/wrapper/model/adapters/GlobalPositionTypeAdapter.java
@@ -2,9 +2,7 @@
 
 import com.google.gson.TypeAdapter;
 import com.google.gson.stream.JsonReader;
-import com.google.gson.stream.JsonToken;
 import com.google.gson.stream.JsonWriter;
-import com.rappytv.globaltags.wrapper.enums.GlobalIcon;
 import com.rappytv.globaltags.wrapper.enums.GlobalPosition;
 
 import java.io.IOException;

From daa56c5d34bd68dd743fd544c49df261994987a3 Mon Sep 17 00:00:00 2001
From: RappyTV 
Date: Sat, 26 Apr 2025 00:49:59 +0200
Subject: [PATCH 08/10] Bump version

---
 pom.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/pom.xml b/pom.xml
index 8a51c16..873bb42 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
 
     com.rappytv.globaltags
     GlobalTagsJava
-    1.2.6
+    1.2.7-SNAPSHOT
 
     GlobalTagsJava
     A wrapper for the GlobalTagsAPI

From 390e00907d0288a6ebe162115c83ab7d3c9d0136 Mon Sep 17 00:00:00 2001
From: RappyTV 
Date: Sat, 26 Apr 2025 23:12:37 +0200
Subject: [PATCH 09/10] Add new global icons

---
 .../rappytv/globaltags/wrapper/enums/GlobalIcon.java  | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/src/main/java/com/rappytv/globaltags/wrapper/enums/GlobalIcon.java b/src/main/java/com/rappytv/globaltags/wrapper/enums/GlobalIcon.java
index 58b5865..b79c394 100644
--- a/src/main/java/com/rappytv/globaltags/wrapper/enums/GlobalIcon.java
+++ b/src/main/java/com/rappytv/globaltags/wrapper/enums/GlobalIcon.java
@@ -9,18 +9,26 @@ public enum GlobalIcon {
     ANDROID,
     APPLE,
     BEREAL,
+    CASHAPP,
     CROWN,
     DISCORD,
     DUOLINGO,
     EBIO,
     EPICGAMES,
+    FACEBOOK,
     GAMESCOM,
     GITHUB,
     GITLAB,
+    GLOBALTAGS,
     HEART,
     INSTAGRAM,
     KICK,
+    LABYMOD,
     LABYNET,
+    LINKEDIN,
+    NAMEMC,
+    MASTODON,
+    PATREON,
     PAYPAL,
     PINTEREST,
     PLAYSTATION,
@@ -32,9 +40,12 @@ public enum GlobalIcon {
     STATSFM,
     STEAM,
     TELEGRAM,
+    TELLONYM,
     THREADS,
     TIKTOK,
     TWITCH,
+    VENMO,
+    WECHAT,
     X,
     XBOX,
     YOUTUBE

From 75cd8233b778a85635619eb34e95597be8987be3 Mon Sep 17 00:00:00 2001
From: RappyTV 
Date: Tue, 29 Apr 2025 14:50:06 +0200
Subject: [PATCH 10/10] Bump version

---
 pom.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/pom.xml b/pom.xml
index 873bb42..d128089 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
 
     com.rappytv.globaltags
     GlobalTagsJava
-    1.2.7-SNAPSHOT
+    1.2.7
 
     GlobalTagsJava
     A wrapper for the GlobalTagsAPI