diff --git a/README.md b/README.md index 314604b..ee4418c 100644 --- a/README.md +++ b/README.md @@ -9,17 +9,16 @@ [BadgeDownload]: https://api.bintray.com/packages/andre601/maven/JavaBotBlockAPI/images/download.svg [Download]: https://bintray.com/andre601/maven/JavaBotBlockAPI/_latestVersion -# JavaBotBlockAPI JavaBotBlockAPI is a continued and updated Java Wrapper for [BotBlock], a website that makes it possible to update guild counts on multiple lists with one API. This wrapper is a fork of [BotBlock4J] and was updated and improved to make it as userfriendly as possible. -## Installation +# Installation [![BadgeDownload]][Download] You can install JavaBotBlockAPI through the following methods. Make sure to replace `{version}` with the above shown version. -### Gradle +## Gradle Put this code into your `build.gradle`: ```gradle repositories{ @@ -31,7 +30,7 @@ dependencies{ } ``` -### Maven +## Maven For maven use this code snipped: ```xml @@ -43,62 +42,157 @@ For maven use this code snipped: ``` -## Usage +# Usage To use the Wrapper you have to follow these steps. -### Notes +## Notes In the below examples do I use a JDA instance called `jda`. This will also work with ShardManager. -### Creating a BotBlockAPI instance -You first need to create an instance of the BotBlockAPI class. -This class is the center of everything, including on what sites you want to post your guild counts. +## POST methods +You can post you guild counts to the different Botlists using the BotBlock API. -You can use the internal Builder class of BotBlockAPI to create an instance. It would look something like this: +### Creating an instance of BotBlockAPI +For posting your guild counts towards the BotBlock API you first need to create an instance of the BotBlockAPI class. +To do this it's recommended to use `BotBlockAPI.Builder()`. + +Here is an example of how it could look like. ```java -// Creating an instance of BotBlockAPI using BotBlockAPI.Builder BotBlockAPI api = new BotBlockAPI.Builder() - .addAuthToken("lbots.org", "MySecretToken123") // Adds a site with the corresponding API token. - .addAuthToken("botlist.space", "MySecretToken456") // The builder allows chaining of the methods. + .addAuthToken("lbots.org", "MySecretToken123") + .addAuthToken("botlist.space", "MySecretToken456") .build(); -``` - -#### Notes -There are a lot of other methods that you can use. Head over to the [Wiki] for more information. +``` +Remember to use `.build();` at the end to create the class. -### Posting -You can post the guilds either automatically or manually depending on your own preferences. +### Auto Posting +JavaBotBlockAPI allows you to post the guild counts automatically every X minutes. +To do this, you first need to get an instance of the RequestHandler and then call `.startAutoPosting(...)`. -#### Auto-posting -JavaBotBlockAPI comes with an inbuilt scheduler to post your guilds automatically. -To use it simply use the `startAutoPosting` method and provide either a JDA instance, ShardManager instance or the bot id and guild count. - -**Example**: +Here is an example: ```java -// We need to get an instance of RequestHandler to use the methods. RequestHandler handler = new RequestHandler(); -// jda is a JDA instance and api a BotBlockAPI instance. +// api is the instance of the BotBlockAPI handler.startAutoPosting(jda, api); -``` +``` +The delay in which you post the guild counts is set through the `.setUpdateInterval(int)` method in the BotBlockAPI.Builder(). -But what if you want to stop it? -For that just call the `stopAutoPosting` method: -``` -handler.stopAutoPosting(); -``` +### Cancel auto posting +To cancel the auto posting just call `.stopAutoPosting();` in the RequestHandler and it should cancel the scheduler. + +### Manually posting +There are methods that allow you to post the guild counts manually. +To Post your guild counts, just call the `.postGuilds(..., ...)` method in the RequestHandler. -#### Manual posting -If you want to post the guild counts manually you can use the `postGuilds` method. ```java -// We need to get an instance of RequestHandler to use the methods. RequestHandler handler = new RequestHandler(); -// jda is a JDA instance and api a BotBlockAPI instance. +// api is the instance of the BotBlockAPI handler.postGuilds(jda, api); ``` -### Exceptions +## GET methods +Since version 2.0.0 of JavaBotBlockAPI can you get certain informations of a bot or the available Botlists on the BotBlock API. + +### All available Botlists +You can call `.getBotlists()` to receive a JSONObject with all available Botlists in the BotBlockAPI. + +The returned JSONObject could look like this: +```json +{ + "botlist.space": { + "api_docs": "https://docs.botlist.space", + "api_post": "https://api.botlist.space/v1/bots/:id", + "api_field": "server_count", + "api_shard_id": null, + "api_shard_count": null, + "api_shards": "shards", + "api_get": "https://api.botlist.space/v1/bots/:id" + }, + "lbots.org": { + "api_docs": "https://lbots.org/api/docs", + "api_post": "https://lbots.org/api/v1/bots/:id/stats", + "api_field": "guild_count", + "api_shard_id": "shard_id", + "api_shard_count": "shard_count", + "api_shards": null, + "api_get": null + } +} +``` + +### Single Botlist +Calling `.getBotlist(String)` returns a specific Botlist as JSONObject. +For example does `.getBotlist("lbots.org")` return the following JSONObject: +```json +{ + "api_docs": "https://lbots.org/api/docs", + "api_post": "https://lbots.org/api/v1/bots/:id/stats", + "api_field": "guild_count", + "api_shard_id": "shard_id", + "api_shard_count": "shard_count", + "api_shards": null, + "api_get": null +} +``` + +### Complete Botinfo +Calling `.getAll(...)` returns a JSONObject from all the botlists and with some general information. + +The JSONObject can look like this: +```json +{ + "id": "123456789012345678", + "name": "MyBot", + "discriminator": "1234", + "owners": [ + "234567890123456789" + ], + "server_count": 100, + "invite": "https://discordapp.com/oauth2/authorize?client_id=123456789012345678&scope=bot", + "list_data": { + "botlist.space": [ + {"data"}, + 200 + ], + "lbots.org": [ + {"data"}, + 404 + ] + } +} +``` +`{"data"}` is the JSON that is returned by the provided Botlist meaning it's different for each site. +`name`, `owners`, `server_count` and `invite` is based on the most common appearances of the data. + +### Botinfo from all Botlists +You can call `.getBotInfos(...)` to only receive the bot info from all the Botlists. + +The returned JSONObject can look like this: +```json +{ + "botlist.space": [ + {"data"}, + 200 + ], + "lbots.org": [ + {"data"}, + 404 + ] +} +``` +`{"data"}` is the JSON that is returned by the provided Botlist meaning it's different for each site. + +### Botinfo of a single site +With `.getBotInfo(..., String)` can you receive the info of a specific site. +The returned data depends on the selected site and can be different for each one. + +### Owners +You can call `.getOwners(...)` to get the owners of a Bot from all the Botlists. +The info is returned as JSONArray and is based on how often the info is provided by the botlists. + +## Exceptions When you post the guild counts you could encounter certain Exceptions. You can receive the following exceptions: - `IOException` @@ -109,7 +203,7 @@ This shouldn't be the case with auto-posting since it has a minimum delay of 1 m - `NullPointerException` Thrown when BotBlock.org sends an empty response, meaning something got messed up on their side. -## Links +# Links Here are some useful links: - [BotBlock.org][BotBlock] Site for which this wrapper was made. - [API] API documentation. diff --git a/build.gradle b/build.gradle index b95ddcf..786403e 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,7 @@ plugins{ } group = 'com.andre601' -version = '1.0.5' +version = '1.1.0' sourceCompatibility = 1.8 @@ -21,21 +21,20 @@ repositories{ } dependencies{ - implementation group: 'com.squareup.okhttp3', name: 'okhttp', version: '3.11.0' - implementation group: 'org.json', name: 'json', version: '20180813' - implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.9' - implementation group: 'org.jetbrains', name: 'annotations', version: '16.0.2' - api(group: 'net.dv8tion', name: 'JDA', version: '3.8.3_463'){ + api group: 'com.squareup.okhttp3', name: 'okhttp', version: '3.11.0' + api group: 'org.json', name: 'json', version: '20180813' + api group: 'org.jetbrains', name: 'annotations', version: '16.0.2' + api(group: 'net.dv8tion', name: 'JDA', version: '4.BETA.0_23'){ exclude(module: 'opus-java') } } -task sourcesJar(type: Jar, dependsOn: classes) { +task sourcesJar(type: Jar, dependsOn: classes){ classifier = 'sources' from sourceSets.main.allSource } -task javadocJar(type: Jar, dependsOn: javadoc) { +task javadocJar(type: Jar, dependsOn: javadoc){ classifier = 'javadoc' from javadoc.destinationDir } diff --git a/src/main/java/com/andre601/javabotblockapi/BotBlockAPI.java b/src/main/java/com/andre601/javabotblockapi/BotBlockAPI.java index 1e1010c..b3611f6 100644 --- a/src/main/java/com/andre601/javabotblockapi/BotBlockAPI.java +++ b/src/main/java/com/andre601/javabotblockapi/BotBlockAPI.java @@ -18,29 +18,47 @@ */ package com.andre601.javabotblockapi; -import org.apache.commons.lang3.ObjectUtils; import org.jetbrains.annotations.NotNull; import java.util.HashMap; import java.util.Map; /** - * Class for handling the sites to post to and the delay for the auto-post option in + * Class for handling the sites to post to and the delay for the auto-post option in the * {@link com.andre601.javabotblockapi.RequestHandler RequestHandler}. */ public class BotBlockAPI{ + private static final int DEFAULT_DELAY = 30; + private Map authTokens; private int updateInterval; /** - * Creates an instance of BotBlockAPI with the provided api tokens (as Map) and update interval. + * Constructor to set the Map with the sites and tokens. + *
This will also set the update interval to 30 minutes. + * + * @param authTokens + * A Map of sites and their tokens. May not be null. + *
You may receive the API-token from your botlist. + */ + public BotBlockAPI(@NotNull Map authTokens){ + this.authTokens = authTokens; + this.updateInterval = DEFAULT_DELAY; + } + + /** + * Constructor to set the Map with the sites and tokens and also the update delay.. * * @param authTokens * A Map of sites and their tokens. May not be null. + *
You may receive the API-token from your botlist. * @param updateInterval * The update interval to set. */ public BotBlockAPI(@NotNull Map authTokens, int updateInterval){ + if(updateInterval < 2) + throw new IllegalArgumentException("Update interval may not be less than 2."); + this.authTokens = authTokens; this.updateInterval = updateInterval; } @@ -58,7 +76,7 @@ int getUpdateInterval(){ */ public static class Builder{ private Map authTokens = new HashMap<>(); - private int updateInterval = 30; + private int updateInterval = DEFAULT_DELAY; /** * Empty constructor to get the class. @@ -67,13 +85,14 @@ public Builder(){} /** * Adds the provided Site name and token to the Map. - *
If there is already an entry with the same key, it will be overwritten. + *
Entries with the same key will be overwritten. * * @param site * The name of the site. May not be null. *
A list of supported sites can be found here. * @param token * The API token you get from the corresponding botlist. May not be null. + *
You may receive the API-token from your botlist. * * @throws NullPointerException * When either the site or token are empty ({@code ""}). @@ -81,8 +100,8 @@ public Builder(){} * @return The Builder after the site and token were set. Useful for chaining. */ public Builder addAuthToken(@NotNull String site, @NotNull String token){ - if(ObjectUtils.isEmpty(site) || ObjectUtils.isEmpty(token)) - throw new NullPointerException("Empty site and/or token is not allowed!"); + Check.notEmpty(site, "Site may not be empty."); + Check.notEmpty(token, "Token may not be empty."); authTokens.put(site, token); @@ -102,8 +121,7 @@ public Builder addAuthToken(@NotNull String site, @NotNull String token){ * @return The Builder after the Map was set. Useful for chaining. */ public Builder setAuthTokens(@NotNull Map authTokens){ - if(ObjectUtils.isEmpty(authTokens)) - throw new NullPointerException("Empty Map for authTokens is not allowed!"); + Check.notEmpty(authTokens, "AuthTokens may not be null."); this.authTokens = authTokens; @@ -124,7 +142,7 @@ public Builder setAuthTokens(@NotNull Map authTokens){ */ public Builder setUpdateInteval(int updateInterval){ if(updateInterval < 2) - throw new IllegalArgumentException("updateInterval can't be less than 2!"); + throw new IllegalArgumentException("Update interval may not be less than 2."); this.updateInterval = updateInterval; diff --git a/src/main/java/com/andre601/javabotblockapi/Check.java b/src/main/java/com/andre601/javabotblockapi/Check.java new file mode 100644 index 0000000..94dbab6 --- /dev/null +++ b/src/main/java/com/andre601/javabotblockapi/Check.java @@ -0,0 +1,23 @@ +package com.andre601.javabotblockapi; + +import java.util.Map; + +class Check { + + static void notNull(Object target, String msg){ + if(target == null) + throw new NullPointerException(msg); + } + + static void notEmpty(CharSequence arguments, String msg){ + notNull(arguments, msg); + if(arguments.length() == 0) + throw new NullPointerException(msg); + } + + static void notEmpty(Map map, String msg){ + notNull(map, msg); + if(map.isEmpty()) + throw new NullPointerException(msg); + } +} diff --git a/src/main/java/com/andre601/javabotblockapi/RequestHandler.java b/src/main/java/com/andre601/javabotblockapi/RequestHandler.java index cdc4fbd..f490b85 100644 --- a/src/main/java/com/andre601/javabotblockapi/RequestHandler.java +++ b/src/main/java/com/andre601/javabotblockapi/RequestHandler.java @@ -19,12 +19,9 @@ package com.andre601.javabotblockapi; import com.andre601.javabotblockapi.exceptions.RatelimitedException; -import net.dv8tion.jda.bot.sharding.ShardManager; -import net.dv8tion.jda.core.JDA; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.RequestBody; -import okhttp3.Response; +import net.dv8tion.jda.api.sharding.ShardManager; +import net.dv8tion.jda.api.JDA; +import okhttp3.*; import org.jetbrains.annotations.NotNull; import org.json.JSONArray; import org.json.JSONException; @@ -38,11 +35,23 @@ /** * Class to handle post-requests to the BotBlock API. + * + *

The class can currently do the following things: + *

    + *
  • Posting Guild counts ({@link #postGuilds(ShardManager, BotBlockAPI) manually} and {@link #startAutoPosting(ShardManager, BotBlockAPI) automatically}).
  • + *
  • {@link #getBotlists() Getting botlists}.
  • + *
  • {@link #getBotlist(String) Getting a single Botlist}.
  • + *
  • {@link #getBotInfos(ShardManager) Getting lists a bot is on}.
  • + *
  • {@link #getBotInfo(ShardManager, String) Getting a single list a bot is on}.
  • + *
  • {@link #getOwners(ShardManager) Getting the owners of a bot.}
  • + *
*/ public class RequestHandler { private ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(); private final OkHttpClient CLIENT = new OkHttpClient(); + private final String BASE_URL = "https://botblock.org/api/"; + private String id = null; private JSONObject json = new JSONObject(); @@ -53,10 +62,10 @@ public class RequestHandler { public RequestHandler(){} /** - * Posts guilds from the provided {@link net.dv8tion.jda.bot.sharding.ShardManager ShardManager}. + * Posts guilds from the provided {@link net.dv8tion.jda.api.sharding.ShardManager ShardManager}. * * @param shardManager - * The {@link net.dv8tion.jda.bot.sharding.ShardManager ShardManager instance} that should be used. + * The {@link net.dv8tion.jda.api.sharding.ShardManager ShardManager instance} that should be used. * @param botBlockAPI * The {@link com.andre601.javabotblockapi.BotBlockAPI BotBlockAPI instance} that should be used. * @@ -64,11 +73,12 @@ public RequestHandler(){} * When the post request couldn't be performed properly. * @throws RatelimitedException * When the Bot (IP or ID) got ratelimited. - * - * @see #postGuilds(JDA, BotBlockAPI) postGuilds(JDA, BotBlockAPI) for posting with JDA. + * @throws NullPointerException + * When the ShardManager gives an invalid shard (Shard id 0 is null). */ public void postGuilds(@NotNull ShardManager shardManager, @NotNull BotBlockAPI botBlockAPI) throws IOException, RatelimitedException{ - this.id = shardManager.getShardById(0).getSelfUser().getId(); + this.id = Objects.requireNonNull(shardManager.getShardById(0), "Received invalid shard.") + .getSelfUser().getId(); json.put("server_count", shardManager.getGuilds().size()) .put("bot_id", id) @@ -82,17 +92,18 @@ public void postGuilds(@NotNull ShardManager shardManager, @NotNull BotBlockAPI botBlockAPI.getAuthTokens().forEach(json::put); - performRequest(); + postRequest(); } /** - * Posts the guilds from the provided {@link net.dv8tion.jda.core.JDA JDA}. - *
If the Bot is sharded (JDA has ShardInfo) then the {@code shard_id} and {@code shard_count} are posted too. + * Posts the guilds from the provided {@link net.dv8tion.jda.api.JDA JDA}. + *
If the bot is part of sharding and the shard count is bigger than 1, then {@code shard_id} and + * {@code shard_count} are added too. Those values are not supported by all sites! * *

If you use this on a sharded bot, better use {@link #postGuilds(ShardManager, BotBlockAPI)}. * * @param jda - * The {@link net.dv8tion.jda.core.JDA JDA instance} that should be used. + * The {@link net.dv8tion.jda.api.JDA JDA instance} that should be used. * @param botBlockAPI * The {@link com.andre601.javabotblockapi.BotBlockAPI BotBlockAPI instance} that should be used. * @@ -100,8 +111,6 @@ public void postGuilds(@NotNull ShardManager shardManager, @NotNull BotBlockAPI * When the post request couldn't be performed properly. * @throws RatelimitedException * When the Bot (IP or ID) got ratelimited. - * - * @see #postGuilds(ShardManager, BotBlockAPI) postGuilds(ShardManager, BotBlockAPI) for posting with ShardManager. */ public void postGuilds(@NotNull JDA jda, @NotNull BotBlockAPI botBlockAPI) throws IOException, RatelimitedException{ this.id = jda.getSelfUser().getId(); @@ -109,18 +118,17 @@ public void postGuilds(@NotNull JDA jda, @NotNull BotBlockAPI botBlockAPI) throw json.put("server_count", jda.getGuildCache().size()) .put("bot_id", id); - if(jda.getShardInfo() != null) + if(jda.getShardInfo().getShardTotal() > 1) json.put("shard_id", jda.getShardInfo().getShardId()) .put("shard_count", jda.getShardInfo().getShardTotal()); botBlockAPI.getAuthTokens().forEach(json::put); - performRequest(); + postRequest(); } /** * Posts the provided guilds from the provided Bot id. - *
This is a shortcut to {@link #postGuilds(String, int, BotBlockAPI)}. * * @param botId * The ID (as long) of the bot. @@ -133,10 +141,6 @@ public void postGuilds(@NotNull JDA jda, @NotNull BotBlockAPI botBlockAPI) throw * When the post request couldn't be performed properly. * @throws RatelimitedException * When the Bot (IP or ID) got ratelimited. - * - * @see #postGuilds(String, int, BotBlockAPI) postGuilds(String, int, BotBlockAPI) for the full method. - * @see #postGuilds(ShardManager, BotBlockAPI) postGuilds(ShardManager, BotBlockAPI) for posting with ShardManager. - * @see #postGuilds(JDA, BotBlockAPI) postGuilds(JDA, BotBlockAPI) for posting with JDA. */ public void postGuilds(Long botId, int guilds, @NotNull BotBlockAPI botBlockAPI) throws IOException, RatelimitedException{ postGuilds(Long.toString(botId), guilds, botBlockAPI); @@ -156,29 +160,28 @@ public void postGuilds(Long botId, int guilds, @NotNull BotBlockAPI botBlockAPI) * When the post request couldn't be performed properly. * @throws RatelimitedException * When the Bot (IP or ID) got ratelimited. - * - * @see #postGuilds(ShardManager, BotBlockAPI) postGuilds(ShardManager, BotBlockAPI) for posting with ShardManager. - * @see #postGuilds(JDA, BotBlockAPI) postGuilds(JDA, BotBlockAPI) for posting with JDA. */ public void postGuilds(@NotNull String botId, int guilds, @NotNull BotBlockAPI botBlockAPI) throws IOException, RatelimitedException{ + Check.notEmpty(botId, "ID may not be empty."); + + this.id = botId; + json.put("server_count", guilds) .put("bot_id", botId); botBlockAPI.getAuthTokens().forEach(json::put); - performRequest(); + postRequest(); } /** - * Starts a scheduler that posts the guilds from the provided {@link net.dv8tion.jda.bot.sharding.ShardManager ShardManager} + * Starts a scheduler that posts the guilds from the provided {@link net.dv8tion.jda.api.sharding.ShardManager ShardManager} * every X minutes. * * @param shardManager - * The {@link net.dv8tion.jda.bot.sharding.ShardManager ShardManager instance} that should be used. + * The {@link net.dv8tion.jda.api.sharding.ShardManager ShardManager instance} that should be used. * @param botBlockAPI * The {@link com.andre601.javabotblockapi.BotBlockAPI BotBlockAPI instance} that should be used. - * - * @see #startAutoPosting(JDA, BotBlockAPI) startAutoPosting(JDA, BotBlockAPI) for posting with JDA. */ public void startAutoPosting(@NotNull ShardManager shardManager, @NotNull BotBlockAPI botBlockAPI){ scheduler.scheduleAtFixedRate(() -> { @@ -191,15 +194,12 @@ public void startAutoPosting(@NotNull ShardManager shardManager, @NotNull BotBlo } /** - * Starts a scheduler that posts the guilds from the provided {@link net.dv8tion.jda.core.JDA JDA} - * every X minutes. + * Starts a scheduler that posts the guilds from the provided {@link net.dv8tion.jda.api.JDA JDA} every X minutes. * * @param jda - * The {@link net.dv8tion.jda.core.JDA JDA instance} that should be used. + * The {@link net.dv8tion.jda.api.JDA JDA instance} that should be used. * @param botBlockAPI * The {@link com.andre601.javabotblockapi.BotBlockAPI BotBlockAPI instance} that should be used. - * - * @see #startAutoPosting(ShardManager, BotBlockAPI) startAutoPosting(ShardManager, BotBlockAPI) for posting with ShardManager. */ public void startAutoPosting(@NotNull JDA jda, @NotNull BotBlockAPI botBlockAPI){ scheduler.scheduleAtFixedRate(() -> { @@ -220,9 +220,6 @@ public void startAutoPosting(@NotNull JDA jda, @NotNull BotBlockAPI botBlockAPI) * The guilds the bot is in. * @param botBlockAPI * The {@link com.andre601.javabotblockapi.BotBlockAPI BotBlockAPI instance} that should be used. - * - * @see #startAutoPosting(JDA, BotBlockAPI) startAutoPosting(JDA, BotBlockAPI) for posting with JDA. - * @see #startAutoPosting(ShardManager, BotBlockAPI) startAutoPosting(ShardManager, BotBlockAPI) for posting with ShardManager. */ public void startAutoPosting(Long botId, int guilds, @NotNull BotBlockAPI botBlockAPI){ scheduler.scheduleAtFixedRate(() -> { @@ -243,9 +240,6 @@ public void startAutoPosting(Long botId, int guilds, @NotNull BotBlockAPI botBlo * The guilds the bot is in. * @param botBlockAPI * The {@link com.andre601.javabotblockapi.BotBlockAPI BotBlockAPI instance} that should be used. - * - * @see #startAutoPosting(JDA, BotBlockAPI) startAutoPosting(JDA, BotBlockAPI) for posting with JDA. - * @see #startAutoPosting(ShardManager, BotBlockAPI) startAutoPosting(ShardManager, BotBlockAPI) for posting with ShardManager. */ public void startAutoPosting(@NotNull String botId, int guilds, @NotNull BotBlockAPI botBlockAPI){ scheduler.scheduleAtFixedRate(() -> { @@ -258,48 +252,740 @@ public void startAutoPosting(@NotNull String botId, int guilds, @NotNull BotBloc } /** - * Shuts down the scheduler. + * Stops the auto-posting by shutting down the scheduler. */ public void stopAutoPosting(){ scheduler.shutdown(); } - private void performRequest() throws IOException, RatelimitedException{ - Objects.requireNonNull(json, "JSON may not be null."); - Objects.requireNonNull(id, "Id may not be null."); + /** + * Gets the owners of a bot as a list. + * + * @param shardManager + * The {@link net.dv8tion.jda.api.sharding.ShardManager ShardManager instance} that should be used. + * + * @return The owners as a list. + * + * @throws IOException + * When the request couldn't be performed properly. + * @throws RatelimitedException + * When the API gets ratelimited. + * + * @since v2.0.0 + */ + public List getOwners(@NotNull ShardManager shardManager) throws IOException, RatelimitedException{ + return getOwners(Objects.requireNonNull(shardManager.getShardById(0), "Received invalid Shard") + .getSelfUser().getId()); + } + + + /** + * Gets the owners of a bot as a list. + * + * @param jda + * The {@link net.dv8tion.jda.api.JDA JDA instance} that should be used. + * + * @return The owners as a list. + * + * @throws IOException + * When the request couldn't be performed properly. + * @throws RatelimitedException + * When the API gets ratelimited. + * + * @since v2.0.0 + */ + public List getOwners(@NotNull JDA jda) throws IOException, RatelimitedException{ + return getOwners(jda.getSelfUser().getId()); + } + + /** + * Gets the owners of a bot as a list. + * + * @param id + * The id of the bot. + * + * @return The owners as a list. + * + * @throws IOException + * When the request couldn't be performed properly. + * @throws RatelimitedException + * When the API gets ratelimited. + * + * @since v2.0.0 + */ + public List getOwners(Long id) throws IOException, RatelimitedException{ + return getOwners(Long.toString(id)); + } + + /** + * Gets the owners of a bot as a List. + * + * @param id + * The ID of the bot to get information from. + * + * @return The owners as a list. + * + * @throws IOException + * When the post request couldn't be performed properly. + * @throws RatelimitedException + * When the Bot (IP or ID) got ratelimited. + * + * @since v2.0.0 + */ + public List getOwners(@NotNull String id) throws IOException, RatelimitedException{ + JSONObject json = getAll(id); + + JSONArray array = json.getJSONArray("owners"); + + List owners = new ArrayList<>(); + for(int i = 0; i < array.length(); i++) + owners.add(array.getString(i)); + + return owners; + } + + /** + * Gets all the available Botlists as JSONObject. + *
The data of each Botlist depends on the site. + * + *

The JSONObject will look something like this: + *


+     * {
+     *   "somebotlist.com": [
+     *    {@literal },
+     *     200
+     *   ],
+     *   "otherlist.org": [
+     *    {@literal },
+     *     404
+     *   ]
+     * }
+     * 
+ * + * @param shardManager + * The {@link net.dv8tion.jda.api.sharding.ShardManager ShardManager instance} that should be used. + * + * @return The Botlists as JSONObject. + * + * @throws IOException + * When the request couldn't be performed properly. + * @throws RatelimitedException + * When the API gets ratelimited. + * + * @since v2.0.0 + */ + public JSONObject getBotInfos(@NotNull ShardManager shardManager) throws IOException, RatelimitedException{ + return getBotInfos(Objects.requireNonNull(shardManager.getShardById(0), "Received invalid shard.") + .getSelfUser().getId()); + } + + /** + * Gets all the available Botlists as JSONObject. + *
The data of each Botlist depends on the site. + * + *

The JSONObject will look something like this: + *


+     * {
+     *   "somebotlist.com": [
+     *    {@literal },
+     *     200
+     *   ],
+     *   "otherlist.org": [
+     *    {@literal },
+     *     404
+     *   ]
+     * }
+     * 
+ * + * @param jda + * The {@link net.dv8tion.jda.api.JDA jda instance} that should be used. + * + * @return The Botlists as JSONObject. + * + * @throws IOException + * When the request couldn't be performed properly. + * @throws RatelimitedException + * When the API gets ratelimited. + * + * @since v2.0.0 + */ + public JSONObject getBotInfos(@NotNull JDA jda) throws IOException, RatelimitedException{ + return getBotInfos(jda.getSelfUser().getId()); + } + + /** + * Gets all the available Botlists as JSONObject. + *
The data of each Botlist depends on the site. + * + *

The JSONObject will look something like this: + *


+     * {
+     *   "somebotlist.com": [
+     *    {@literal },
+     *     200
+     *   ],
+     *   "otherlist.org": [
+     *    {@literal },
+     *     404
+     *   ]
+     * }
+     * 
+ * + * @param id + * The id of the bot. + * + * @return The Botlists as JSONObject. + * + * @throws IOException + * When the request couldn't be performed properly. + * @throws RatelimitedException + * When the API gets ratelimited. + * + * @since v2.0.0 + */ + public JSONObject getBotInfos(Long id) throws IOException, RatelimitedException{ + return getBotInfos(Long.toString(id)); + } + + /** + * Gets all the available Botlists as JSONObject. + *
The data of each Botlist depends on the site. + * + *

The JSONObject will look something like this: + *


+     * {
+     *   "somebotlist.com": [
+     *    {@literal },
+     *     200
+     *   ],
+     *   "otherlist.org": [
+     *    {@literal },
+     *     404
+     *   ]
+     * }
+     * 
+ * + * @param id + * The id of the bot + * + * @return The Botlists as JSONObject. + * + * @throws IOException + * When the request couldn't be performed properly. + * @throws RatelimitedException + * When the API gets ratelimited. + * + * @since v2.0.0 + */ + public JSONObject getBotInfos(@NotNull String id) throws IOException, RatelimitedException{ + return getAll(id).getJSONObject("list_data"); + } + + /** + * Gets the specific information from a single Botlist. + *
The returned data depends on the Botlist. + * + *

The JSONObject will look something like this: + *


+     * {[
+     *  {@literal },
+     *   200
+     * ]}
+     * 
+ * + * @param shardManager + * The {@link net.dv8tion.jda.api.sharding.ShardManager ShardManager instance} that should be used. + * @param site + * The sites name to get information from. + *
A list of supported sites can be found here. + * + * @return The sites information as JSONArray. + * + * @throws IOException + * When the request couldn't be performed properly. + * @throws RatelimitedException + * When the API gets ratelimited. + * + * @since v2.0.0 + */ + public JSONArray getBotInfo(@NotNull ShardManager shardManager, @NotNull String site) throws IOException, RatelimitedException{ + return getBotInfo(Objects.requireNonNull(shardManager.getShardById(0), "Received invalid shard.") + .getSelfUser().getId(), site); + } + + /** + * Gets the specific information from a single Botlist. + *
The returned data depends on the Botlist. + * + *

The JSONObject will look something like this: + *


+     * {[
+     *  {@literal },
+     *   200
+     * ]}
+     * 
+ * + * @param id + * The id of the bot. + * @param site + * The sites name to get information from. + *
A list of supported sites can be found here. + * + * @return The sites information as JSONArray. + * + * @throws IOException + * When the request couldn't be performed properly. + * @throws RatelimitedException + * When the API gets ratelimited. + * + * @since v2.0.0 + */ + public JSONArray getBotInfo(Long id, @NotNull String site) throws IOException, RatelimitedException{ + return getBotInfo(Long.toString(id), site); + } + + /** + * Gets the specific information from a single Botlist. + *
The returned data depends on the Botlist. + * + *

The JSONObject will look something like this: + *


+     * {[
+     *  {@literal },
+     *   200
+     * ]}
+     * 
+ * + * @param jda + * The {@link net.dv8tion.jda.api.JDA JDA instance} that should be used. + * @param site + * The sites name to get information from. + *
A list of supported sites can be found here. + * + * @return The sites information as JSONArray. + * + * @throws IOException + * When the request couldn't be performed properly. + * @throws RatelimitedException + * When the API gets ratelimited. + * + * @since v2.0.0 + */ + public JSONArray getBotInfo(@NotNull JDA jda, String site) throws IOException, RatelimitedException{ + return getBotInfo(jda.getSelfUser().getId(), site); + } + + /** + * Gets the specific information from a single Botlist. + *
The returned data depends on the Botlist. + * + *

The JSONObject will look something like this: + *


+     * {[
+     *  {@literal },
+     *   200
+     * ]}
+     * 
+ * + * @param id + * The id of the bot. + * @param site + * The sites name to get information from. + *
A list of supported sites can be found here. + * + * @return The sites information as JSONArray. + * + * @throws IOException + * When the request couldn't be performed properly. + * @throws RatelimitedException + * When the API gets ratelimited. + * + * @since v2.0.0 + */ + public JSONArray getBotInfo(@NotNull String id, @NotNull String site) throws IOException, RatelimitedException{ + return getAll(id).getJSONObject("list_data").getJSONArray(site); + } + + /** + * Gets information from BotBlock about the provided Bot. + *
The information can contain: + *
    + *
  • Bot id
  • + *
  • Username
  • + *
  • Discriminator
  • + *
  • Botowners*
  • + *
  • Server count*
  • + *
  • OAuth invite*
  • + *
  • Data of the botlists**
  • + *
+ * *Based on most appearances on the botlists. + *
**The provided data depends on the Botlist and can be different. + * + *

A response could look like this: + *


+     * {
+     *     "id": "123456789012345678",
+     *     "usernam": "MyBot",
+     *     "discriminator": "1234",
+     *     "owners": [
+     *         "234567890123456789"
+     *     ],
+     *     "server_count": 100,
+     *     "invite":{@literal "https://discordapp.com/oauth2/authorize?client_id=123456789012345678&scope=bot"},
+     *     "list_data": {
+     *         "somebotlist.com": [
+     *            {@literal },
+     *             200
+     *         ],
+     *         "otherlist.org": [
+     *            {@literal },
+     *             404
+     *         ]
+     *     }
+     * }
+     * 
+ * + * @param shardManager + * The instance of {@link net.dv8tion.jda.api.sharding.ShardManager ShardManager} to use. + * + * @return The Bot information as JSONObject. + * + * @throws IOException + * When the request couldn't be performed properly. + * @throws RatelimitedException + * When the API gets ratelimited. + * + * @since v2.0.0 + */ + public JSONObject getAll(@NotNull ShardManager shardManager) throws IOException, RatelimitedException{ + return getAll(Objects.requireNonNull(shardManager.getShardById(0), "Received invalid shard.") + .getSelfUser().getId()); + } + + /** + * Gets information from BotBlock about the provided Bot. + *
The information can contain: + *
    + *
  • Bot id
  • + *
  • Username
  • + *
  • Discriminator
  • + *
  • Botowners*
  • + *
  • Server count*
  • + *
  • OAuth invite*
  • + *
  • Data of the botlists**
  • + *
+ * *Based on most appearances on the botlists. + *
**The provided data depends on the Botlist and can be different. + * + *

A response could look like this: + *


+     * {
+     *     "id": "123456789012345678",
+     *     "usernam": "MyBot",
+     *     "discriminator": "1234",
+     *     "owners": [
+     *         "234567890123456789"
+     *     ],
+     *     "server_count": 100,
+     *     "invite":{@literal "https://discordapp.com/oauth2/authorize?client_id=123456789012345678&scope=bot"},
+     *     "list_data": {
+     *         "somebotlist.com": [
+     *            {@literal },
+     *             200
+     *         ],
+     *         "otherlist.org": [
+     *            {@literal },
+     *             404
+     *         ]
+     *     }
+     * }
+     * 
+ * + * @param jda + * The instance of {@link net.dv8tion.jda.api.JDA JDA} to use. + * + * @return The Bot information as JSONObject. + * + * @throws IOException + * When the request couldn't be performed properly. + * @throws RatelimitedException + * When the API gets ratelimited. + * + * @since v2.0.0 + */ + public JSONObject getAll(@NotNull JDA jda) throws IOException, RatelimitedException{ + return getAll(jda.getSelfUser().getId()); + } + + /** + * Gets information from BotBlock about the provided Bot. + *
The information can contain: + *
    + *
  • Bot id
  • + *
  • Username
  • + *
  • Discriminator
  • + *
  • Botowners*
  • + *
  • Server count*
  • + *
  • OAuth invite*
  • + *
  • Data of the botlists**
  • + *
+ * *Based on most appearances on the botlists. + *
**The provided data depends on the Botlist and can be different. + * + *

A response could look like this: + *


+     * {
+     *     "id": "123456789012345678",
+     *     "usernam": "MyBot",
+     *     "discriminator": "1234",
+     *     "owners": [
+     *         "234567890123456789"
+     *     ],
+     *     "server_count": 100,
+     *     "invite":{@literal "https://discordapp.com/oauth2/authorize?client_id=123456789012345678&scope=bot"},
+     *     "list_data": {
+     *         "somebotlist.com": [
+     *            {@literal },
+     *             200
+     *         ],
+     *         "otherlist.org": [
+     *            {@literal },
+     *             404
+     *         ]
+     *     }
+     * }
+     * 
+ * + * @param id + * The id of the bot. + * + * @return The Bot information as JSONObject. + * + * @throws IOException + * When the request couldn't be performed properly. + * @throws RatelimitedException + * When the API gets ratelimited. + * + * @since v2.0.0 + */ + public JSONObject getAll(Long id) throws IOException, RatelimitedException{ + return getAll(Long.toString(id)); + } + + /** + * Gets information from BotBlock about the provided Bot. + *
The information can contain: + *
    + *
  • Bot id
  • + *
  • Username
  • + *
  • Discriminator
  • + *
  • Botowners*
  • + *
  • Server count*
  • + *
  • OAuth invite*
  • + *
  • Data of the botlists**
  • + *
+ * *Based on most appearances on the botlists. + *
**The provided data depends on the Botlist and can be different. + * + *

A response could look like this: + *


+     * {
+     *     "id": "123456789012345678",
+     *     "usernam": "MyBot",
+     *     "discriminator": "1234",
+     *     "owners": [
+     *         "234567890123456789"
+     *     ],
+     *     "server_count": 100,
+     *     "invite":{@literal "https://discordapp.com/oauth2/authorize?client_id=123456789012345678&scope=bot"},
+     *     "list_data": {
+     *         "somebotlist.com": [
+     *            {@literal },
+     *             200
+     *         ],
+     *         "otherlist.org": [
+     *            {@literal },
+     *             404
+     *         ]
+     *     }
+     * }
+     * 
+ * + * @param id + * The id of the bot. + * + * @return The Bot information as JSONObject. + * + * @throws IOException + * When the request couldn't be performed properly. + * @throws RatelimitedException + * When the API gets ratelimited. + * + * @since v2.0.0 + */ + public JSONObject getAll(@NotNull String id) throws IOException, RatelimitedException{ + String url = BASE_URL + "bots/" + id; + + Request request = new Request.Builder() + .url(url) + .addHeader("User-Agent", id) + .build(); + + try(Response response = CLIENT.newCall(request).execute()){ + Check.notNull(response.body(), "Received empty response body from BotBlcok API."); + ResponseBody responseBody = response.body(); + + Check.notEmpty(responseBody.string(), "Received empty response body from BotBlock API."); + + if(!response.isSuccessful()){ + if(response.code() == 429) + throw new RatelimitedException(responseBody.string()); + + throw new IOException(String.format( + "Couldn't get Bot information. Site responded with error code %d (%s)", + response.code(), + response.message() + )); + } + + return new JSONObject(responseBody.string()); + } + } + + /** + * Returns the provided botlist info that is saved in BotBlock. + * + *

A response could look like this: + *


+     * {
+     *     "api_docs": "https://somebotlist.com/docs",
+     *     "api_post": "https://somebotlist.com/api/v1/bots/:id/post",
+     *     "api_field": "server_count",
+     *     "api_shard_id": "shard_id",
+     *     "api_shard_count": "shard_count",
+     *     "api_shards": "shards",
+     *     "api_get": "https://somebotlist.com/api/v1/bots/:id"
+     * }
+     * 
+ * + * @param name + * The name of the botlist. + * + * @return The botlist as JSONObject. + * + * @throws IOException + * When the request couldn't be performed properly. + * @throws RatelimitedException + * When the API gets ratelimited. + * + * @since v2.0.0 + */ + public JSONObject getBotlist(@NotNull String name) throws IOException, RatelimitedException{ + return getBotlists().getJSONObject(name); + } + + /** + * Returns the current botlists that BotBlock supports. + * + *

A response could look like this: + *


+     * {
+     *     "somebotlist.com": {
+     *         "api_docs": "https://somebotlist.com/docs",
+     *         "api_post": "https://somebotlist.com/api/v1/bots/:id/post",
+     *         "api_field": "server_count",
+     *         "api_shard_id": "shard_id",
+     *         "api_shard_count": "shard_count",
+     *         "api_shards": "shards",
+     *         "api_get": "https://somebotlist.com/api/v1/bots/:id"
+     *     },
+     *     "otherlist.org": {
+     *         "api_docs": "https://docs.otherlist.org",
+     *         "api_post": null,
+     *         "api_field": null,
+     *         "api_shard_id": null,
+     *         "api_shard_count": null,
+     *         "api_shards": null,
+     *         "api_get": "https://api.otherlist.org/v2/bot/:id"
+     *     }
+     * }
+     * 
+ * + * @return The botlists as JSONObject. + * + * @throws IOException + * When the request couldn't be performed properly. + * @throws RatelimitedException + * When the API gets ratelimited. + * + * @since v2.0.0 + */ + public JSONObject getBotlists() throws IOException, RatelimitedException{ + String url = BASE_URL + "lists"; + + Request request = new Request.Builder() + .url(url) + .build(); + + try(Response response = CLIENT.newCall(request).execute()){ + Check.notNull(response.body(), "Received empty response body from BotBlcok API."); + ResponseBody responseBody = response.body(); + + Check.notEmpty(responseBody.string(), "Received empty response body from BotBlock API."); + + if(!response.isSuccessful()){ + if(response.code() == 429) + throw new RatelimitedException(responseBody.string()); + + throw new IOException(String.format( + "Couldn't get Botlists. Site responded with error code %d (%s)", + response.code(), + response.message() + )); + } + + return new JSONObject(responseBody.string()); + } + } + + private void postRequest() throws IOException, RatelimitedException{ + Check.notNull(json, "JSON may not be null."); + Check.notEmpty(id, "ID may not be empty."); + + String url = BASE_URL + "count"; RequestBody body = RequestBody.create(null, json.toString()); Request request = new Request.Builder() - .url("https://botblock.org/api/count") + .url(url) .addHeader("User-Agent", id) .addHeader("Content-Type", "application/json") // Some sites require this in the header. .post(body) .build(); try(Response response = CLIENT.newCall(request).execute()){ - Objects.requireNonNull(response.body(), "Received empty body from BotBlocks API."); + Check.notNull(response.body(), "Received empty response body from BotBlcok API."); + ResponseBody responseBody = response.body(); - if(response.body().string().isEmpty()) - throw new NullPointerException("Received empty body from BotBlocks API."); + Check.notEmpty(responseBody.string(), "Received empty response body from BotBlock API."); if(!response.isSuccessful()){ if(response.code() == 429) - throw new RatelimitedException(response.body().string()); + throw new RatelimitedException(responseBody.string()); throw new IOException(String.format( - "Couldn't post guild counts to BotBlockAPI! Site responded with %d (%s)", + "Couldn't post guild counts to BotBlockAPI! Site responded with error code %d (%s)", response.code(), response.message() )); } - JSONObject json = new JSONObject(response.body()); + JSONObject json = new JSONObject(responseBody); if(json.has("failure")){ JSONObject failure = json.getJSONObject("failure"); List sites = new ArrayList<>(); - for (String key : failure.keySet()) { - try { + for(String key : failure.keySet()){ + try{ JSONArray array = failure.getJSONArray(key); sites.add(String.format( "Name: %s, Error code: %d, Error Message: %s", @@ -307,7 +993,7 @@ private void performRequest() throws IOException, RatelimitedException{ array.getInt(0), array.getString(1) )); - } catch (JSONException ex) { + }catch (JSONException ex){ Map notFound = failure.toMap(); sites.add("Errors: " + notFound.toString()); }