diff --git a/LICENSE b/LICENSE
index b62c9918..0a041280 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,21 +1,165 @@
-MIT License
-
-Copyright (c) 2017-2020 NamelessMC
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+ This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+ 0. Additional Definitions.
+
+ As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+ "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+ An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+ A "Combined Work" is a work produced by combining or linking an
+Application with the Library. The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+ The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+ The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+ 1. Exception to Section 3 of the GNU GPL.
+
+ You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+ 2. Conveying Modified Versions.
+
+ If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+ a) under this License, provided that you make a good faith effort to
+ ensure that, in the event an Application does not supply the
+ function or data, the facility still operates, and performs
+ whatever part of its purpose remains meaningful, or
+
+ b) under the GNU GPL, with none of the additional permissions of
+ this License applicable to that copy.
+
+ 3. Object Code Incorporating Material from Library Header Files.
+
+ The object code form of an Application may incorporate material from
+a header file that is part of the Library. You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+ a) Give prominent notice with each copy of the object code that the
+ Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the object code with a copy of the GNU GPL and this license
+ document.
+
+ 4. Combined Works.
+
+ You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+ a) Give prominent notice with each copy of the Combined Work that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
+ document.
+
+ c) For a Combined Work that displays copyright notices during
+ execution, include the copyright notice for the Library among
+ these notices, as well as a reference directing the user to the
+ copies of the GNU GPL and this license document.
+
+ d) Do one of the following:
+
+ 0) Convey the Minimal Corresponding Source under the terms of this
+ License, and the Corresponding Application Code in a form
+ suitable for, and under terms that permit, the user to
+ recombine or relink the Application with a modified version of
+ the Linked Version to produce a modified Combined Work, in the
+ manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.
+
+ 1) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (a) uses at run time
+ a copy of the Library already present on the user's computer
+ system, and (b) will operate properly with a modified version
+ of the Library that is interface-compatible with the Linked
+ Version.
+
+ e) Provide Installation Information, but only if you would otherwise
+ be required to provide such information under section 6 of the
+ GNU GPL, and only to the extent that such information is
+ necessary to install and execute a modified version of the
+ Combined Work produced by recombining or relinking the
+ Application with a modified version of the Linked Version. (If
+ you use option 4d0, the Installation Information must accompany
+ the Minimal Corresponding Source and Corresponding Application
+ Code. If you use option 4d1, you must provide the Installation
+ Information in the manner specified by section 6 of the GNU GPL
+ for conveying Corresponding Source.)
+
+ 5. Combined Libraries.
+
+ You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+ a) Accompany the combined library with a copy of the same work based
+ on the Library, uncombined with any other library facilities,
+ conveyed under the terms of this License.
+
+ b) Give prominent notice with the combined library that part of it
+ is a work based on the Library, and explaining where to find the
+ accompanying uncombined form of the same work.
+
+ 6. Revised Versions of the GNU Lesser General Public License.
+
+ The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+ If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
diff --git a/README.md b/README.md
index af909a6a..8de14d31 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,35 @@
# Nameless-Java-API
-Java library for interacting with a NamelessMC website. It is used by for example the NamelessMC Spigot plugin and Nameless-Link Discord bot.
+
+Java library for interacting with a NamelessMC website. It is used by for example the NamelessMC Minecraft server plugin and Nameless-Link Discord bot.
+
+## Install to local maven repository
+
+We don't publish builds to maven central (yet). You will need to build and install this project locally.
+
+OpenJDK 11, git and maven should be installed. Run in a terminal:
+
+```
+git clone https://github.com/NamelessMC/Nameless-Java-API
+cd Nameless-Java-API
+mvn install
+```
+
+## Include as dependency
+
+```xml
+
+ com.namelessmc
+ java-api
+ canary
+
+```
+
+## Usage
+
+Create an API instance:
+
+```java
+String apiUrl = "";
+String apiKey = "";
+NamelessAPI api = NamelessAPI.builder(apiUrl, apiKey).build();
+```
diff --git a/pom.xml b/pom.xml
index c61578d5..4ecbe73c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -11,30 +11,51 @@
UTF-8
+ 3.53.1
- src
-
org.apache.maven.pluginsmaven-compiler-plugin
- 3.10.1
+ 3.15.01111
+ true
+ true
+
+
+ org.checkerframework
+ checker
+ ${checkerFrameworkVersion}
+
+
+
+ org.checkerframework.checker.nullness.NullnessChecker
+ org.checkerframework.checker.optional.OptionalChecker
+ org.checkerframework.checker.regex.RegexChecker
+ org.checkerframework.checker.formatter.FormatterChecker
+
+
+ -Awarns
+ -J--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED
+ -J--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED
+ -J--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED
+ -J--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED
+ -J--add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED
+ -J--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED
+ -J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED
+ -J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED
+ -J--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED
+ -J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED
+
-
- org.apache.maven.plugins
- maven-shade-plugin
- 3.2.4
-
- false
- true
-
+ maven-surefire-plugin
+ 3.5.4
@@ -44,26 +65,39 @@
com.google.code.gsongson
- 2.9.0
+ 2.13.2com.google.guavaguava
- 21.0
+ 23.0org.slf4jslf4j-api
- 2.0.0-alpha7
+ 2.0.17provided
- org.jetbrains
- annotations
- 23.0.0
+ org.checkerframework
+ checker-qual
+ ${checkerFrameworkVersion}
+
+
+
+ com.github.mizosoft.methanol
+ methanol
+ 1.9.0
+
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ 6.0.3
+ test
diff --git a/src/com/namelessmc/java_api/ApiError.java b/src/com/namelessmc/java_api/ApiError.java
deleted file mode 100644
index 815416d3..00000000
--- a/src/com/namelessmc/java_api/ApiError.java
+++ /dev/null
@@ -1,69 +0,0 @@
-package com.namelessmc.java_api;
-
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.util.Optional;
-
-public class ApiError extends NamelessException {
-
- public static final int UNKNOWN_ERROR = 0;
- public static final int INVALID_API_KEY = 1;
- // 2 intentionally missing
- public static final int INVALID_API_METHOD = 3;
- public static final int NO_UNIQUE_SITE_ID_AVAILABLE = 4;
- // 5 intentionally missing
- public static final int INVALID_GET_POST_CONTENTS = 6;
- public static final int INVALID_EMAIL_ADDRESS = 7;
- public static final int INVALID_USERNAME = 8;
- public static final int INVALID_UUID = 9;
- public static final int EMAIL_ALREADY_EXISTS = 10;
- public static final int USERNAME_ALREADY_EXISTS = 11;
- public static final int UUID_ALREADY_EXISTS = 12;
- public static final int UNABLE_TO_CREATE_ACCOUNT = 13;
- public static final int UNABLE_TO_SEND_REGISTRATION_EMAIL = 14;
- // 15 intentionally missing
- public static final int UNABLE_TO_FIND_USER = 16;
- public static final int UNABLE_TO_FIND_GROUP = 17;
- // 18 intentionally missing
- public static final int REPORT_CONTENT_TOO_LARGE = 19;
- // 20 intentionally missing
- public static final int USER_CREATING_REPORT_BANNED = 21;
- public static final int USER_ALREADY_HAS_OPEN_REPORT = 22;
- // 23 intentionally missing
- public static final int UNABLE_TO_UPDATE_USERNAME = 24;
- public static final int UNABLE_TO_UPDATE_SERVER_INFO = 25;
- public static final int CANNOT_REPORT_YOURSELF = 26;
- public static final int INVALID_SERVER_ID = 27;
- public static final int INVALID_VALIDATE_CODE = 28;
- public static final int UNABLE_TO_SET_USER_DISCORD_ID = 29;
- public static final int UNABLE_TO_SET_DISCORD_BOT_URL = 30;
- // 31 intentionally missing
- public static final int ACCOUNT_ALREADY_ACTIVATED = 32;
- public static final int UNABLE_TO_SET_DISCORD_GUILD_ID = 33;
- public static final int DISCORD_INTEGRATION_DISABLED = 34;
- // 35 intentionally missing
- public static final int REQUEST_NOT_AUTHORIZED = 36;
- public static final int INVALID_INTEGRATION = 37;
- // 38 intentionally missing
-
- private static final long serialVersionUID = 3093028909912281912L;
-
- private final int code;
- private final @Nullable String meta;
-
- public ApiError(final int code, @Nullable String meta) {
- super("An unexpected API error occurred with error code " + code + " and " + (meta == null ? "no meta" : "meta " + meta));
- this.code = code;
- this.meta = meta;
- }
-
- public int getError() {
- return this.code;
- }
-
- public @NotNull Optional<@NotNull String> getMeta() {
- return Optional.ofNullable(meta);
- }
-
-}
diff --git a/src/com/namelessmc/java_api/CustomProfileFieldValue.java b/src/com/namelessmc/java_api/CustomProfileFieldValue.java
deleted file mode 100644
index ee25851b..00000000
--- a/src/com/namelessmc/java_api/CustomProfileFieldValue.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package com.namelessmc.java_api;
-
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-public class CustomProfileFieldValue {
-
- private final @NotNull CustomProfileField field;
- private final @Nullable String value;
-
- CustomProfileFieldValue(@NotNull CustomProfileField field, @Nullable String value) {
- this.field = field;
- this.value = value;
- }
-
- public @NotNull CustomProfileField getField() {
- return this.field;
- }
-
- public @Nullable String getValue() {
- return value;
- }
-
-}
diff --git a/src/com/namelessmc/java_api/FilteredUserListBuilder.java b/src/com/namelessmc/java_api/FilteredUserListBuilder.java
deleted file mode 100644
index 8ea3160d..00000000
--- a/src/com/namelessmc/java_api/FilteredUserListBuilder.java
+++ /dev/null
@@ -1,87 +0,0 @@
-package com.namelessmc.java_api;
-
-import com.google.gson.JsonArray;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonObject;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.UUID;
-
-public class FilteredUserListBuilder {
-
- private final @NotNull NamelessAPI api;
- private @Nullable Map, Object> filters;
- private @NotNull String operator = "AND";
-
- FilteredUserListBuilder(@NotNull NamelessAPI api) {
- this.api = api;
- }
-
- public FilteredUserListBuilder withFilter(UserFilter filter, T value) {
- if (filters == null) {
- filters = new HashMap<>();
- }
-
- filters.put(filter, value);
- return this;
- }
-
- public FilteredUserListBuilder all() {
- this.operator = "AND";
- return this;
- }
-
- public FilteredUserListBuilder any() {
- this.operator = "OR";
- return this;
- }
-
- public List makeRequest() throws NamelessException {
- final Object[] parameters;
- if (filters != null) {
- int filterCount = filters.size();
- parameters = new Object[filterCount * 2 + 2];
- parameters[0] = "operator";
- parameters[1] = operator;
- Iterator, Object>> iterator = filters.entrySet().iterator();
- for (int i = 1; i < filterCount; i++) {
- Map.Entry, Object> entry = iterator.next();
- parameters[i*2] = entry.getKey().getName();
- parameters[i*2+1] = entry.getValue();
- }
- } else {
- parameters = new Object[0];
- }
-
- final JsonObject response = this.api.getRequestHandler().get("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();
- final UUID uuid;
- if (o.has("uuid")) {
- final String uuidString = o.get("uuid").getAsString();
- if (uuidString == null || uuidString.equals("none") || uuidString.equals("")) {
- uuid = null;
- } else {
- uuid = NamelessAPI.websiteUuidToJavaUuid(uuidString);
- }
- } else {
- uuid = null;
- }
- users.add(new NamelessUser(this.api, id, username, true, uuid, false, -1L));
- }
-
- return Collections.unmodifiableList(users);
- }
-
-}
diff --git a/src/com/namelessmc/java_api/IntegrationType.java b/src/com/namelessmc/java_api/IntegrationType.java
deleted file mode 100644
index 40e541ed..00000000
--- a/src/com/namelessmc/java_api/IntegrationType.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package com.namelessmc.java_api;
-
-import org.jetbrains.annotations.NotNull;
-
-public enum IntegrationType {
-
- MINECRAFT("Minecraft"),
- DISCORD("Discord"),
- ;
-
- private final @NotNull String apiValue;
-
- IntegrationType(final @NotNull String apiValue) {
- this.apiValue = apiValue;
- }
-
- public @NotNull String apiValue() {
- return apiValue;
- }
-
-}
diff --git a/src/com/namelessmc/java_api/LanguageCodeMap.java b/src/com/namelessmc/java_api/LanguageCodeMap.java
deleted file mode 100644
index 459fd911..00000000
--- a/src/com/namelessmc/java_api/LanguageCodeMap.java
+++ /dev/null
@@ -1,41 +0,0 @@
-package com.namelessmc.java_api;
-
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.util.HashMap;
-import java.util.Map;
-
-public class LanguageCodeMap {
-
- private static final Map NAMELESS_TO_POSIX = new HashMap<>();
-
- static {
- NAMELESS_TO_POSIX.put("Czech", "cs_CZ");
- NAMELESS_TO_POSIX.put("German", "de_DE");
- NAMELESS_TO_POSIX.put("Greek", "el_GR");
- NAMELESS_TO_POSIX.put("EnglishUK", "en_UK");
- NAMELESS_TO_POSIX.put("EnglishUS", "en_US");
- NAMELESS_TO_POSIX.put("Spanish", "es_419");
- NAMELESS_TO_POSIX.put("SpanishES", "es_ES");
- NAMELESS_TO_POSIX.put("French", "fr_FR");
- NAMELESS_TO_POSIX.put("Hungarian", "hu_HU");
- NAMELESS_TO_POSIX.put("Italian", "it_IT");
- NAMELESS_TO_POSIX.put("Lithuanian", "lt_LT");
- NAMELESS_TO_POSIX.put("Norwegian", "nb_NO");
- NAMELESS_TO_POSIX.put("Dutch", "nl_NL");
- NAMELESS_TO_POSIX.put("Polish", "pl_PL");
- NAMELESS_TO_POSIX.put("Portuguese", "pt_BR");
- NAMELESS_TO_POSIX.put("Romanian", "ro_RO");
- NAMELESS_TO_POSIX.put("Russian", "ru_RU");
- NAMELESS_TO_POSIX.put("Slovak", "sk_SK");
- NAMELESS_TO_POSIX.put("SwedishSE", "sv_SE");
- NAMELESS_TO_POSIX.put("Turkish", "tr_TR");
- NAMELESS_TO_POSIX.put("Chinese(Simplified)", "zh_CN");
- }
-
- static @Nullable String getLanguagePosix(final @NotNull String language) {
- return NAMELESS_TO_POSIX.get(language);
- }
-
-}
diff --git a/src/com/namelessmc/java_api/LanguageEntity.java b/src/com/namelessmc/java_api/LanguageEntity.java
deleted file mode 100644
index 36943e84..00000000
--- a/src/com/namelessmc/java_api/LanguageEntity.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package com.namelessmc.java_api;
-
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-public interface LanguageEntity {
-
- @NotNull String getLanguage() throws NamelessException;
-
- @Nullable String getLanguagePosix() throws NamelessException;
-
-}
diff --git a/src/com/namelessmc/java_api/NamelessAPI.java b/src/com/namelessmc/java_api/NamelessAPI.java
deleted file mode 100755
index e3872394..00000000
--- a/src/com/namelessmc/java_api/NamelessAPI.java
+++ /dev/null
@@ -1,510 +0,0 @@
-package com.namelessmc.java_api;
-
-import com.google.common.base.Preconditions;
-import com.google.gson.Gson;
-import com.google.gson.JsonArray;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonObject;
-import com.namelessmc.java_api.exception.*;
-import com.namelessmc.java_api.modules.websend.WebsendAPI;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.math.BigInteger;
-import java.net.URL;
-import java.util.*;
-import java.util.stream.Collectors;
-import java.util.stream.StreamSupport;
-
-public final class NamelessAPI {
-
- static final Gson GSON = new Gson();
-
- @NotNull
- private final RequestHandler requests;
-
- NamelessAPI(@NotNull final RequestHandler requests) {
- this.requests = Objects.requireNonNull(requests, "Request handler is null");
- }
-
- @NotNull
- RequestHandler getRequestHandler() {
- return this.requests;
- }
-
- @NotNull
- public URL getApiUrl() {
- return this.getRequestHandler().getApiUrl();
- }
-
- @NotNull
- public String getApiKey() {
- return getApiKey(this.getApiUrl().toString());
- }
-
- @NotNull
- static String getApiKey(@NotNull final String url) {
- if (url.endsWith("/")) {
- return getApiKey(url.substring(0, url.length() - 1));
- }
-
- return url.substring(url.lastIndexOf('/') + 1);
- }
-
- /**
- * Get announcements visible to guests. Use {@link NamelessUser#getAnnouncements()} for non-guest announcements.
- * @return List of announcements
- */
- @NotNull
- public List<@NotNull Announcement> getAnnouncements() throws NamelessException {
- final JsonObject response = this.requests.get("announcements");
- return getAnnouncements(response);
- }
-
- /**
- * Get announcements visible to a {@link NamelessUser}
- * @param user User to get visible announcements for
- * @return List of announcements visible to the user
- * @deprecated Use {@link NamelessUser#getAnnouncements()}
- */
- @NotNull
- @Deprecated
- public List<@NotNull Announcement> getAnnouncements(@NotNull final NamelessUser user) throws NamelessException {
- final JsonObject response = this.requests.get("users/" + user.getUserTransformer() + "/announcements");
-
- return getAnnouncements(response);
- }
-
- /**
- * Convert announcement json to objects
- * @param response Announcements json API response
- * @return List of {@link Announcement} objects
- */
- @NotNull
- static List<@NotNull Announcement> getAnnouncements(@NotNull final JsonObject response) {
- return StreamSupport.stream(response.getAsJsonArray("announcements").spliterator(), false)
- .map(JsonElement::getAsJsonObject)
- .map(Announcement::new)
- .collect(Collectors.toList());
- }
-
- /**
- * Send Minecraft server information to the website. Currently, the exact JSON contents are undocumented.
- * @param jsonData Json data to submit
- */
- public void submitServerInfo(final @NotNull JsonObject jsonData) throws NamelessException {
- this.requests.post("minecraft/server-info", jsonData);
- }
-
- /**
- * Get website information
- * @return {@link Website} object containing website information
- */
- public Website getWebsite() throws NamelessException {
- final JsonObject json = this.requests.get("info");
- return new Website(json);
- }
-
- public FilteredUserListBuilder getRegisteredUsers() {
- return new FilteredUserListBuilder(this);
- }
-
- public @NotNull Optional getUser(final int id) throws NamelessException {
- final NamelessUser user = getUserLazy(id);
- if (user.exists()) {
- return Optional.of(user);
- } else {
- return Optional.empty();
- }
- }
-
- public @NotNull Optional getUser(@NotNull final String username) throws NamelessException {
- final NamelessUser user = getUserLazy(username);
- if (user.exists()) {
- return Optional.of(user);
- } else {
- return Optional.empty();
- }
- }
-
- public @NotNull Optional getUser(@NotNull final UUID uuid) throws NamelessException {
- final NamelessUser user = getUserLazy(uuid);
- if (user.exists()) {
- return Optional.of(user);
- } else {
- return Optional.empty();
- }
- }
-
- public @NotNull Optional getUserByDiscordId(final long discordId) throws NamelessException {
- final NamelessUser user = getUserLazyDiscord(discordId);
- if (user.exists()) {
- return Optional.of(user);
- } else {
- return Optional.empty();
- }
- }
-
- /**
- * Construct a NamelessUser object without making API requests (so without checking if the user exists)
- * @param id NamelessMC user id
- * @return Nameless user object, never null
- */
- public @NotNull NamelessUser getUserLazy(final int id) {
- return new NamelessUser(this, id, null, false, null, false, -1L);
- }
-
- /**
- * Construct a NamelessUser object without making API requests (so without checking if the user exists)
- * @param username NamelessMC user
- * @return Nameless user object, never null
- */
- public @NotNull NamelessUser getUserLazy(final @NotNull String username) {
- return new NamelessUser(this, -1, username, false, null, false, -1L);
- }
-
- /**
- * Construct a NamelessUser object without making API requests (so without checking if the user exists)
- * @param uuid Minecraft UUID
- * @return Nameless user object, never null
- */
- public @NotNull NamelessUser getUserLazy(@NotNull final UUID uuid) {
- return new NamelessUser(this, -1, null, true, uuid, false, -1L);
- }
-
- /**
- * Construct a NamelessUser object without making API requests (so without checking if the user exists)
- * @param username The user's username
- * @param uuid The user's Mojang UUID
- * @return Nameless user object, never null
- */
- public NamelessUser getUserLazy(@NotNull final String username, @NotNull final UUID uuid) {
- return new NamelessUser(this, -1, username, true, uuid, false,-1L);
- }
-
- /**
- * Construct a NamelessUser object without making API requests (so without checking if the user exists)
- * @param id NamelessMC user id
- * @return Nameless user object, never null
- */
- public NamelessUser getUserLazy(final int id, final @NotNull String username, final @NotNull UUID uuid) {
- return new NamelessUser(this, id, username, true, uuid, false, -1L);
- }
-
- /**
- * Construct a NamelessUser object without making API requests (so without checking if the user exists)
- * @param discordId Discord user id
- * @return Nameless user object, never null
- */
- public NamelessUser getUserLazyDiscord(final long discordId) {
- Preconditions.checkArgument(discordId > 0, "Discord id must be a positive long");
- return new NamelessUser(this, -1, null, false, null, true, discordId);
- }
-
- /**
- * Get NamelessMC group by ID
- * @param id Group id
- * @return Optional with a group if the group exists, empty optional if it doesn't
- */
- @NotNull
- public Optional<@NotNull Group> getGroup(final int id) throws NamelessException {
- final JsonObject response = this.requests.get("groups", "id", id);
- final JsonArray jsonArray = response.getAsJsonArray("groups");
- if (jsonArray.size() != 1) {
- return Optional.empty();
- } else {
- return Optional.of(new Group(jsonArray.get(0).getAsJsonObject()));
- }
- }
-
- /**
- * Get NamelessMC groups by name
- * @param name NamelessMC groups name
- * @return List of groups with this name, empty if there are no groups with this name.
- */
- @NotNull
- public List<@NotNull Group> getGroup(@NotNull final String name) throws NamelessException {
- Objects.requireNonNull(name, "Group name is null");
- final JsonObject response = this.requests.get("groups", "name", name);
- return groupListFromJsonArray(response.getAsJsonArray("groups"));
- }
-
- /**
- * Get a list of all groups on the website
- * @return list of groups
- */
- public @NotNull List getAllGroups() throws NamelessException {
- final JsonObject response = this.requests.get("groups");
- return groupListFromJsonArray(response.getAsJsonArray("groups"));
-
- }
-
- public int @NotNull[] getAllGroupIds() throws NamelessException {
- final JsonObject response = this.requests.get("groups");
- return StreamSupport.stream(response.getAsJsonArray("groups").spliterator(), false)
- .map(JsonElement::getAsJsonObject)
- .mapToInt(o -> o.get("id").getAsInt())
- .toArray();
- }
-
- private @NotNull List groupListFromJsonArray(@NotNull final JsonArray array) {
- return StreamSupport.stream(array.spliterator(), false)
- .map(JsonElement::getAsJsonObject)
- .map(Group::new)
- .collect(Collectors.toList());
- }
-
- /**
- * Registers a new account. The user will be emailed to set a password.
- *
- * @param username Username (this should match the user's in-game username when specifying a UUID)
- * @param email Email address
- * @param uuid Mojang UUID, if you wish to use the Minecraft integration. Nullable.
- * @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)
- * @see #registerUser(String, String)
- */
- public @NotNull Optional registerUser(@NotNull final String username,
- @NotNull final String email,
- @Nullable final UUID uuid)
- throws NamelessException, InvalidUsernameException, UsernameAlreadyExistsException, CannotSendEmailException, UuidAlreadyExistsException {
- Objects.requireNonNull(username, "Username is null");
- Objects.requireNonNull(email, "Email address is null");
-
- final JsonObject post = new JsonObject();
- post.addProperty("username", username);
- post.addProperty("email", email);
- if (uuid != null) {
- post.addProperty("uuid", uuid.toString());
- }
-
- try {
- final JsonObject response = this.requests.post("users/register", post);
-
- if (response.has("link")) {
- return Optional.of(response.get("link").getAsString());
- } else {
- return Optional.empty();
- }
- } catch (final ApiError e) {
- if (e.getError() == ApiError.INVALID_USERNAME) {
- throw new InvalidUsernameException();
- } else if (e.getError() == ApiError.USERNAME_ALREADY_EXISTS) {
- throw new UsernameAlreadyExistsException();
- } else if (e.getError() == ApiError.UNABLE_TO_SEND_REGISTRATION_EMAIL) {
- throw new CannotSendEmailException();
- } else if (e.getError() == ApiError.UUID_ALREADY_EXISTS) {
- throw new UuidAlreadyExistsException();
- } else {
- throw e;
- }
- }
- }
-
- /**
- * Register user without UUID {@link #registerUser(String, String, UUID)}
- * WARNING: This will fail if the website has Minecraft integration enabled!
- * @param username New username for this user
- * @param email New email address for this user
- * @return Verification URL if email verification is disabled.
- */
- public @NotNull Optional registerUser(@NotNull final String username,
- @NotNull final String email)
- throws NamelessException, InvalidUsernameException, UsernameAlreadyExistsException, CannotSendEmailException {
- try {
- return registerUser(username, email, null);
- } catch (final UuidAlreadyExistsException e) {
- throw new IllegalStateException("Website said duplicate uuid but we haven't specified a uuid?", e);
- }
- }
-
- /**
- * Set Discord bot URL (Nameless-Link internal webserver)
- * @param url Discord bot URL
- */
- public void setDiscordBotUrl(@NotNull final URL url) throws NamelessException {
- Objects.requireNonNull(url, "Bot url is null");
-
- final JsonObject json = new JsonObject();
- json.addProperty("url", url.toString());
- this.requests.post("discord/update-bot-settings", json);
- }
-
- /**
- * Set Discord guild (server) id
- * @param guildId Discord guild (server) id
- */
- public void setDiscordGuildId(final long guildId) throws NamelessException {
- final JsonObject json = new JsonObject();
- json.addProperty("guild_id", guildId + "");
- this.requests.post("discord/update-bot-settings", json);
- }
-
- /**
- * Set discord bot username and user id
- * @param username Bot username#tag
- * @param userId Bot user id
- * @see #setDiscordBotSettings(URL, long, String, long)
- */
- public void setDiscordBotUser(@NotNull final String username, final long userId) throws NamelessException {
- Objects.requireNonNull(username, "Bot username is null");
-
- final JsonObject json = new JsonObject();
- json.addProperty("bot_username", username);
- json.addProperty("bot_user_id", userId + "");
- this.requests.post("discord/update-bot-settings", json);
- }
-
- /**
- * Update all Discord bot settings.
- * @param url Discord bot URL
- * @param guildId Discord guild (server) id
- * @param username Discord bot username#tag
- * @param userId Discord bot user id
- * @see #setDiscordBotUrl(URL)
- * @see #setDiscordGuildId(long)
- * @see #setDiscordBotUser(String, long)
- */
- public void setDiscordBotSettings(@NotNull final URL url, final long guildId, @NotNull final String username, final long userId) throws NamelessException {
- Objects.requireNonNull(url, "Bot url is null");
- Objects.requireNonNull(username, "Bot username is null");
-
- final JsonObject json = new JsonObject();
- json.addProperty("url", url.toString());
- json.addProperty("guild_id", guildId + "");
- json.addProperty("bot_username", username);
- json.addProperty("bot_user_id", userId + "");
- this.requests.post("discord/update-bot-settings", json);
- }
-
- /**
- * Send list of Discord roles to the website for populating the dropdown in StaffCP > API > Group sync
- * @param discordRoles Map of Discord roles, key is role id, value is role name
- */
- public void submitDiscordRoleList(@NotNull final Map discordRoles) throws NamelessException {
- final JsonArray roles = new JsonArray();
- discordRoles.forEach((id, name) -> {
- final JsonObject role = new JsonObject();
- role.addProperty("id", id);
- role.addProperty("name", name);
- roles.add(role);
- });
- final JsonObject json = new JsonObject();
- json.add("roles", roles);
- this.requests.post("discord/submit-role-list", json);
- }
-
- /**
- * Update Discord username for a NamelessMC user associated with the provided Discord user id
- * @param discordUserId Discord user id
- * @param discordUsername New Discord [username#tag]s
- * @see #updateDiscordUsernames(long[], String[])
- */
- public void updateDiscordUsername(final long discordUserId,
- final @NotNull String discordUsername)
- throws NamelessException {
- Objects.requireNonNull(discordUsername, "Discord username is null");
-
- final JsonObject user = new JsonObject();
- user.addProperty("id", discordUserId);
- user.addProperty("name", discordUsername);
- final JsonArray users = new JsonArray();
- users.add(user);
- final JsonObject json = new JsonObject();
- json.add("users", users);
- this.requests.post("discord/update-usernames", json);
- }
-
- /**
- * Update Discord usernames in bulk
- * @param discordUserIds Discord user ids
- * @param discordUsernames New Discord [username#tag]s
- * @see #updateDiscordUsername(long, String)
- */
- public void updateDiscordUsernames(final long@NotNull[] discordUserIds,
- final @NotNull String@NotNull[] discordUsernames)
- throws NamelessException {
- Objects.requireNonNull(discordUserIds, "User ids array is null");
- Objects.requireNonNull(discordUsernames, "Usernames array is null");
- Preconditions.checkArgument(discordUserIds.length == discordUsernames.length,
- "discord user ids and discord usernames must be of same length");
-
- if (discordUserIds.length == 0) {
- return;
- }
-
- final JsonArray users = new JsonArray();
-
- for (int i = 0; i < discordUserIds.length; i++) {
- final JsonObject user = new JsonObject();
- user.addProperty("id", discordUserIds[i]);
- user.addProperty("name", discordUsernames[i]);
- users.add(user);
- }
-
- final JsonObject json = new JsonObject();
- json.add("users", users);
- this.requests.post("discord/update-usernames", json);
- }
-
- private void verifyIntegration(final @NotNull IntegrationType type,
- final @NotNull String verificationCode,
- final @NotNull String identifier,
- final @NotNull String username) throws NamelessException, InvalidValidateCodeException {
- JsonObject data = new JsonObject();
- data.addProperty("integration", type.apiValue());
- data.addProperty("code", Objects.requireNonNull(verificationCode, "Verification code is null"));
- data.addProperty("identifier", Objects.requireNonNull(identifier, "Identifier is null"));
- data.addProperty("username", Objects.requireNonNull(username, "Username is null"));
- try {
- this.requests.post("integration/verify", data);
- } catch (ApiError e) {
- if (e.getError() == ApiError.INVALID_VALIDATE_CODE) {
- throw new InvalidValidateCodeException();
- } else {
- throw e;
- }
- }
- }
-
- public void verifyMinecraft(final @NotNull String verificationCode,
- final @NotNull UUID uuid,
- final @NotNull String username) throws NamelessException, InvalidValidateCodeException {
- this.verifyIntegration(IntegrationType.MINECRAFT, verificationCode, uuid.toString(), username);
- }
-
- public void verifyDiscord(final @NotNull String verificationCode,
- final long id,
- final String username) throws NamelessException, InvalidValidateCodeException {
- this.verifyIntegration(IntegrationType.DISCORD, verificationCode, String.valueOf(id), username);
- }
-
- public @NotNull WebsendAPI websend() {
- return new WebsendAPI(this.requests);
- }
-
-
- /**
- * Adds back dashes to a UUID string and converts it to a Java UUID object
- * @param uuid UUID without dashes
- * @return UUID with dashes
- */
- static @NotNull UUID websiteUuidToJavaUuid(@NotNull final String uuid) {
- Objects.requireNonNull(uuid, "UUID string is null");
- // Website sends UUIDs without dashes, so we can't use UUID#fromString
- // https://stackoverflow.com/a/30760478
- try {
- final BigInteger a = new BigInteger(uuid.substring(0, 16), 16);
- final BigInteger b = new BigInteger(uuid.substring(16, 32), 16);
- return new UUID(a.longValue(), b.longValue());
- } catch (final IndexOutOfBoundsException e) {
- throw new IllegalArgumentException("Invalid uuid: '" + uuid + "'", e);
- }
- }
-
- @NotNull
- public static NamelessApiBuilder builder(@NotNull URL apiUrl, @NotNull String apiKey) {
- return new NamelessApiBuilder(apiUrl, apiKey);
- }
-
-}
diff --git a/src/com/namelessmc/java_api/NamelessApiBuilder.java b/src/com/namelessmc/java_api/NamelessApiBuilder.java
deleted file mode 100644
index 8ef49b4d..00000000
--- a/src/com/namelessmc/java_api/NamelessApiBuilder.java
+++ /dev/null
@@ -1,97 +0,0 @@
-package com.namelessmc.java_api;
-
-import com.google.gson.GsonBuilder;
-import com.namelessmc.java_api.logger.ApiLogger;
-import com.namelessmc.java_api.logger.PrintStreamLogger;
-import com.namelessmc.java_api.logger.Slf4jLogger;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.net.Authenticator;
-import java.net.ProxySelector;
-import java.net.URL;
-import java.net.http.HttpClient;
-import java.time.Duration;
-import java.util.Objects;
-
-public class NamelessApiBuilder {
-
- private static final Duration DEFAULT_TIMEOUT = Duration.ofSeconds(3);
- private static final String DEFAULT_USER_AGENT = "Nameless-Java-API";
-
- private final @NotNull URL apiUrl;
- private final @NotNull String apiKey;
- private final @NotNull HttpClient.Builder httpClientBuilder;
- private final @NotNull GsonBuilder gsonBuilder;
- private @NotNull String userAgent = DEFAULT_USER_AGENT;
- private @Nullable ApiLogger debugLogger = null;
- private Duration timeout = DEFAULT_TIMEOUT;
-
- NamelessApiBuilder(@NotNull URL apiUrl, @NotNull String apiKey) {
- this.apiUrl = apiUrl;
- this.apiKey = apiKey;
- this.httpClientBuilder = HttpClient.newBuilder();
- this.gsonBuilder = new GsonBuilder();
- this.gsonBuilder.disableHtmlEscaping();
- }
-
- public @NotNull NamelessApiBuilder userAgent(@NotNull final String userAgent) {
- this.userAgent = Objects.requireNonNull(userAgent, "User agent is null");
- return this;
- }
-
- public @NotNull NamelessApiBuilder debug(final boolean debug) {
- if (debug) {
- return this.withStdErrDebugLogging();
- } else {
- this.debugLogger = null;
- return this;
- }
- }
-
- public @NotNull NamelessApiBuilder withStdErrDebugLogging() {
- this.debugLogger = PrintStreamLogger.DEFAULT_INSTANCE;
- return this;
- }
-
- public @NotNull NamelessApiBuilder withSlf4jDebugLogging() {
- this.debugLogger = Slf4jLogger.DEFAULT_INSTANCE;
- return this;
- }
-
- public @NotNull NamelessApiBuilder withCustomDebugLogger(final @Nullable ApiLogger debugLogger) {
- this.debugLogger = debugLogger;
- return this;
- }
-
- @Deprecated
- public @NotNull NamelessApiBuilder withTimeoutMillis(final int timeout) {
- this.timeout = Duration.ofMillis(timeout);
- return this;
- }
-
- public @NotNull NamelessApiBuilder withTimeout(final @Nullable Duration timeout) {
- this.timeout = timeout;
- return this;
- }
-
- public @NotNull NamelessApiBuilder withProxy(ProxySelector proxy) {
- this.httpClientBuilder.proxy(proxy);
- return this;
- }
-
- public @NotNull NamelessApiBuilder withAuthenticator(Authenticator authenticator) {
- this.httpClientBuilder.authenticator(authenticator);
- return this;
- }
-
- public @NotNull NamelessApiBuilder withPrettyJson() {
- gsonBuilder.setPrettyPrinting();
- return this;
- }
-
- public @NotNull NamelessAPI build() {
- return new NamelessAPI(new RequestHandler(this.apiUrl, this.apiKey, this.httpClientBuilder.build(), this.gsonBuilder.create(), this.userAgent, this.debugLogger, this.timeout));
- }
-
-}
diff --git a/src/com/namelessmc/java_api/NamelessException.java b/src/com/namelessmc/java_api/NamelessException.java
deleted file mode 100644
index 549ee5e5..00000000
--- a/src/com/namelessmc/java_api/NamelessException.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package com.namelessmc.java_api;
-
-import org.jetbrains.annotations.NotNull;
-
-/**
- * Generic exception thrown by many methods in the Nameless API
- */
-public class NamelessException extends Exception {
-
- private static final long serialVersionUID = -3698433855091611529L;
-
- public NamelessException(@NotNull final String message) {
- super(message);
- }
-
- public NamelessException(@NotNull final String message, @NotNull final Throwable cause) {
- super(message, cause);
- }
-
- public NamelessException(@NotNull final Throwable cause) {
- super(cause);
- }
-
- public NamelessException() {
- super();
- }
-
-}
diff --git a/src/com/namelessmc/java_api/NamelessUser.java b/src/com/namelessmc/java_api/NamelessUser.java
deleted file mode 100644
index 6f0fa940..00000000
--- a/src/com/namelessmc/java_api/NamelessUser.java
+++ /dev/null
@@ -1,456 +0,0 @@
-package com.namelessmc.java_api;
-
-import com.google.common.base.Preconditions;
-import com.google.gson.JsonArray;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonObject;
-import com.namelessmc.java_api.Notification.NotificationType;
-import com.namelessmc.java_api.exception.AlreadyHasOpenReportException;
-import com.namelessmc.java_api.exception.CannotReportSelfException;
-import com.namelessmc.java_api.exception.ReportUserBannedException;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.util.*;
-import java.util.stream.Collectors;
-import java.util.stream.StreamSupport;
-
-public final class NamelessUser implements LanguageEntity {
-
- @NotNull
- private final NamelessAPI api;
- @NotNull
- private final RequestHandler requests;
-
- private int id; // -1 if not known
- private @Nullable String username; // null if not known
- private boolean uuidKnown;
- private @Nullable UUID uuid; // null if not known or not present
- private boolean discordIdKnown;
- private long discordId; // -1 if not known or not present
-
- @Nullable
- private JsonObject _userInfo; // Do not use directly, instead use getUserInfo()
-
- /**
- * Create a Nameless user. Only one of 'id', 'uuid', 'discordId' has to be provided.
- * @param api Nameless API
- * @param id The user's id, or -1 if not known
- * @param username The user's username, or null if not known
- * @param uuidKnown True if it is known whether this user has a UUID or not
- * @param uuid The user's uuid, or null if the user doesn't have a UUID, or it is not known whether the user has a UUID
- * @param discordIdKnown True if it is known whether this user has a linked Discord id or not
- * @param discordId The user's discord id, or -1 if the user doesn't have a linked Discord id, or it is not known whether the user has a Discord id
- */
- NamelessUser(@NotNull final NamelessAPI api,
- final int id,
- @Nullable final String username,
- boolean uuidKnown,
- @Nullable UUID uuid,
- boolean discordIdKnown,
- long discordId
- ) {
- this.api = api;
- this.requests = api.getRequestHandler();
-
- if (id == -1 && username == null && !uuidKnown && !discordIdKnown) {
- throw new IllegalArgumentException("You must specify at least one of ID, uuid, username, discordId");
- }
-
- this.id = id;
- this.username = username;
- this.uuidKnown = uuidKnown;
- this.uuid = uuid;
- this.discordIdKnown = discordIdKnown;
- this.discordId = discordId;
- }
-
- private JsonObject getUserInfo() throws NamelessException {
- if (this._userInfo == null) {
- final JsonObject response = this.requests.get("users/" + this.getUserTransformer());
-
- if (!response.get("exists").getAsBoolean()) {
- throw new UserNotExistException();
- }
-
- this._userInfo = response;
- }
-
- return this._userInfo;
- }
-
- public String getUserTransformer() {
- if (id != -1) {
- return "id:" + this.id;
- } else if (this.uuidKnown && this.uuid != null) {
- return "integration_id:minecraft:" + this.uuid;
- } else if (this.discordIdKnown && this.discordId != -1) {
- return "integration_id:discord:" + this.discordId;
- } else if (this.username != null) {
- return "username:" + username;
- } else {
- throw new IllegalStateException("ID, uuid, and username not known for this player. " +
- "This should be impossible, the constructor checks for this.");
- }
- }
-
- @NotNull
- public NamelessAPI getApi() {
- return this.api;
- }
-
- /**
- * The API method `userInfo` is only called once to improve performance.
- * This means that if something changes on the website, methods that use
- * data from the `userInfo` API method will keep returning the old data.
- * Calling this method will invalidate the cache and require making a new
- * API request. It will not make a new API request immediately. Calling
- * this method multiple times while the cache is already cleared has no
- * effect.
- */
- public void invalidateCache() {
- this._userInfo = null;
- }
-
- public int getId() throws NamelessException {
- if (this.id == -1) {
- this.id = this.getUserInfo().get("id").getAsInt();
- }
-
- return this.id;
- }
-
- public @NotNull String getUsername() throws NamelessException {
- if (this.username == null) {
- this.username = this.getUserInfo().get("username").getAsString();
- }
-
- return this.username;
- }
-
- public void updateUsername(final @NotNull String username) throws NamelessException {
- JsonObject post = new JsonObject();
- post.addProperty("username", username);
- this.requests.post("users/" + this.getUserTransformer() + "/update-username", post);
- }
-
- public @NotNull Optional<@NotNull UUID> getUniqueId() throws NamelessException {
- if (!this.uuidKnown) {
- JsonObject userInfo = this.getUserInfo();
- if (userInfo.has("uuid")) {
- final String uuidString = userInfo.get("uuid").getAsString();
- if (uuidString == null ||
- uuidString.equals("none") ||
- uuidString.equals("")) {
- this.uuid = null;
- } else {
- this.uuid = NamelessAPI.websiteUuidToJavaUuid(uuidString);
- }
- } else {
- this.uuid = null;
- }
- this.uuidKnown = true;
- }
-
- return Optional.ofNullable(this.uuid);
- }
-
- public @NotNull Optional<@NotNull Long> getDiscordId() throws NamelessException {
- if (!this.discordIdKnown) {
- JsonObject userInfo = this.getUserInfo();
- //noinspection ConstantConditions
- if (userInfo.has("discord_id")) {
- this.discordId = userInfo.get("discord_id").getAsLong();
- } else {
- this.discordId = -1;
- }
- this.discordIdKnown = true;
- }
-
- return this.discordId > 0 ? Optional.of(this.discordId) : Optional.empty();
- }
-
- public boolean exists() throws NamelessException {
- try {
- this.getUserInfo();
- return true;
- } catch (final UserNotExistException e) {
- return false;
- }
- }
-
- public @NotNull String getDisplayName() throws NamelessException {
- return this.getUserInfo().get("displayname").getAsString();
- }
-
- /**
- * @return The date the user registered on the website.
- */
- public @NotNull Date getRegisteredDate() throws NamelessException {
- return new Date(this.getUserInfo().get("registered_timestamp").getAsLong() * 1000);
- }
-
- public @NotNull Date getLastOnline() throws NamelessException {
- return new Date(this.getUserInfo().get("last_online_timestamp").getAsLong() * 1000);
- }
-
- /**
- * @return Whether this account is banned from the website.
- */
- public boolean isBanned() throws NamelessException {
- return this.getUserInfo().get("banned").getAsBoolean();
- }
-
- public boolean isVerified() throws NamelessException {
- return this.getUserInfo().get("validated").getAsBoolean();
- }
-
- @Override
- public @NotNull String getLanguage() throws NamelessException {
- return this.getUserInfo().get("language").getAsString();
- }
-
- /**
- * Get POSIX code for user language (uses lookup table)
- * @return Language code or null if the user's language does not exist in our lookup table
- */
- @Override
- public @Nullable String getLanguagePosix() throws NamelessException {
- return LanguageCodeMap.getLanguagePosix(this.getLanguage());
- }
-
- public @NotNull VerificationInfo getVerificationInfo() throws NamelessException {
- final boolean verified = isVerified();
- final JsonObject verification = this.getUserInfo().getAsJsonObject("verification");
- return new VerificationInfo(verified, verification);
- }
-
- /**
- * @return True if the user is member of at least one staff group, otherwise false
- */
- public boolean isStaff() throws NamelessException {
- JsonArray groups = this.getUserInfo().getAsJsonArray("groups");
- for (JsonElement elem : groups) {
- JsonObject group = elem.getAsJsonObject();
- if (group.has("staff") &&
- group.get("staff").getAsBoolean()) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * @return Set of user's groups
- * @see #getSortedGroups()
- */
- public @NotNull Set<@NotNull Group> getGroups() throws NamelessException {
- return Collections.unmodifiableSet(
- StreamSupport.stream(this.getUserInfo().getAsJsonArray("groups").spliterator(), false)
- .map(JsonElement::getAsJsonObject)
- .map(Group::new)
- .collect(Collectors.toSet()));
- }
-
- /**
- * @return List of the user's groups, sorted from low order to high order.
- * @see #getGroups()
- */
- public @NotNull List<@NotNull Group> getSortedGroups() throws NamelessException {
- return Collections.unmodifiableList(
- StreamSupport.stream(this.getUserInfo().getAsJsonArray("groups").spliterator(), false)
- .map(JsonElement::getAsJsonObject)
- .map(Group::new)
- .sorted()
- .collect(Collectors.toList()));
- }
-
- /**
- * Same as doing {@link #getGroups()}.get(0), but with better performance
- * since it doesn't need to create and sort a list of group objects.
- * Empty if the user is not in any groups.
- *
- * @return Player's group with the lowest order
- */
- public @NotNull Optional<@NotNull Group> getPrimaryGroup() throws NamelessException {
- final JsonArray groups = this.getUserInfo().getAsJsonArray("groups");
- if (groups.size() > 0) {
- return Optional.of(new Group(groups.get(0).getAsJsonObject()));
- } else {
- return Optional.empty();
- }
- }
-
- public void addGroups(@NotNull final Group@NotNull ... groups) throws NamelessException {
- final JsonObject post = new JsonObject();
- post.add("groups", groupsToJsonArray(groups));
- this.requests.post("users/" + this.getUserTransformer() + "/groups/add", post);
- invalidateCache(); // Groups modified, invalidate cache
- }
-
- public void removeGroups(@NotNull final Group@NotNull... groups) throws NamelessException {
- final JsonObject post = new JsonObject();
- post.add("groups", groupsToJsonArray(groups));
- this.requests.post("users/" + this.getUserTransformer() + "/groups/add", post);
- invalidateCache(); // Groups modified, invalidate cache
- }
-
- private JsonArray groupsToJsonArray(@NotNull final Group@NotNull [] groups) {
- final JsonArray array = new JsonArray();
- for (final Group group : groups) {
- array.add(group.getId());
- }
- return array;
- }
-
- public int getNotificationCount() throws NamelessException {
- final JsonObject response = this.requests.get("users/" + this.getUserTransformer() + "/notifications");
- return response.getAsJsonArray("notifications").size();
- }
-
- public @NotNull List getNotifications() throws NamelessException {
- final JsonObject response = this.requests.get("users/" + this.getUserTransformer() + "/notifications");
-
- 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;
- }
-
- /**
- * Creates a report for a website user
- * @param user User to report. Lazy loading possible, only the ID is used.
- * @param reason Reason why this player has been reported
- * @throws IllegalArgumentException Report reason is too long (>255 characters)
- * @throws IllegalArgumentException Report reason is too long (>255 characters)
- * @throws NamelessException Unexpected http or api error
- * @throws ReportUserBannedException If the user creating this report is banned
- * @throws AlreadyHasOpenReportException If the user creating this report already has an open report for this user
- * @throws CannotReportSelfException If the user tries to report themselves
- */
- public void createReport(@NotNull final NamelessUser user, @NotNull final String reason)
- throws NamelessException, ReportUserBannedException, AlreadyHasOpenReportException, CannotReportSelfException {
- Objects.requireNonNull(user, "User to report is null");
- Objects.requireNonNull(reason, "Report reason is null");
- Preconditions.checkArgument(reason.length() < 255,
- "Report reason too long, it's %s characters but must be less than 255", reason.length());
- final JsonObject post = new JsonObject();
- post.addProperty("reporter", this.getId());
- post.addProperty("reported", user.getId());
- post.addProperty("content", reason);
- try {
- this.requests.post("reports/create", post);
- } catch (final ApiError e) {
- if (e.getError() == ApiError.USER_CREATING_REPORT_BANNED) {
- throw new ReportUserBannedException();
- } else if (e.getError() == ApiError.REPORT_CONTENT_TOO_LARGE) {
- throw new IllegalStateException("Website said report reason is too long, but we have client-side validation for this");
- } else if (e.getError() == ApiError.USER_ALREADY_HAS_OPEN_REPORT) {
- throw new AlreadyHasOpenReportException();
- } else if (e.getError() == ApiError.CANNOT_REPORT_YOURSELF) {
- throw new CannotReportSelfException();
- } else {
- throw e;
- }
- }
- }
-
- /**
- * Create a report for a user who may or may not have a website account
- * @param reportedUuid The Mojang UUID of the Minecraft player to report
- * @param reportedName The Minecraft username of this player
- * @param reason Report reason
- * @throws IllegalArgumentException Report reason is too long (>255 characters)
- * @throws NamelessException Unexpected http or api error
- * @throws ReportUserBannedException If the user creating this report is banned
- * @throws AlreadyHasOpenReportException If the user creating this report already has an open report for this user
- * @throws CannotReportSelfException If the user tries to report themselves
- */
- public void createReport(final @NotNull UUID reportedUuid,
- final @NotNull String reportedName,
- final @NotNull String reason)
- throws NamelessException, ReportUserBannedException, AlreadyHasOpenReportException, CannotReportSelfException {
- Objects.requireNonNull(reportedUuid, "Reported uuid is null");
- Objects.requireNonNull(reportedName, "Reported name is null");
- Objects.requireNonNull(reason, "Report reason is null");
- Preconditions.checkArgument(reason.length() < 255,
- "Report reason too long, it's %s characters but must be less than 255", reason.length());
- final JsonObject post = new JsonObject();
- post.addProperty("reporter", this.getId());
- post.addProperty("reported_uid", reportedUuid.toString());
- post.addProperty("reported_username", reportedName);
- post.addProperty("content", reason);
- try {
- this.requests.post("reports/create", post);
- } catch (final ApiError e) {
- if (e.getError() == ApiError.USER_CREATING_REPORT_BANNED) {
- throw new ReportUserBannedException();
- } else if (e.getError() == ApiError.REPORT_CONTENT_TOO_LARGE) {
- throw new IllegalStateException("Website said report reason is too long, but we have client-side validation for this");
- } else if (e.getError() == ApiError.USER_ALREADY_HAS_OPEN_REPORT) {
- throw new AlreadyHasOpenReportException();
- } else if (e.getError() == ApiError.CANNOT_REPORT_YOURSELF) {
- throw new CannotReportSelfException();
- } else {
- throw e;
- }
- }
- }
-
- public void setDiscordRoles(final long@NotNull[] roleIds) throws NamelessException {
- final JsonObject post = new JsonObject();
- post.addProperty("user", this.getId());
- post.add("roles", NamelessAPI.GSON.toJsonTree(roleIds));
- this.requests.post("discord/set-roles", post);
- }
-
- /**
- * Get announcements visible to this user
- * @return List of announcements visible to this user
- */
- @NotNull
- public List<@NotNull Announcement> getAnnouncements() throws NamelessException {
- final JsonObject response = this.requests.get("users/" + this.getUserTransformer() + "/announcements");
- return NamelessAPI.getAnnouncements(response);
- }
-
- /**
- * Ban this user
- * @since 2021-10-24 commit cce8d262b0be3f70818c188725cd7e7fc4fdbb9a
- */
- public void banUser() throws NamelessException {
- this.requests.post("users/" + this.getUserTransformer() + "/ban", null);
- }
-
- public @NotNull Collection<@NotNull CustomProfileFieldValue> getProfileFields() throws NamelessException {
- if (!this.getUserInfo().has("profile_fields")) {
- return Collections.emptyList();
- }
-
- final JsonObject fieldsJson = this.getUserInfo().getAsJsonObject("profile_fields");
- final List fieldValues = new ArrayList<>(fieldsJson.size());
- for (final Map.Entry e : fieldsJson.entrySet()) {
- int id = Integer.parseInt(e.getKey());
- final JsonObject values = e.getValue().getAsJsonObject();
- fieldValues.add(new CustomProfileFieldValue(
- new CustomProfileField(
- id,
- values.get("name").getAsString(),
- CustomProfileFieldType.fromNamelessTypeInt(values.get("type").getAsInt()),
- values.get("public").getAsBoolean(),
- values.get("required").getAsBoolean(),
- values.get("description").getAsString()
- ),
- values.get("value").getAsString()
- ));
- }
-
- return fieldValues;
- }
-
-}
diff --git a/src/com/namelessmc/java_api/NamelessVersion.java b/src/com/namelessmc/java_api/NamelessVersion.java
deleted file mode 100644
index d4b56553..00000000
--- a/src/com/namelessmc/java_api/NamelessVersion.java
+++ /dev/null
@@ -1,101 +0,0 @@
-package com.namelessmc.java_api;
-
-import com.namelessmc.java_api.exception.UnknownNamelessVersionException;
-import org.jetbrains.annotations.NotNull;
-
-import java.util.*;
-
-public enum NamelessVersion {
-
- V2_0_0_PR_7("2.0.0-pr7", "2.0.0 pre-release 7", 2, 0, true),
- V2_0_0_PR_8("2.0.0-pr8", "2.0.0 pre-release 8", 2, 0, true),
- V2_0_0_PR_9("2.0.0-pr9", "2.0.0 pre-release 9", 2, 0, true),
- V2_0_0_PR_10("2.0.0-pr10", "2.0.0 pre-release 10", 2, 0, true),
- V2_0_0_PR_11("2.0.0-pr11", "2.0.0 pre-release 11", 2, 0, true),
- V2_0_0_PR_12("2.0.0-pr12", "2.0.0 pre-release 12", 2, 0, true),
- V2_0_0_PR_13("2.0.0-pr13", "2.0.0 pre-release 13", 2, 0, true),
-
- ;
-
- private static final Set SUPPORTED_VERSIONS = EnumSet.of(
- // Actually, only pr13 is supported but pr13 development releases still report as pr12
- V2_0_0_PR_12,
- V2_0_0_PR_13
- );
-
- private final @NotNull String name;
- private final @NotNull String friendlyName;
- private final int major;
- private final int minor;
- private final boolean isBeta;
-
- @SuppressWarnings("SameParameterValue")
- NamelessVersion(@NotNull final String name, @NotNull String friendlyName, final int major, final int minor, final boolean isBeta) {
- this.name = name;
- this.friendlyName = friendlyName;
- this.major = major;
- this.minor = minor;
- this.isBeta = isBeta;
- }
-
- public @NotNull String getName() {
- return this.name;
- }
-
- public @NotNull String getFriendlyName() {
- return this.friendlyName;
- }
-
- public int getMajor() {
- return this.major;
- }
-
- public int getMinor() {
- return this.minor;
- }
-
- /**
- * @return True if this version is a release candidate, pre-release, beta, alpha.
- */
- public boolean isBeta() {
- return this.isBeta;
- }
-
- @Override
- public String toString() {
- return this.friendlyName;
- }
-
- private static final Map BY_NAME = new HashMap<>();
-
- static {
- for (final NamelessVersion version : values()) {
- BY_NAME.put(version.getName(), version);
- }
- }
-
- public static @NotNull NamelessVersion parse(@NotNull final String versionName) throws UnknownNamelessVersionException {
- Objects.requireNonNull(versionName, "Version name is null");
- final NamelessVersion version = BY_NAME.get(versionName);
- if (version == null) {
- throw new UnknownNamelessVersionException(versionName);
- }
- return version;
- }
-
- /**
- * @return List of NamelessMC versions supported by the Java API
- */
- public static Set getSupportedVersions() {
- return SUPPORTED_VERSIONS;
- }
-
- /**
- * @param version A version to check
- * @return Whether the provided NamelessMC version is supported by this Java API library.
- */
- public static boolean isSupportedByJavaApi(final NamelessVersion version) {
- return SUPPORTED_VERSIONS.contains(version);
- }
-
-}
diff --git a/src/com/namelessmc/java_api/Notification.java b/src/com/namelessmc/java_api/Notification.java
deleted file mode 100644
index 83c39a2d..00000000
--- a/src/com/namelessmc/java_api/Notification.java
+++ /dev/null
@@ -1,49 +0,0 @@
-package com.namelessmc.java_api;
-
-public class Notification {
-
- private final String message;
- private final String url;
- private final NotificationType type;
-
- public Notification(final String message, final String url, final NotificationType type) {
- this.message = message;
- this.url = url;
- this.type = type;
- }
-
- public String getMessage() {
- return this.message;
- }
-
- public String getUrl() {
- return this.url;
- }
-
- public NotificationType getType() {
- return this.type;
- }
-
- public enum NotificationType {
-
- TAG,
- MESSAGE,
- LIKE,
- PROFILE_COMMENT,
- COMMENT_REPLY,
- THREAD_REPLY,
- FOLLOW,
-
- UNKNOWN;
-
- public static NotificationType fromString(final String string) {
- try {
- return NotificationType.valueOf(string.replace('-', '_').toUpperCase());
- } catch (final IllegalArgumentException e) {
- return NotificationType.UNKNOWN;
- }
- }
-
- }
-
-}
diff --git a/src/com/namelessmc/java_api/RequestHandler.java b/src/com/namelessmc/java_api/RequestHandler.java
deleted file mode 100644
index 871546a8..00000000
--- a/src/com/namelessmc/java_api/RequestHandler.java
+++ /dev/null
@@ -1,225 +0,0 @@
-package com.namelessmc.java_api;
-
-import com.google.common.base.Ascii;
-import com.google.common.base.Preconditions;
-import com.google.gson.Gson;
-import com.google.gson.JsonObject;
-import com.google.gson.JsonParser;
-import com.google.gson.JsonSyntaxException;
-import com.namelessmc.java_api.exception.ApiDisabledException;
-import com.namelessmc.java_api.logger.ApiLogger;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.io.IOException;
-import java.io.UnsupportedEncodingException;
-import java.net.URI;
-import java.net.URL;
-import java.net.URLEncoder;
-import java.net.http.HttpClient;
-import java.net.http.HttpRequest;
-import java.net.http.HttpResponse;
-import java.nio.charset.StandardCharsets;
-import java.time.Duration;
-import java.util.Arrays;
-import java.util.Objects;
-import java.util.function.Supplier;
-import java.util.stream.Collectors;
-
-public class RequestHandler {
-
- private final @NotNull URL baseUrl;
- private final @NotNull String apiKey;
- private final @NotNull HttpClient httpClient;
- private final @NotNull String userAgent;
- private final @Nullable ApiLogger debugLogger;
- private final @Nullable Duration timeout;
- private final @NotNull Gson gson;
-
- RequestHandler(final @NotNull URL baseUrl,
- final @NotNull String apiKey,
- final @NotNull HttpClient httpClient,
- final @NotNull Gson gson,
- final @NotNull String userAgent,
- final @Nullable ApiLogger debugLogger,
- final @Nullable Duration timeout) {
- this.baseUrl = Objects.requireNonNull(baseUrl, "Base URL is null");
- this.apiKey = Objects.requireNonNull(apiKey, "Api key is null");
- this.httpClient = Objects.requireNonNull(httpClient, "http client is null");
- this.gson = gson;
- this.userAgent = Objects.requireNonNull(userAgent, "User agent is null");
- this.debugLogger = debugLogger;
- this.timeout = timeout;
- }
-
- public @NotNull URL getApiUrl() {
- return this.baseUrl;
- }
-
- public @NotNull String getApiKey() {
- return this.apiKey;
- }
-
- public @NotNull JsonObject post(final @NotNull String route, final @Nullable JsonObject postData) throws NamelessException {
- Preconditions.checkArgument(!route.startsWith("/"), "Route must not start with a slash");
- URI uri = URI.create(this.baseUrl + "/" + route);
- return makeConnection(uri, postData);
- }
-
- public @NotNull JsonObject get(final @NotNull String route, final @NotNull Object @NotNull... parameters) throws NamelessException {
- Preconditions.checkArgument(!route.startsWith("/"), "Route must not start with a slash");
-
- final StringBuilder urlBuilder = new StringBuilder(this.baseUrl.toString());
- urlBuilder.append("/");
- urlBuilder.append(route);
-
- if (parameters.length > 0) {
- if (parameters.length % 2 != 0) {
- final String paramString = Arrays.stream(parameters).map(Object::toString).collect(Collectors.joining("|"));
- throw new IllegalArgumentException(String.format("Parameter string varargs array length must be even (length is %s - %s)", parameters.length, paramString));
- }
-
- for (int i = 0; i < parameters.length; i++) {
- if (i % 2 == 0) {
- urlBuilder.append("&");
- urlBuilder.append(parameters[i]);
- } else {
- urlBuilder.append("=");
- try {
- urlBuilder.append(URLEncoder.encode(parameters[i].toString(), StandardCharsets.UTF_8.toString()));
- } catch (final UnsupportedEncodingException e) {
- throw new RuntimeException(e);
- }
- }
- }
- }
-
- final @NotNull URI uri = URI.create(urlBuilder.toString());
- return makeConnection(uri, null);
- }
-
- private void debug(final @NotNull String message, @NotNull Supplier