diff --git a/pom.xml b/pom.xml
index 429ce0f7..e3794548 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
4.0.0
com.namelessmc
- NamelessAPI
+ java-api
canary
diff --git a/src/com/namelessmc/NamelessAPI/NamelessAPI.java b/src/com/namelessmc/NamelessAPI/NamelessAPI.java
deleted file mode 100644
index dfbf3a06..00000000
--- a/src/com/namelessmc/NamelessAPI/NamelessAPI.java
+++ /dev/null
@@ -1,280 +0,0 @@
-package com.namelessmc.NamelessAPI;
-
-import java.io.UnsupportedEncodingException;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.net.URLEncoder;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.UUID;
-
-import com.google.gson.JsonArray;
-import com.google.gson.JsonObject;
-import com.namelessmc.NamelessAPI.Request.Action;
-import com.namelessmc.NamelessAPI.Website.Update;
-
-public final class NamelessAPI {
-
- private static final String DEFAULT_USER_AGENT = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)";
-
- public static boolean DEBUG_MODE = false;
-
- private final URL apiUrl;
-
- private String userAgent = null;
-
- /**
- * @param apiUrl URL of API to connect to, in the format http(s)://yoursite.com/index.php?route=/api/v2/API_KEY
- * @param debug If debug is set to true, debug messages are enabled for every NamelessAPI instance.
- */
- public NamelessAPI(final URL apiUrl, final boolean debug) {
- if (debug) {
- DEBUG_MODE = true;
- }
- this.apiUrl = apiUrl;
- this.userAgent = DEFAULT_USER_AGENT;
- }
-
- /**
- * @param host URL of your website, in the format http(s)://yoursite.com
- * @param apiKey API key
- * @param debug If debug is set to true, debug messages are enabled for every NamelessAPI instance.
- * @throws MalformedURLException
- */
- public NamelessAPI(final String host, final String apiKey, final boolean debug) throws MalformedURLException {
- if (debug) {
- DEBUG_MODE = true;
- }
-
- this.apiUrl = new URL(host + "/index.php?route=/api/v2/" + apiKey);
- this.userAgent = DEFAULT_USER_AGENT;
- }
-
- /**
- * Checks if a web API connection can be established
- * @return
- *
- * - An {@link ApiError} if the api has returned an error
- * - A {@link NamelessException} if the connection was unsuccessful
- * - null if the connection was successful.
- *
- */
- public NamelessException checkWebAPIConnection() {
- try {
- final Request request = new Request(this.apiUrl, this.userAgent, Action.INFO);
- request.connect();
-
- if (request.hasError()) {
- throw new ApiError(request.getError());
- }
-
- final JsonObject response = request.getResponse();
- if (response.has("nameless_version")) {
- return null;
- } else {
- return new NamelessException("Invalid respose: " + response.getAsString());
- }
- } catch (final NamelessException e) {
- return e;
- }
- }
-
- /**
- * Get all announcements
- * @return list of current announcements
- * @throws NamelessException if there is an error in the request
- */
- public List getAnnouncements() throws NamelessException {
- final Request request = new Request(this.apiUrl, this.userAgent, Action.GET_ANNOUNCEMENTS);
- request.connect();
-
- if (request.hasError()) {
- throw new ApiError(request.getError());
- }
-
- final List announcements = new ArrayList<>();
-
- final JsonObject object = request.getResponse();
- object.getAsJsonArray().forEach((element) -> {
- final JsonObject announcementJson = element.getAsJsonObject();
- final String content = announcementJson.get("content").getAsString();
- final String[] display = jsonToArray(announcementJson.get("display").getAsJsonArray());
- final String[] permissions = jsonToArray(announcementJson.get("permissions").getAsJsonArray());
- announcements.add(new Announcement(content, display, permissions));
- });
-
- return announcements;
- }
-
- /**
- * Get all announcements visible for the player with the specified uuid
- * @param uuid UUID of player to get visibile announcements for
- * @return list of current announcements visible to the player
- * @throws NamelessException if there is an error in the request
- */
- public List getAnnouncements(final UUID uuid) throws NamelessException {
- final Request request = new Request(this.apiUrl, this.userAgent, Action.GET_ANNOUNCEMENTS, new ParameterBuilder().add("uuid", uuid).build());
- request.connect();
-
- if (request.hasError()) {
- throw new ApiError(request.getError());
- }
-
- final List announcements = new ArrayList<>();
-
- request.getResponse().get("announcements").getAsJsonArray().forEach((element) -> {
- final JsonObject announcementJson = element.getAsJsonObject();
- final String content = announcementJson.get("content").getAsString();
- final String[] display = jsonToArray(announcementJson.get("display").getAsJsonArray());
- final String[] permissions = jsonToArray(announcementJson.get("permissions").getAsJsonArray());
- announcements.add(new Announcement(content, display, permissions));
- });
-
- return announcements;
- }
-
- public void submitServerInfo(final String jsonData) throws NamelessException {
- final Request request = new Request(this.apiUrl, this.userAgent, Action.SERVER_INFO, new ParameterBuilder().add("info", jsonData).build());
- request.connect();
- if (request.hasError()) {
- throw new ApiError(request.getError());
- }
- }
-
- public Website getWebsite() throws NamelessException {
- final Request request = new Request(this.apiUrl, this.userAgent, Action.INFO);
- request.connect();
-
- if (request.hasError()) {
- throw new ApiError(request.getError());
- }
-
- final JsonObject json = request.getResponse();
-
- final String version = json.get("nameless_version").getAsString();
-
- final String[] modules = jsonToArray(json.get("modules").getAsJsonArray());
-
- final JsonObject updateJson = json.get("version_update").getAsJsonObject();
- final boolean updateAvailable = updateJson.get("update").getAsBoolean();
- Update update;
- if (updateAvailable) {
- final String updateVersion = updateJson.get("version").getAsString();
- final boolean isUrgent = updateJson.get("urgent").getAsBoolean();
- update = new Update(isUrgent, updateVersion);
- } else {
- update = null;
- }
-
- return new Website(version, update, modules);
-
- }
-
- public boolean validateUser(final UUID uuid, final String code) throws NamelessException {
- final String[] parameters = new ParameterBuilder().add("uuid", uuid.toString()).add("code", code).build();
- final Request request = new Request(this.apiUrl, this.userAgent, Action.VALIDATE_USER, parameters);
- request.connect();
- if (request.hasError()) {
- final int errorCode = request.getError();
- if (errorCode == 28) {
- return false;
- }
- throw new ApiError(errorCode);
- }
- return true;
- }
-
- public NamelessPlayer getPlayer(final UUID uuid) throws NamelessException {
- return new NamelessPlayer(uuid, this.apiUrl, this.userAgent);
- }
-
- public Map getRegisteredUsers(final boolean hideInactive, final boolean hideBanned) throws NamelessException {
- final Request request = new Request(this.apiUrl, this.userAgent, Action.LIST_USERS);
- request.connect();
- if (request.hasError()) {
- throw new ApiError(request.getError());
- }
-
- final Map users = new HashMap<>();
-
- request.getResponse().get("users").getAsJsonArray().forEach(userJsonElement -> {
- final String uuid = userJsonElement.getAsJsonObject().get("uuid").getAsString();
- final String username = userJsonElement.getAsJsonObject().get("username").getAsString();
- final String active = userJsonElement.getAsJsonObject().get("active").getAsString();
- final String banned = userJsonElement.getAsJsonObject().get("banned").getAsString();
-
- if (!(
- uuid.equals("none") ||
- hideInactive && active.equals("0") ||
- hideBanned && banned.equals("1")
- )) {
- try {
- users.put(websiteUuidToJavaUuid(uuid), username);
- } catch (final StringIndexOutOfBoundsException e) {
- System.err.println("NamelessMC API - Skipped user with invalid UUID '" + uuid + "' (username: '" + username + "')");
- }
- }
- });
-
- return users;
- }
-
- public List getRegisteredUsersAsNamelessPlayerList(final boolean hideInactive, final boolean hideBanned) throws NamelessException {
- final Map users = this.getRegisteredUsers(hideInactive, hideBanned);
- final List namelessPlayers = new ArrayList<>();
-
- for (final UUID userUuid : users.keySet()) {
- namelessPlayers.add(this.getPlayer(userUuid));
- }
-
- return namelessPlayers;
- }
-
- public void setUserAgent(final String userAgent) {
- this.userAgent = userAgent;
- }
-
- /**
- *
- * @return Configured user agent or {@code null} if not configured.
- */
- public String getUserAgent() {
- return this.userAgent;
- }
-
- static String encode(final Object object) {
- try {
- return URLEncoder.encode(object.toString(), "UTF-8");
- } catch (final UnsupportedEncodingException e) {
- throw new RuntimeException(e.getMessage());
- }
- }
-
- static String[] jsonToArray(final JsonArray jsonArray) {
- final List list = new ArrayList<>();
- jsonArray.iterator().forEachRemaining((element) -> list.add(element.getAsString()));
- return list.toArray(new String[] {});
- }
-
- static UUID websiteUuidToJavaUuid(final String uuid) {
- // Add dashes to uuid
- // https://bukkit.org/threads/java-adding-dashes-back-to-minecrafts-uuids.272746/
- StringBuffer sb = new StringBuffer(uuid);
- sb.insert(8, "-");
-
- sb = new StringBuffer(sb.toString());
- sb.insert(13, "-");
-
- sb = new StringBuffer(sb.toString());
- sb.insert(18, "-");
-
- sb = new StringBuffer(sb.toString());
- sb.insert(23, "-");
-
- return UUID.fromString(sb.toString());
- }
-
-
-}
\ No newline at end of file
diff --git a/src/com/namelessmc/NamelessAPI/NamelessPlayer.java b/src/com/namelessmc/NamelessAPI/NamelessPlayer.java
deleted file mode 100644
index 450c35cb..00000000
--- a/src/com/namelessmc/NamelessAPI/NamelessPlayer.java
+++ /dev/null
@@ -1,281 +0,0 @@
-package com.namelessmc.NamelessAPI;
-
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
-import java.util.UUID;
-
-import com.google.gson.JsonObject;
-import com.namelessmc.NamelessAPI.Notification.NotificationType;
-import com.namelessmc.NamelessAPI.Request.Action;
-
-public final class NamelessPlayer {
-
- private String userName;
- private String displayName;
- private final UUID uuid;
- private int groupID;
- private int reputation;
- private Date registeredDate;
- private boolean exists;
- private boolean validated;
- private boolean banned;
- private String groupName;
-
- private final URL baseUrl;
- private final String userAgent;
-
- /**
- * Creates a new NamelessPlayer object. This constructor should not be called in the main server thread.
- * @param uuid
- * @param baseUrl Base API URL: http(s)://yoursite.com/api/v2/API_KEY
- * @throws NamelessException
- */
- NamelessPlayer(final UUID uuid, final URL baseUrl, final String userAgent) throws NamelessException {
- this.uuid = uuid;
- this.baseUrl = baseUrl;
- this.userAgent = userAgent;
-
- final Request request = new Request(baseUrl, userAgent, Action.USER_INFO, new ParameterBuilder().add("uuid", uuid).build());
- this.init(request);
- }
-
- private void init(final Request request) throws NamelessException {
- request.connect();
-
- if (request.hasError()) throw new ApiError(request.getError());
-
- final JsonObject response = request.getResponse();
-
- this.exists = response.get("exists").getAsBoolean();
-
- if (!this.exists)
- return;
-
- // Convert UNIX timestamp to date
- final Date registered = new Date(Long.parseLong(response.get("registered").toString().replaceAll("^\"|\"$", "")) * 1000);
-
- this.userName = response.get("username").getAsString();
- this.displayName = response.get("displayname").getAsString();
- //uuid = UUID.fromString(addDashesToUUID(response.get("uuid").getAsString()));
- this.groupName = response.get("group_name").getAsString();
- this.groupID = response.get("group_id").getAsInt();
- this.registeredDate = registered;
- this.validated = response.get("validated").getAsBoolean();
- //reputation = response.get("reputation").getAsInt();
- this.reputation = 0; // temp until reputation is added to API
- this.banned = response.get("banned").getAsBoolean();
- }
-
- /*public static String addDashesToUUID(String uuid) {
- // https://bukkit.org/threads/java-adding-dashes-back-to-minecrafts-uuids.272746/
- StringBuffer sb = new StringBuffer(uuid);
- sb.insert(8, "-");
-
- sb = new StringBuffer(sb.toString());
- sb.insert(13, "-");
-
- sb = new StringBuffer(sb.toString());
- sb.insert(18, "-");
-
- sb = new StringBuffer(sb.toString());
- sb.insert(23, "-");
-
- return sb.toString();
- }*/
-
- /**
- * @return The Minecraft username associated with the provided UUID. This is not always the name displayed on the website.
- * @see #getDisplayName()
- */
- public String getUsername() {
- if (!this.exists)
- throw new UnsupportedOperationException("This player does not exist.");
-
- return this.userName;
- }
-
- /**
- * @return The name this player uses on the website. This is not always the same as their Minecraft username.
- * @see #getUsername()
- */
- public String getDisplayName() {
- if (!this.exists)
- throw new UnsupportedOperationException("This player does not exist.");
-
- return this.displayName;
- }
-
- /**
- * @return Minecraft UUID of this player.
- * @see #getUsername()
- */
- public UUID getUniqueId() {
- return this.uuid;
- }
-
- /**
- * @return A numerical group id.
- */
- public int getGroupID() {
- if (!this.exists)
- throw new UnsupportedOperationException("This player does not exist.");
-
- return this.groupID;
- }
-
- /**
- * @return The user's primary group name
- */
- public String getGroupName() {
- if (!this.exists)
- throw new UnsupportedOperationException("This player does not exist.");
-
- return this.groupName;
- }
-
- /**
- * @return The user's site reputation.
- */
- public int getReputation() {
- if (!this.exists)
- throw new UnsupportedOperationException("This player does not exist.");
-
- return this.reputation;
- }
-
- /**
- * @return The date the user registered on the website.
- */
- public Date getRegisteredDate() {
- if (!this.exists)
- throw new UnsupportedOperationException("This player does not exist.");
-
- return this.registeredDate;
- }
-
- /**
- * @return Whether an account associated with the UUID exists.
- * @see #getUniqueId()
- */
- public boolean exists() {
- return this.exists;
- }
-
- /**
- * @return Whether this account has been validated. An account is validated when a password is set.
- */
- public boolean isValidated() {
- if (!this.exists)
- throw new UnsupportedOperationException("This player does not exist.");
-
- return this.validated;
- }
-
- /**
- * @return Whether this account is banned from the website.
- */
- public boolean isBanned() {
- if (!this.exists)
- throw new UnsupportedOperationException("This player does not exist.");
-
- return this.banned;
- }
-
- /**
- * @param code
- * @return True if the user could be validated successfully, false if the provided code is wrong
- * @throws NamelessException
- * @throws
- */
- public boolean validate(final String code) throws NamelessException {
- final String[] params = new ParameterBuilder()
- .add("uuid", this.uuid)
- .add("code", code).build();
- final Request request = new Request(this.baseUrl, this.userAgent, Action.VALIDATE_USER, params);
- request.connect();
-
- if (request.hasError()) {
- if (request.getError() == ApiError.INVALID_VALIDATE_CODE)
- return false;
- else
- throw new ApiError(request.getError());
- } else
- return true;
- }
-
- public List getNotifications() throws NamelessException {
- final Request request = new Request(this.baseUrl, this.userAgent, Action.GET_NOTIFICATIONS, new ParameterBuilder().add("uuid", this.uuid).build());
- request.connect();
-
- if (request.hasError()) throw new ApiError(request.getError());
-
- final List notifications = new ArrayList<>();
-
- final JsonObject object = request.getResponse();
- object.getAsJsonArray("notifications").forEach((element) -> {
- final String message = element.getAsJsonObject().get("message").getAsString();
- final String url = element.getAsJsonObject().get("url").getAsString();
- final NotificationType type = NotificationType.fromString(element.getAsJsonObject().get("type").getAsString());
- notifications.add(new Notification(message, url, type));
- });
-
- return notifications;
- }
-
- /**
- * Sets the players group
- * @param groupId Numerical ID associated with a group
- * @throws NamelessException
- */
- public void setGroup(final int groupId) throws NamelessException {
- final String[] parameters = new ParameterBuilder().add("uuid", this.uuid).add("group_id", groupId).build();
- final Request request = new Request(this.baseUrl, this.userAgent, Action.SET_GROUP, parameters);
- request.connect();
- if (request.hasError()) throw new ApiError(request.getError());
- }
-
- /**
- * Registers a new account. The player will be sent an email to set a password.
- * @param minecraftName In-game name for this player
- * @param email Email address
- * @return Email verification disabled: A link which the user needs to click to complete registration
- *
Email verification enabled: An empty string (the user needs to check their email to complete registration)
- * @throws NamelessException
- */
- public String register(final String minecraftName, final String email) throws NamelessException {
- final String[] parameters = new ParameterBuilder().add("username", minecraftName).add("uuid", this.uuid).add("email", email).build();
- final Request request = new Request(this.baseUrl, this.userAgent, Action.REGISTER, parameters);
- request.connect();
-
- if (request.hasError()) throw new ApiError(request.getError());
-
- final JsonObject response = request.getResponse();
-
- if (response.has("link"))
- return response.get("link").getAsString();
- else
- return "";
- }
-
- /**
- * Reports a player
- * @param reportedUuid UUID of the reported player
- * @param reportedUsername In-game name of the reported player
- * @param reason Reason why this player has been reported
- * @throws NamelessException
- */
- public void createReport(final UUID reportedUuid, final String reportedUsername, final String reason) throws NamelessException {
- final String[] parameters = new ParameterBuilder()
- .add("reporter_uuid", this.uuid)
- .add("reported_uuid", reportedUuid)
- .add("reported_username", reportedUsername)
- .add("content", reason)
- .build();
- final Request request = new Request(this.baseUrl, this.userAgent, Action.CREATE_REPORT, parameters);
- request.connect();
- if (request.hasError()) throw new ApiError(request.getError());
- }
-
-}
\ No newline at end of file
diff --git a/src/com/namelessmc/NamelessAPI/ParameterBuilder.java b/src/com/namelessmc/NamelessAPI/ParameterBuilder.java
deleted file mode 100644
index e06bbf19..00000000
--- a/src/com/namelessmc/NamelessAPI/ParameterBuilder.java
+++ /dev/null
@@ -1,41 +0,0 @@
-package com.namelessmc.NamelessAPI;
-
-import java.io.UnsupportedEncodingException;
-import java.net.URLEncoder;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-public class ParameterBuilder {
-
- private Map parameters;
-
- public ParameterBuilder() {
- parameters = new HashMap<>();
- }
-
- public ParameterBuilder add(String key, Object value) {
- parameters.put(key, value.toString());
- return this;
- }
-
- public String[] build() {
- List parameterStrings = new ArrayList<>();
-
- parameters.entrySet().forEach((entry) -> {
- parameterStrings.add(entry.getKey() + "=" + encode(entry.getValue()));
- });
-
- return parameterStrings.toArray(new String[] {});
- }
-
- private static String encode(Object object) {
- try {
- return URLEncoder.encode(object.toString(), "UTF-8");
- } catch (UnsupportedEncodingException e) {
- throw new RuntimeException(e.getMessage());
- }
- }
-
-}
diff --git a/src/com/namelessmc/NamelessAPI/Request.java b/src/com/namelessmc/NamelessAPI/Request.java
deleted file mode 100644
index f109065c..00000000
--- a/src/com/namelessmc/NamelessAPI/Request.java
+++ /dev/null
@@ -1,144 +0,0 @@
-package com.namelessmc.NamelessAPI;
-
-import static com.namelessmc.NamelessAPI.Request.RequestMethod.GET;
-import static com.namelessmc.NamelessAPI.Request.RequestMethod.POST;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
-import java.io.Reader;
-import java.net.HttpURLConnection;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.nio.charset.Charset;
-
-import com.google.gson.JsonObject;
-import com.google.gson.JsonParser;
-
-public class Request {
-
- private URL url;
- private final RequestMethod method;
- private final String parameters;
- private final Action action;
- private final String userAgent;
-
- private JsonObject response;
- private boolean hasError;
- private int errorCode = -1;
-
- public Request(final URL baseUrl, final String userAgent, final Action action, final String... parameters) {
- this.userAgent = userAgent;
- this.action = action;
- this.method = action.method;
- this.parameters = String.join("&", parameters);
-
- try {
- final String base = baseUrl.toString() + "/" + action.toString();
-
- if (action.method == RequestMethod.GET && parameters.length > 0) {
- this.url = new URL(base + "&" + this.parameters);
- } else {
- this.url = new URL(base);
- }
- } catch (final MalformedURLException e) {
- final IllegalArgumentException ex = new IllegalArgumentException("URL is malformed (" + e.getMessage() + ")");
- ex.initCause(e);
- throw ex;
- }
- }
-
- public boolean hasError() {
- return this.hasError;
- }
-
- public int getError() throws NamelessException {
- if (!this.hasError) {
- throw new NamelessException("Requested error code but there is no error.");
- }
-
- return this.errorCode;
- }
-
- public JsonObject getResponse() throws NamelessException {
- return this.response;
- }
-
- public void connect() throws NamelessException {
- if (NamelessAPI.DEBUG_MODE) {
- System.out.println(String.format("NamelessAPI > Making %s request (%s", this.action.toString(), this.method.toString()));
- System.out.println(String.format("NamelessAPI > URL: %s", this.url));
- System.out.println(String.format("NamelessAPI > Parameters: %s", this.parameters));
- }
-
- try {
- final HttpURLConnection connection = (HttpURLConnection) this.url.openConnection();
-
- connection.setRequestMethod(this.method.toString());
- connection.addRequestProperty("User-Agent", this.userAgent);
-
-
- if (this.method == RequestMethod.POST) {
- final byte[] encodedMessage = this.parameters.getBytes(Charset.forName("UTF-8"));
- connection.setRequestProperty("Content-Length", encodedMessage.length + "");
- connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
- connection.setDoOutput(true);
- try (OutputStream out = connection.getOutputStream()){
- out.write(encodedMessage);
- }
- }
-
- try (InputStream in = connection.getInputStream();
- Reader reader = new InputStreamReader(connection.getInputStream(), Charset.forName("UTF-8"))){
- JsonParser.parseReader(reader).getAsJsonObject();
- }
-
- connection.disconnect();
-
- this.hasError = this.response.get("error").getAsBoolean();
- if (this.hasError) {
- this.errorCode = this.response.get("code").getAsInt();
- }
- } catch (final IOException e) {
- throw new NamelessException(e);
- }
- }
-
- public enum Action {
-
- INFO("info", GET),
- GET_ANNOUNCEMENTS("getAnnouncements", GET),
- REGISTER("register", POST),
- USER_INFO("userInfo", GET),
- SET_GROUP("setGroup", POST),
- CREATE_REPORT("createReport", POST),
- GET_NOTIFICATIONS("getNotifications", GET),
- SERVER_INFO("serverInfo", POST),
- VALIDATE_USER("validateUser", POST),
- LIST_USERS("listUsers", GET),
-
- ;
-
- RequestMethod method;
- String name;
-
- Action(final String name, final RequestMethod method){
- this.name = name;
- this.method = method;
- }
-
- @Override
- public String toString() {
- return this.name;
- }
-
- }
-
- public enum RequestMethod {
-
- GET, POST
-
- }
-
-}
diff --git a/src/com/namelessmc/NamelessAPI/Announcement.java b/src/com/namelessmc/java_api/Announcement.java
similarity index 93%
rename from src/com/namelessmc/NamelessAPI/Announcement.java
rename to src/com/namelessmc/java_api/Announcement.java
index 92b00168..c64c8d8c 100644
--- a/src/com/namelessmc/NamelessAPI/Announcement.java
+++ b/src/com/namelessmc/java_api/Announcement.java
@@ -1,4 +1,4 @@
-package com.namelessmc.NamelessAPI;
+package com.namelessmc.java_api;
public class Announcement {
diff --git a/src/com/namelessmc/NamelessAPI/ApiError.java b/src/com/namelessmc/java_api/ApiError.java
similarity index 92%
rename from src/com/namelessmc/NamelessAPI/ApiError.java
rename to src/com/namelessmc/java_api/ApiError.java
index dd7bacaa..58c008ce 100644
--- a/src/com/namelessmc/NamelessAPI/ApiError.java
+++ b/src/com/namelessmc/java_api/ApiError.java
@@ -1,4 +1,4 @@
-package com.namelessmc.NamelessAPI;
+package com.namelessmc.java_api;
public class ApiError extends NamelessException {
@@ -34,15 +34,15 @@ public class ApiError extends NamelessException {
private static final long serialVersionUID = 3093028909912281912L;
- private int code;
+ private final int code;
- public ApiError(int code) {
+ public ApiError(final int code) {
super("An API error occured with error code " + code);
this.code = code;
}
- public int getErrorCode() {
- return code;
+ public int getError() {
+ return this.code;
}
}
diff --git a/src/com/namelessmc/java_api/Group.java b/src/com/namelessmc/java_api/Group.java
new file mode 100644
index 00000000..b3672700
--- /dev/null
+++ b/src/com/namelessmc/java_api/Group.java
@@ -0,0 +1,35 @@
+package com.namelessmc.java_api;
+
+import com.google.gson.JsonObject;
+
+public class Group {
+
+ private final int id;
+ private final String name;
+ private final boolean primary;
+
+ Group(final int id, final String name, final boolean primary) {
+ this.id = id;
+ this.name = name;
+ this.primary = primary;
+ }
+
+ Group(final JsonObject group) {
+ this.id = group.get("id").getAsInt();
+ this.name = group.get("name").getAsString();
+ this.primary = group.has("primary") ? group.get("primary").getAsBoolean() : false;
+ }
+
+ public int getId() {
+ return this.id;
+ }
+
+ public String getName() {
+ return this.name;
+ }
+
+ public boolean isPrimary() {
+ return this.primary;
+ }
+
+}
diff --git a/src/com/namelessmc/java_api/NamelessAPI.java b/src/com/namelessmc/java_api/NamelessAPI.java
new file mode 100644
index 00000000..47359f2e
--- /dev/null
+++ b/src/com/namelessmc/java_api/NamelessAPI.java
@@ -0,0 +1,320 @@
+package com.namelessmc.java_api;
+
+import java.io.UnsupportedEncodingException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLEncoder;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+import java.util.UUID;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.namelessmc.java_api.RequestHandler.Action;
+import com.namelessmc.java_api.Website.Update;
+
+public final class NamelessAPI {
+
+ private static final String DEFAULT_USER_AGENT = "Nameless-Java-API/beta"; // TODO include mavenversion string
+
+ private final RequestHandler requests;
+
+ public NamelessAPI(final URL apiUrl) {
+ this(apiUrl, DEFAULT_USER_AGENT);
+ }
+
+ /**
+ * @param apiUrl URL of API to connect to, in the format http(s)://yoursite.com/index.php?route=/api/v2/API_KEY
+ * @param debug
+ */
+ public NamelessAPI(final URL apiUrl, final boolean debug) {
+ this(apiUrl, DEFAULT_USER_AGENT, debug);
+ }
+
+ public NamelessAPI(final URL apiUrl, final String userAgent) {
+ this(apiUrl, userAgent, false);
+ }
+
+ /**
+ * @param host URL of your website, in the format http(s)://yoursite.com
+ * @param apiKey API key
+ * @param debug
+ * @throws MalformedURLException
+ */
+ public NamelessAPI(final String host, final String apiKey, final String userAgent, final boolean debug) throws MalformedURLException {
+ this(new URL(host + "/index.php?route=/api/v2/" + apiKey), userAgent, debug);
+ }
+
+ public NamelessAPI(final String host, final String apiKey, final boolean debug) throws MalformedURLException {
+ this(host, apiKey, DEFAULT_USER_AGENT, debug);
+ }
+
+ public NamelessAPI(final String host, final String apiKey, final String userAgent) throws MalformedURLException {
+ this(host, apiKey, userAgent, false);
+ }
+
+ public NamelessAPI(final String host, final String apiKey) throws MalformedURLException {
+ this(host, apiKey, DEFAULT_USER_AGENT);
+ }
+
+ public NamelessAPI(final URL apiUrl, final String userAgent, final boolean debug) {
+ this.requests = new RequestHandler(apiUrl, userAgent, debug);
+ }
+
+ RequestHandler getRequestHandler() {
+ return this.requests;
+ }
+
+ /**
+ * Checks if a web API connection can be established
+ * throws {@link NamelessException} if the connection was unsuccessful
+ */
+ public void checkWebAPIConnection() throws NamelessException {
+ final JsonObject response = this.requests.get(Action.INFO);
+ if (!response.has("nameless_version")) {
+ throw new NamelessException("Invalid respose: " + response.getAsString());
+ }
+ }
+
+ /**
+ * Get all announcements
+ * @return list of current announcements
+ * @throws NamelessException if there is an error in the request
+ */
+ public List getAnnouncements() throws NamelessException {
+ final JsonObject response = this.requests.get(Action.GET_ANNOUNCEMENTS);
+
+ final List announcements = new ArrayList<>();
+
+ response.getAsJsonArray().forEach((element) -> {
+ final JsonObject announcementJson = element.getAsJsonObject();
+ final String content = announcementJson.get("content").getAsString();
+ final String[] display = jsonToArray(announcementJson.get("display").getAsJsonArray());
+ final String[] permissions = jsonToArray(announcementJson.get("permissions").getAsJsonArray());
+ announcements.add(new Announcement(content, display, permissions));
+ });
+
+ return announcements;
+ }
+
+ /**
+ * Get all announcements visible for the player with the specified uuid
+ * @param uuid UUID of player to get visibile announcements for
+ * @return list of current announcements visible to the player
+ * @throws NamelessException if there is an error in the request
+ */
+ public List getAnnouncements(final NamelessUser user) throws NamelessException {
+ final JsonObject response = this.requests.get(Action.GET_ANNOUNCEMENTS, "id", user.getId());
+
+ final List announcements = new ArrayList<>();
+
+ response.get("announcements").getAsJsonArray().forEach((element) -> {
+ final JsonObject announcementJson = element.getAsJsonObject();
+ final String content = announcementJson.get("content").getAsString();
+ final String[] display = jsonToArray(announcementJson.get("display").getAsJsonArray());
+ final String[] permissions = jsonToArray(announcementJson.get("permissions").getAsJsonArray());
+ announcements.add(new Announcement(content, display, permissions));
+ });
+
+ return announcements;
+ }
+
+ public void submitServerInfo(final String jsonData) throws NamelessException {
+ this.requests.post(Action.SERVER_INFO, jsonData);
+ }
+
+ public Website getWebsite() throws NamelessException {
+ final JsonObject json = this.requests.get(Action.INFO);
+
+ final String version = json.get("nameless_version").getAsString();
+
+ final String[] modules = jsonToArray(json.get("modules").getAsJsonArray());
+
+ final JsonObject updateJson = json.get("version_update").getAsJsonObject();
+ final boolean updateAvailable = updateJson.get("update").getAsBoolean();
+ Update update;
+ if (updateAvailable) {
+ final String updateVersion = updateJson.get("version").getAsString();
+ final boolean isUrgent = updateJson.get("urgent").getAsBoolean();
+ update = new Update(isUrgent, updateVersion);
+ } else {
+ update = null;
+ }
+
+ return new Website(version, update, modules);
+
+ }
+
+ public List getRegisteredUsers(final UserFilter>... filters) throws NamelessException {
+ final List parameters = new ArrayList<>();
+ for (final UserFilter> filter : filters) {
+ parameters.add(filter.getName());
+ parameters.add(filter.getValue().toString());
+ }
+ final JsonObject response = this.requests.get(Action.LIST_USERS, parameters);
+ final JsonArray array = response.getAsJsonArray("users");
+ final List users = new ArrayList<>(array.size());
+ for (final JsonElement e : array) {
+ final JsonObject o = e.getAsJsonObject();
+ final int id = o.get("id").getAsInt();
+ final String username = o.get("username").getAsString();
+ Optional uuid;
+ if (o.has("uuid")) {
+ uuid = Optional.of(NamelessAPI.websiteUuidToJavaUuid(o.get("uuid").getAsString()));
+ } else {
+ uuid = Optional.empty();
+ }
+ users.add(new NamelessUser(this, id, username, uuid));
+ };
+ return Collections.unmodifiableList(users);
+ }
+
+ public Optional getUser(final int id) throws NamelessException {
+ final NamelessUser user = getUserLazy(id);
+ if (user.exists()) {
+ return Optional.of(user);
+ } else {
+ return Optional.empty();
+ }
+ }
+
+ public Optional getUser(final String username) throws NamelessException {
+ final NamelessUser user = getUserLazy(username);
+ if (user.exists()) {
+ return Optional.of(user);
+ } else {
+ return Optional.empty();
+ }
+ }
+
+ public Optional getUser(final UUID uuid) throws NamelessException {
+ final NamelessUser user = getUserLazy(uuid);
+ if (user.exists()) {
+ return Optional.of(user);
+ } else {
+ return Optional.empty();
+ }
+ }
+
+ public NamelessUser getUserLazy(final int id) throws NamelessException {
+ return new NamelessUser(this, id, null, null);
+ }
+
+ public NamelessUser getUserLazy(final String username) throws NamelessException {
+ return new NamelessUser(this, null, username, null);
+ }
+
+ public NamelessUser getUserLazy(final UUID uuid) throws NamelessException {
+ return new NamelessUser(this, null, null, Optional.of(uuid));
+ }
+
+ public NamelessUser getUserLazy(final String username, final UUID uuid) throws NamelessException {
+ return new NamelessUser(this, null, null, Optional.of(uuid));
+ }
+
+ public NamelessUser getUserLazy(final int id, final String username, final UUID uuid) throws NamelessException {
+ return new NamelessUser(this, id, username, Optional.of(uuid));
+ }
+
+ public Optional getUserByDiscordId(final long id) throws NamelessException {
+ try {
+ final JsonObject response = this.requests.get(Action.GET_USER_BY_DISCORD_ID, "discord_id", id);
+ return Optional.of(new NamelessUser(this, response.get("id").getAsInt(), null, null));
+ } catch (final ApiError e) {
+ if (e.getError() == ApiError.UNABLE_TO_FIND_USER) {
+ return Optional.empty();
+ } else {
+ throw e;
+ }
+ }
+ }
+
+ public void submitRankList(final List rankNames) {
+
+ }
+
+ public Optional getGroup(final int id) throws NamelessException {
+ final JsonObject response = this.requests.get(Action.GROUP_INFO, "id", id);
+ if (!response.has("group")) {
+ return Optional.empty();
+ } else {
+ return Optional.of(new Group(response.getAsJsonObject("group")));
+ }
+ }
+
+ public Optional getGroup(final String name) throws NamelessException {
+ final JsonObject response = this.requests.get(Action.GROUP_INFO, "name", name);
+ if (!response.has("group")) {
+ return Optional.empty();
+ } else {
+ return Optional.of(new Group(response.getAsJsonObject("group")));
+ }
+ }
+
+ /**
+ * Registers a new account. The user will be sent an email to set a password.
+ * @param username Username
+ * @param email Email address
+ * @return Email verification disabled: A link which the user needs to click to complete registration
+ *
Email verification enabled: An empty string (the user needs to check their email to complete registration)
+ * @throws NamelessException
+ */
+ public Optional registerUser(final String username, final String email, final UUID uuid) throws NamelessException {
+ final JsonObject post = new JsonObject();
+ post.addProperty("username", username);
+ post.addProperty("email", email);
+ if (uuid != null) {
+ post.addProperty("uuid", uuid.toString());
+ }
+
+ final JsonObject response = this.requests.post(Action.REGISTER, post.toString());
+
+ if (response.has("link")) {
+ return Optional.of(response.get("link").getAsString());
+ } else {
+ return Optional.empty();
+ }
+ }
+
+ public Optional registerUser(final String username, final String email) throws NamelessException {
+ return registerUser(username, email, null);
+ }
+
+ static String encode(final Object object) {
+ try {
+ return URLEncoder.encode(object.toString(), "UTF-8");
+ } catch (final UnsupportedEncodingException e) {
+ throw new RuntimeException(e.getMessage());
+ }
+ }
+
+ @Deprecated
+ static String[] jsonToArray(final JsonArray jsonArray) {
+ final List list = new ArrayList<>();
+ jsonArray.iterator().forEachRemaining((element) -> list.add(element.getAsString()));
+ return list.toArray(new String[] {});
+ }
+
+ static UUID websiteUuidToJavaUuid(final String uuid) {
+ // Add dashes to uuid
+ // https://bukkit.org/threads/java-adding-dashes-back-to-minecrafts-uuids.272746/
+ StringBuffer sb = new StringBuffer(uuid);
+ sb.insert(8, "-");
+
+ sb = new StringBuffer(sb.toString());
+ sb.insert(13, "-");
+
+ sb = new StringBuffer(sb.toString());
+ sb.insert(18, "-");
+
+ sb = new StringBuffer(sb.toString());
+ sb.insert(23, "-");
+
+ return UUID.fromString(sb.toString());
+ }
+
+
+}
\ No newline at end of file
diff --git a/src/com/namelessmc/NamelessAPI/NamelessException.java b/src/com/namelessmc/java_api/NamelessException.java
similarity index 93%
rename from src/com/namelessmc/NamelessAPI/NamelessException.java
rename to src/com/namelessmc/java_api/NamelessException.java
index c94becb5..d0a4e778 100644
--- a/src/com/namelessmc/NamelessAPI/NamelessException.java
+++ b/src/com/namelessmc/java_api/NamelessException.java
@@ -1,4 +1,4 @@
-package com.namelessmc.NamelessAPI;
+package com.namelessmc.java_api;
/**
* Generic exception thrown by many methods in the Nameless API
diff --git a/src/com/namelessmc/java_api/NamelessUser.java b/src/com/namelessmc/java_api/NamelessUser.java
new file mode 100644
index 00000000..0c8d5a4e
--- /dev/null
+++ b/src/com/namelessmc/java_api/NamelessUser.java
@@ -0,0 +1,311 @@
+package com.namelessmc.java_api;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.Optional;
+import java.util.UUID;
+import java.util.stream.StreamSupport;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.namelessmc.java_api.Notification.NotificationType;
+import com.namelessmc.java_api.RequestHandler.Action;
+
+public final class NamelessUser {
+
+ private final NamelessAPI api;
+ private final RequestHandler requests;
+
+ private Integer id;
+ private String username;
+ private Optional uuid;
+
+ private JsonObject userInfo;
+
+ // only one of id, username, uuid has to be provided
+ NamelessUser(final NamelessAPI api, final Integer id, final String username, final Optional uuid) throws NamelessException {
+ this.api = api;
+ this.requests = api.getRequestHandler();
+
+ if (id == null && username == null && uuid == null) {
+ throw new IllegalArgumentException("You must specify at least one of ID, uuid, username");
+ }
+
+ this.id = id;
+ this.username = username;
+ this.uuid = uuid;
+ }
+
+ private void loadUserInfo() throws NamelessException {
+ final JsonObject response;
+ if (this.id != null) {
+ response = this.requests.get(Action.USER_INFO, "id", this.id);
+ } else if (this.uuid != null && this.uuid.isPresent()) {
+ response = this.requests.get(Action.USER_INFO, "uuid", this.uuid.get());
+ } else if (this.username != null) {
+ response = this.requests.get(Action.USER_INFO, "username", this.username);
+ } else {
+ throw new IllegalStateException("ID, uuid, and username not known for this player.");
+ }
+
+ if (!response.get("exists").getAsBoolean()) {
+ throw new UserNotExistException();
+ }
+
+ this.userInfo = response;
+ }
+
+ public NamelessAPI getApi() {
+ return this.api;
+ }
+
+ public int getId() throws NamelessException {
+ if (this.id == null) {
+ this.loadUserInfo();
+ this.id = this.userInfo.get("id").getAsInt();
+ }
+
+ return this.id;
+ }
+
+ public String getUsername() throws NamelessException {
+ if (this.username == null) {
+ this.loadUserInfo();
+ this.username = this.userInfo.get("username").getAsString();
+ }
+
+ return this.username;
+ }
+
+ public Optional getUniqueId() throws NamelessException {
+ if (this.uuid == null) {
+ this.loadUserInfo();
+ if (this.userInfo.has("uuid")) {
+ this.uuid = Optional.of(NamelessAPI.websiteUuidToJavaUuid(this.userInfo.get("uuid").getAsString()));
+ } else {
+ this.uuid = Optional.empty();
+ }
+ }
+
+ return this.uuid;
+ }
+
+ public boolean exists() throws NamelessException {
+ if (this.userInfo == null) {
+ try {
+ loadUserInfo();
+ } catch (final UserNotExistException e) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public String getDisplayName() throws NamelessException {
+ if (this.userInfo == null) {
+ this.loadUserInfo();
+ }
+
+ return this.userInfo.get("displayname").getAsString();
+ }
+
+
+ /**
+ * @return The date the user registered on the website.
+ * @throws NamelessException
+ */
+ public Date getRegisteredDate() throws NamelessException {
+ if (this.userInfo == null) {
+ this.loadUserInfo();
+ }
+
+ return new Date(Long.parseLong(this.userInfo.get("registered").toString().replaceAll("^\"|\"$", "")) * 1000);
+ }
+
+ /**
+ * @return Whether this account is banned from the website.
+ * @throws NamelessException
+ */
+ public boolean isBanned() throws NamelessException {
+ if (this.userInfo == null) {
+ this.loadUserInfo();
+ }
+
+ return this.userInfo.get("banned").getAsBoolean();
+ }
+
+ public boolean isVerified() throws NamelessException {
+ if (this.userInfo == null) {
+ this.loadUserInfo();
+ }
+
+ return this.userInfo.get("verified").getAsBoolean();
+ }
+
+ public VerificationInfo getVerificationInfo() throws NamelessException {
+ final boolean verified = isVerified();
+ final JsonObject verification = this.userInfo.getAsJsonObject("verification");
+ return new VerificationInfo(verified, verification);
+ }
+
+ public Group getPrimaryGroup() throws NamelessException {
+ if (this.userInfo == null) {
+ this.loadUserInfo();
+ }
+
+ final JsonObject groups = this.userInfo.getAsJsonObject("groups");
+ final JsonObject primary = groups.getAsJsonObject("primary");
+ return new Group(primary.get("id").getAsInt(), primary.get("name").getAsString(), true);
+ }
+
+ public List getSecondaryGroups() throws NamelessException {
+ if (this.userInfo == null) {
+ this.loadUserInfo();
+ }
+
+ final JsonObject groups = this.userInfo.getAsJsonObject("groups");
+ final List secondaryGroups = new ArrayList<>();
+ groups.getAsJsonArray("secondary").forEach(e -> {
+ final JsonObject group = e.getAsJsonObject();
+ secondaryGroups.add(new Group(group.get("id").getAsInt(), group.get("name").getAsString(), false));
+ });
+ return Collections.unmodifiableList(secondaryGroups);
+ }
+
+ public List getGroups() throws NamelessException {
+ final List secondaryGroups = this.getSecondaryGroups();
+ final List list = new ArrayList<>(secondaryGroups.size() + 1);
+ list.add(this.getPrimaryGroup());
+ list.addAll(this.getSecondaryGroups());
+ return Collections.unmodifiableList(list);
+ }
+
+ public void setPrimaryGroup(final Group group) throws NamelessException {
+ final JsonObject post = new JsonObject();
+ post.addProperty("id", this.id);
+ post.addProperty("group", group.getId());
+ this.requests.post(Action.SET_PRIMARY_GROUP, post.toString());
+ }
+
+ public void addSecondaryGroups(final Group... groups) throws NamelessException {
+ final JsonObject post = new JsonObject();
+ post.addProperty("id", this.id);
+ post.add("groups", new Gson().toJsonTree(Arrays.stream(groups).mapToInt(Group::getId).toArray()));
+ this.requests.post(Action.ADD_SECONDARY_GROUPS, post.toString());
+ }
+
+ public void removeSecondaryGroups(final Group... groups) throws NamelessException {
+ final JsonObject post = new JsonObject();
+ post.addProperty("id", this.id);
+ post.add("groups", new Gson().toJsonTree(Arrays.stream(groups).mapToInt(Group::getId).toArray()));
+ this.requests.post(Action.REMOVE_SECONDARY_GROUPS, post.toString());
+ }
+
+ public int getNotificationCount() throws NamelessException {
+ return getNotifications().size(); // TODO more efficient method
+ }
+
+ public List getNotifications() throws NamelessException {
+ final JsonObject response = this.requests.get(Action.GET_NOTIFICATIONS, "id", this.id);
+
+ final List notifications = new ArrayList<>();
+ response.getAsJsonArray("notifications").forEach((element) -> {
+ final String message = element.getAsJsonObject().get("message").getAsString();
+ final String url = element.getAsJsonObject().get("url").getAsString();
+ final NotificationType type = NotificationType.fromString(element.getAsJsonObject().get("type").getAsString());
+ notifications.add(new Notification(message, url, type));
+ });
+
+ return notifications;
+ }
+
+ /**
+ * Reports a player
+ * @param username Username of the player or user to report
+ * @param reason Reason why this player has been reported
+ * @throws NamelessException
+ */
+ public void createReport(final String username, final String reason) throws NamelessException {
+ final JsonObject post = new JsonObject();
+ post.addProperty("reporter", this.id);
+ post.addProperty("reported", username);
+ post.addProperty("content", reason);
+ this.requests.post(Action.CREATE_REPORT, post.toString());
+ }
+
+ /**
+ * @param code
+ * @return True if the user could be validated successfully, false if the provided code is wrong
+ * @throws NamelessException
+ * @throws
+ */
+ public boolean verifyMinecraft(final String code) throws NamelessException {
+ final JsonObject post = new JsonObject();
+ post.addProperty("id", this.id);
+ post.addProperty("code", code);
+ try {
+ this.requests.post(Action.VERIFY_MINECRAFT, post.toString());
+ return true;
+ } catch (final ApiError e) {
+ if (e.getError() == ApiError.INVALID_VALIDATE_CODE) {
+ return false;
+ } else {
+ throw e;
+ }
+ }
+ }
+
+ public void verifyDiscord(final String verificationToken) throws NamelessException {
+ this.requests.post(Action.VERIFY_DISCORD, verificationToken);
+ }
+
+ public Optional getDiscordId() throws NamelessException {
+ final JsonObject response = this.requests.get(Action.GET_DISCORD_ID, "id", this.id);
+ if (response.has("discord_id")) {
+ return Optional.of(response.get("discord_id").getAsInt());
+ } else {
+ return Optional.empty();
+ }
+ }
+
+ public long[] getDiscordRoles() throws NamelessException {
+ final JsonObject response = this.requests.get(Action.GET_DISCORD_ROLES, "id", this.id);
+ return StreamSupport.stream(response.getAsJsonArray("roles").spliterator(), false)
+ .mapToLong(JsonElement::getAsLong).toArray();
+ }
+
+ public void setDiscordRoles(final long[] roleIds) throws NamelessException {
+ final JsonObject post = new JsonObject();
+ post.addProperty("id", this.id);
+ post.add("roles", new Gson().toJsonTree(roleIds));
+ this.requests.post(Action.SET_DISCORD_ROLES, post.toString());
+ }
+
+ public void addDiscordRole(final long roleId) throws NamelessException {
+ addDiscordRoles(new long[] {roleId});
+ }
+
+ public void addDiscordRoles(final long[] roleIds) throws NamelessException {
+ final JsonObject post = new JsonObject();
+ post.addProperty("id", this.id);
+ post.add("roles", new Gson().toJsonTree(roleIds));
+ this.requests.post(Action.ADD_DISCORD_ROLES, post.toString());
+ }
+
+ public void removeDiscordRole(final long roleId) throws NamelessException {
+ removeDiscordRole(new long[] {roleId});
+ }
+
+ public void removeDiscordRole(final long[] roleIds) throws NamelessException {
+ final JsonObject post = new JsonObject();
+ post.addProperty("id", this.id);
+ post.add("roles", new Gson().toJsonTree(roleIds));
+ this.requests.post(Action.REMOVE_DISCORD_ROLES, post.toString());
+ }
+
+}
\ No newline at end of file
diff --git a/src/com/namelessmc/NamelessAPI/Notification.java b/src/com/namelessmc/java_api/Notification.java
similarity index 95%
rename from src/com/namelessmc/NamelessAPI/Notification.java
rename to src/com/namelessmc/java_api/Notification.java
index 96f75945..2ecd44cc 100644
--- a/src/com/namelessmc/NamelessAPI/Notification.java
+++ b/src/com/namelessmc/java_api/Notification.java
@@ -1,4 +1,4 @@
-package com.namelessmc.NamelessAPI;
+package com.namelessmc.java_api;
public class Notification {
diff --git a/src/com/namelessmc/java_api/RequestHandler.java b/src/com/namelessmc/java_api/RequestHandler.java
new file mode 100644
index 00000000..4a4a1deb
--- /dev/null
+++ b/src/com/namelessmc/java_api/RequestHandler.java
@@ -0,0 +1,176 @@
+package com.namelessmc.java_api;
+
+import static com.namelessmc.java_api.RequestHandler.RequestMethod.GET;
+import static com.namelessmc.java_api.RequestHandler.RequestMethod.POST;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+
+public class RequestHandler {
+
+ private final URL baseUrl;
+ private final String userAgent;
+
+ RequestHandler(final URL baseUrl, final String userAgent, final boolean debug) {
+ this.baseUrl = baseUrl;
+ this.userAgent = userAgent;
+ }
+
+ public JsonObject post(final Action action, final String postData) throws NamelessException {
+ if (action.method != RequestMethod.POST) {
+ throw new IllegalArgumentException("Cannot POST to a GET API method");
+ }
+
+ URL url;
+ try {
+ url = new URL(this.baseUrl.toString() + "/" + action);
+ } catch (final MalformedURLException e) {
+ throw new NamelessException("Invalid URL or parameter string");
+ }
+
+ try {
+ return makeConnection(url, postData);
+ } catch (final IOException e) {
+ throw new NamelessException(e);
+ }
+ }
+
+ public JsonObject get(final Action action, final Object... parameters) throws NamelessException {
+ if (action.method != RequestMethod.GET) {
+ throw new IllegalArgumentException("Cannot GET a POST API method");
+ }
+
+ final StringBuilder urlBuilder = new StringBuilder(this.baseUrl.toString());
+ urlBuilder.append("/");
+ urlBuilder.append(action);
+
+ if (parameters.length > 0) {
+ if (parameters.length % 2 != 0) {
+ throw new IllegalArgumentException("Parameter string varargs array length must be even");
+ }
+
+ for (int i = 0; i < parameters.length; i++) {
+ if (i % 2 == 0) {
+ urlBuilder.append("&");
+ urlBuilder.append(parameters[i]);
+ } else {
+ urlBuilder.append("=");
+ urlBuilder.append(URLEncoder.encode(parameters[i].toString(), StandardCharsets.UTF_8));
+ }
+ }
+ }
+
+ URL url;
+ try {
+ url = new URL(urlBuilder.toString());
+ } catch (final MalformedURLException e) {
+ throw new NamelessException("Error while building request URL: " + urlBuilder);
+ }
+
+ try {
+ return makeConnection(url, null);
+ } catch (final IOException e) {
+ throw new NamelessException(e);
+ }
+ }
+
+ private JsonObject makeConnection(final URL url, final String postBody) throws NamelessException, IOException {
+ final HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+
+ connection.addRequestProperty("User-Agent", this.userAgent);
+
+ if (postBody != null) {
+ connection.setRequestMethod("POST");
+ final String contentType = postBody.startsWith("[") || postBody.startsWith("{") ? "application/json" : "text/plain";
+ final byte[] encodedMessage = postBody.getBytes(StandardCharsets.UTF_8);
+ connection.setRequestProperty("Content-Length", encodedMessage.length + "");
+ connection.setRequestProperty("Content-Type", contentType);
+ connection.setDoOutput(true);
+ try (OutputStream out = connection.getOutputStream()){
+ out.write(encodedMessage);
+ }
+ }
+
+ JsonObject response;
+
+ try (InputStream in = connection.getInputStream();
+ Reader reader = new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8)){
+ response = JsonParser.parseReader(reader).getAsJsonObject();
+ }
+
+ connection.disconnect();
+
+ if (!response.has("error")) {
+ throw new NamelessException("Unexpected response from website (missing json key 'error')");
+ }
+
+ if (response.get("error").getAsBoolean()) {
+ throw new ApiError(response.get("code").getAsInt());
+ }
+
+ return response;
+ }
+
+ public enum Action {
+
+ INFO("info", GET),
+ GET_ANNOUNCEMENTS("getAnnouncements", GET),
+ REGISTER("register", POST),
+ USER_INFO("userInfo", GET),
+ LIST_GROUPS("listGroups", GET),
+ GROUP_INFO("groupInfo", GET),
+ SET_PRIMARY_GROUP("setPrimaryGroup", POST),
+ ADD_SECONDARY_GROUPS("addSecondaryGroups", POST),
+ REMOVE_SECONDARY_GROUPS("setSecondaryGroups", POST),
+ SET_GROUP("setGroup", POST),
+ CREATE_REPORT("createReport", POST),
+ GET_NOTIFICATIONS("getNotifications", GET),
+ SERVER_INFO("serverInfo", POST),
+ UPDATE_USERNAME("updateUsername", POST),
+ VERIFY_MINECRAFT("verifyMinecraft", POST),
+ LIST_USERS("listUsers", GET),
+ INGAME_RANKS("ingameRanks", POST),
+ VERIFY_DISCORD("verifyDiscord", POST),
+ GET_DISCORD_ID("getDiscordId", GET),
+ GET_DISCORD_ROLES("getDiscordRoles", GET),
+ GET_USER_BY_DISCORD_ID("getUserByDiscordId", GET),
+ SET_DISCORD_ROLES("setDiscordRoles", POST),
+ ADD_DISCORD_ROLES("addDiscordRoles", POST),
+ REMOVE_DISCORD_ROLES("removeDiscordRoles", POST),
+
+ ;
+
+ RequestMethod method;
+ String name;
+
+ Action(final String name, final RequestMethod post){
+ this.name = name;
+ this.method = post;
+ }
+
+ @Override
+ public String toString() {
+ return this.name;
+ }
+
+ }
+
+
+ public enum RequestMethod {
+
+ GET, POST
+
+ }
+
+}
diff --git a/src/com/namelessmc/java_api/UserFilter.java b/src/com/namelessmc/java_api/UserFilter.java
new file mode 100644
index 00000000..1245540a
--- /dev/null
+++ b/src/com/namelessmc/java_api/UserFilter.java
@@ -0,0 +1,30 @@
+package com.namelessmc.java_api;
+
+public class UserFilter {
+
+ public static UserFilter BANNED = new UserFilter<>("banned", true);
+ public static UserFilter UNBANNED = new UserFilter<>("banned", true);
+ public static UserFilter VERIFIED = new UserFilter<>("verified", false);
+ public static UserFilter UNVERIFIED = new UserFilter<>("verified", false);
+
+ private final String filterName;
+ private FilterValue value;
+
+ public UserFilter(final String filterName, final FilterValue defaultValue) {
+ this.filterName = filterName;
+ this.value = defaultValue;
+ }
+
+ public void value(final FilterValue value) {
+ this.value = value;
+ }
+
+ public String getName() {
+ return this.filterName;
+ }
+
+ public FilterValue getValue() {
+ return this.value;
+ }
+
+}
diff --git a/src/com/namelessmc/java_api/UserNotExistException.java b/src/com/namelessmc/java_api/UserNotExistException.java
new file mode 100644
index 00000000..88ce6ed5
--- /dev/null
+++ b/src/com/namelessmc/java_api/UserNotExistException.java
@@ -0,0 +1,7 @@
+package com.namelessmc.java_api;
+
+public class UserNotExistException extends NamelessException {
+
+ private static final long serialVersionUID = 1L;
+
+}
diff --git a/src/com/namelessmc/java_api/VerificationInfo.java b/src/com/namelessmc/java_api/VerificationInfo.java
new file mode 100644
index 00000000..dbfaaaaf
--- /dev/null
+++ b/src/com/namelessmc/java_api/VerificationInfo.java
@@ -0,0 +1,41 @@
+package com.namelessmc.java_api;
+
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+
+public class VerificationInfo {
+
+ private final boolean verified;
+ private final JsonObject json;
+
+ VerificationInfo(final boolean verified, final JsonObject json) {
+ this.verified = verified;
+ this.json = json;
+ }
+
+ public boolean isVerified() {
+ return this.verified;
+ }
+
+ public boolean isVerifiedCustom(final String name) {
+ final JsonElement e = this.json.get(name);
+ if (e == null) {
+ throw new UnsupportedOperationException("The API did not return verification for '" + name + "'");
+ } else {
+ return e.getAsBoolean();
+ }
+ }
+
+ public boolean isVerifiedEmail() {
+ return isVerifiedCustom("email");
+ }
+
+ public boolean isVerifiedMinecraft() {
+ return isVerifiedCustom("minecraft");
+ }
+
+ public boolean isVerifiedDiscord() {
+ return isVerifiedCustom("discord");
+ }
+
+}
diff --git a/src/com/namelessmc/NamelessAPI/Website.java b/src/com/namelessmc/java_api/Website.java
similarity index 95%
rename from src/com/namelessmc/NamelessAPI/Website.java
rename to src/com/namelessmc/java_api/Website.java
index 8f80ed54..6f4f4f76 100644
--- a/src/com/namelessmc/NamelessAPI/Website.java
+++ b/src/com/namelessmc/java_api/Website.java
@@ -1,4 +1,4 @@
-package com.namelessmc.NamelessAPI;
+package com.namelessmc.java_api;
public class Website {