diff --git a/README.md b/README.md index 677a4fe..b670023 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,8 @@

+

-[![Discord Banner 2](https://discordapp.com/api/guilds/847480795232993280/widget.png?style=banner2)](https://discord.gg/dvZmP92d4h) - # JavaDestinyAPI This library is used as an interface to the API for the game Destiny 2, created by Bungie. @@ -37,7 +36,8 @@ You need to get an API key from [bungie.net/developer](https://bungie.net/develo ```java DestinyAPI api = new DestinyAPI().setApiKey("YOUR API KEY HERE"); ``` -*Must be intialized before any parts of the API are used!* +**IMPORTANT:** *DestinyAPI MUST be the first thing initialized before making any interactions with the API! This is because +all interactions with the API rely on the API Key set in DestinyAPI.* **Getting the time played in hours, of the user named dec4234#9904** ```java @@ -51,13 +51,19 @@ System.out.println(new Clan("Heavenly Mayhem").getFounder().getSupplementalDispl Check out the [wiki](https://github.com/dec4234/JavaDestinyAPI/wiki/Getting-Started) for more specific examples and information. +### An aside about APIException +As of 4/30/2024, all functions that interact with the API in any way (i.e. could make an HTTP request) throw APIException. +This is to allow for you, the user, to handle API errors in the way you see fit. This means you must use a try/catch or +add "throws" to your function signature. +
+This was done because the old way could create a lot of runtime exceptions which were hard to account for. +The most important error you may want to look at is [APIOfflineException](https://github.com/dec4234/JavaDestinyAPI/blob/master/src/main/java/net/dec4234/javadestinyapi/exceptions/APIOfflineException.java) +which indicates when Bungie has disabled the API for maintenence. This may be useful to tell your user's that the API is offline. + ## How's it made? There is both official and unofficial documentation for the API available on [destinydevs.github.io](http://destinydevs.github.io/BungieNetPlatform/docs/Endpoints) and on the [offical bungie api documentation](https://bungie-net.github.io/). ### TO-DO - Managing inventory / Item transferring - Collections / Triumphs -- Revamping the wiki - -### Contributing -All development since 3/10/2022 on new features occurs within the [dev](https://github.com/dec4234/JavaDestinyAPI/tree/dev) branch. Clone that and submit a pull request with your changes. \ No newline at end of file +- Revamping the wiki \ No newline at end of file diff --git a/src/main/java/net/dec4234/javadestinyapi/material/inventory/items/InventoryItem.java b/src/main/java/net/dec4234/javadestinyapi/material/inventory/items/InventoryItem.java index 36e9f2c..6d21cf1 100644 --- a/src/main/java/net/dec4234/javadestinyapi/material/inventory/items/InventoryItem.java +++ b/src/main/java/net/dec4234/javadestinyapi/material/inventory/items/InventoryItem.java @@ -17,6 +17,12 @@ import java.util.ArrayList; import java.util.List; +/** + * An InventoryItem describes any item contained in a player's or one of their characters inventories. + * This could be a weapon, armor piece, ghost, etc. + *

+ * This class is currently incomplete. If you would like to contribute, please create a pull request on GitHub + */ public class InventoryItem extends DestinyItem { private static HttpUtils httpUtils = DestinyAPI.getHttpUtils(); diff --git a/src/main/java/net/dec4234/javadestinyapi/material/inventory/loadouts/Loadout.java b/src/main/java/net/dec4234/javadestinyapi/material/inventory/loadouts/Loadout.java index fd89ed2..c2ed180 100644 --- a/src/main/java/net/dec4234/javadestinyapi/material/inventory/loadouts/Loadout.java +++ b/src/main/java/net/dec4234/javadestinyapi/material/inventory/loadouts/Loadout.java @@ -13,6 +13,9 @@ import java.util.ArrayList; import java.util.List; +/** + * This class is currently incomplete. If you would like to contribute, please create a pull request on GitHub + */ public class Loadout { private String colorHash; diff --git a/src/main/java/net/dec4234/javadestinyapi/material/user/BungieUser.java b/src/main/java/net/dec4234/javadestinyapi/material/user/BungieUser.java index d5280db..26371f0 100644 --- a/src/main/java/net/dec4234/javadestinyapi/material/user/BungieUser.java +++ b/src/main/java/net/dec4234/javadestinyapi/material/user/BungieUser.java @@ -51,10 +51,14 @@ public class BungieUser extends ContentFramework { private Clan clan = null; /** - * The simplest starter of a BungieUser. - * Just provide the bungie membership id of the user + * The simplest constructor for starting a BungieUser, only requires the BungieID of the user + *
+ * A BungieID is a static, unique id representing a bungie account. + *
+ * Multiple destiny accounts (on separate platforms) can be associated with the same BungieID. Use + * membership type to specify which profile you want to examine. * - * @param bungieMembershipID The bungie id of the user you want to get information for + * @param bungieMembershipID The BungieID of the user you want to examine */ public BungieUser(String bungieMembershipID) { super("https://www.bungie.net/Platform/Destiny2/-1/Profile/" + bungieMembershipID + "/LinkedProfiles/?components=200", source -> { @@ -63,6 +67,14 @@ public BungieUser(String bungieMembershipID) { this.bungieMembershipID = bungieMembershipID; } + /** + * Uses a BungieID and a JsonObject of data about the user to construct the object more effeciently. + *
+ * Useful for certain endpoints (mostly from the Clan section) where you may need to load data about a lot of users. + * + * @param bungieMembershipID The BungieID of the user you want to examine + * @param destinyMembership A JsonObject containing data about the user, usually from a Bungie endpoint + */ public BungieUser(String bungieMembershipID, JsonObject destinyMembership) { super("https://www.bungie.net/Platform/Destiny2/-1/Profile/" + bungieMembershipID + "/LinkedProfiles/?components=200", source -> { return source.getAsJsonObject("Response"); @@ -78,8 +90,11 @@ public BungieUser(String bungieMembershipID, JsonObject destinyMembership) { /** * Used to provide a little more context for what profile the BungieUser class should pull info from - * because a LinkedProfiles request can have multiple platform accounts attached - * You are responsible for checking the getMembershipTypes() to see if that user has a profile on that platform + * because a LinkedProfiles request can have multiple platform accounts attached to it. + *
+ * You are responsible for checking the getMembershipTypes() to see if that user has a profile on that platform. + *
+ * Deprecated since cross save became a thing, likely no longer useful. * * @param bungieMembershipID The bungie Id of the user you want to get information for * @param intendedPlatform The platform that you want to pull information for @@ -94,7 +109,14 @@ public BungieUser(String bungieMembershipID, DestinyPlatform intendedPlatform) { } /** - * A constructor contianing more information which may decrease load times in certain circumstances + * Construct the object will muliple fields from the get-go, thus improving efficiency. + * + * @param bungieMembershipID The bungie Id of the user you want to get information for + * @param displayName The display name of the user in-game + * @param crossSaveOverride The cross save override of the user's Bungie Account + * @param membershipType The membership type of the profile you would like to examine + * @param isPublic Marks if the Bungie Account is public or not + * @see net.dec4234.javadestinyapi.material.user.BungieUser#BungieUser(String, JsonObject) */ public BungieUser(String bungieMembershipID, String displayName, int crossSaveOverride, int membershipType, boolean isPublic) { super("https://www.bungie.net/Platform/Destiny2/-1/Profile/" + bungieMembershipID + "/LinkedProfiles/?components=200", source -> { @@ -107,6 +129,17 @@ public BungieUser(String bungieMembershipID, String displayName, int crossSaveOv this.isPublic = isPublic; } + /** + * Construct the object will muliple fields from the get-go, thus improving efficiency. + * + * @param bungieMembershipID The bungie Id of the user you want to get information for + * @param displayName The display name of the user in-game + * @param globalDisplayName The global display name of the user, overrides all platform specific names + * @param crossSaveOverride The cross save override of the user's Bungie Account + * @param membershipType The membership type of the profile you would like to examine + * @param isPublic Marks if the Bungie Account is public or not + * @see net.dec4234.javadestinyapi.material.user.BungieUser#BungieUser(String, JsonObject) + */ public BungieUser(String bungieMembershipID, String displayName, String globalDisplayName, int crossSaveOverride, int membershipType, boolean isPublic) { super("https://www.bungie.net/Platform/Destiny2/-1/Profile/" + bungieMembershipID + "/LinkedProfiles/?components=200", source -> { return source.getAsJsonObject("Response"); @@ -120,42 +153,56 @@ public BungieUser(String bungieMembershipID, String displayName, String globalDi } /** - * Gets the bungie membership ID of the user + * Gets the static, unqiue BungieID of the user + * + * @return The BungieID of the user */ - public String getID() { return bungieMembershipID; } + public String getID() { + return bungieMembershipID; + } /** - * Determines if the user has any profiles on their account - * Useful to see if a user's account has any data on it + * Determines whether or not the Bungie account has any Destiny profiles associated with it. + *
+ * An account can be empty AKA "invalid" if it exists but does not have any Destiny profiles + * + * @return A boolean representing whether or not the Bungie User is valid */ + @Deprecated public boolean isValidUser() throws APIException { try { return getJO().getAsJsonArray("profiles").size() != 0; - } catch (NullPointerException nullPointerException) { + } catch(NullPointerException nullPointerException) { return false; } } /** - * Gets the display name of the user - * Prefers returning the name of their account on steam, if they have one - * + * Gets the platform specific display name of the user. + *
+ * Not too useful after the introduction of global display names. + *
* Deprecated in favor of getGlobalDisplayName() + * + * @return The platform specifc display name for the current profile + * @see BungieUser#getGlobalDisplayName() */ @Deprecated public String getDisplayName() throws APIException { getJE(); - if (displayName == null) { + if(displayName == null) { displayName = getJE().get("displayName").getAsString(); } return displayName; } /** - * Returns the Global Display Name of the user across all Destiny Platforms - * Should be used instead of getDisplayName() + * Returns the Global Display Name of the user across all Destiny Platforms. + *
* Please note, an empty string will be returned if a user has not logged in since the - * start of Season of The Lost + * start of Season of The Lost. + * + * @return The global display name for this user, which is the same for all connected profiles */ public String getGlobalDisplayName() throws APIException { if (globalDisplayName == null) { @@ -169,8 +216,12 @@ public String getGlobalDisplayName() throws APIException { } /** - * Returns the combined displayname and user discriminator as used in friend requests and user searches - * E.g. dec4234#9904 + * Returns the combined displayname and user discriminator as used in friend requests and user searches. + *
+ * + * @return The full global display name and discriminator for the current user: "dec4234#9904" + * @see BungieUser#getGlobalDisplayName() + * @see BungieUser#getDiscriminator() */ public String getSupplementalDisplayName() throws APIException { if (supplementalDisplayName == null) { @@ -182,7 +233,9 @@ public String getSupplementalDisplayName() throws APIException { /** * Get the discriminator of a user's name - * E.g. "9904" of dec4234#9904 + *
+ * + * @return The discriminator of the user: E.g. "9904" of dec4234#9904 */ public String getDiscriminator() throws APIException { if (discriminator == null) { @@ -193,7 +246,10 @@ public String getDiscriminator() throws APIException { } /** - * Gets the last day this user was seen online + * Gets the last day this user was seen online. + * + * @return The date the last user was seen online + * @see BungieUser#getDaysSinceLastPlayed() */ public Date getLastPlayed() throws APIException { if (lastPlayed == null) { @@ -204,27 +260,45 @@ public Date getLastPlayed() throws APIException { } /** - * Gets a double representing the number of days since the user last played + * Gets a double representing the number of days since the user last played. + * + * @return A double, representing the number of days from the current day to when this user was last seen online + * @see BungieUser#getLastPlayed() */ public double getDaysSinceLastPlayed() throws APIException { DecimalFormat df = new DecimalFormat("0.##"); return Double.parseDouble(df.format((new Date().getTime() - getLastPlayed().getTime()) / 1000.0 / 60.0 / 60.0 / 24.0)); } + /** + * Determines whether the current profile was overriden by cross save + * + * @return A boolean saying if the profile was overriden by cross save + */ public boolean isOverridden() throws APIException { return !isOverridden ? isOverridden = getJE().get("isOverridden").getAsBoolean() : isOverridden; } + /** + * Determines whether the current profile is the primary profile on an account with cross save active + * + * @return A boolean representing whether or not the current profile is a cross save primary + */ public boolean isCrossSavePrimary() throws APIException { return !isCrossSavePrimary ? isCrossSavePrimary = getJE().get("isCrossSavePrimary").getAsBoolean() : isCrossSavePrimary; } + /** + * @return An integer representing the membership type of the profile that overrode this profile from cross save + */ public int getCrossSaveOverride() throws APIException { return crossSaveOverride == -1 ? crossSaveOverride = getJE().get("crossSaveOverride").getAsInt() : crossSaveOverride; } /** - * Get the applicable membership types declared in the response + * Returns all applicable membership types for the current account, due to cross save + * + * @return A list of integers representing the membership types for applicable active profiles */ public ArrayList getApplicableMembershipTypes() throws APIException { if (applicableMembershipTypes.isEmpty()) { @@ -241,7 +315,7 @@ public ArrayList getApplicableMembershipTypes() throws APIException { public ArrayList getMembershipTypes() throws APIException { ArrayList integers = new ArrayList<>(); - for (JsonElement jsonElement : getJO().get("profiles").getAsJsonArray()) { + for(JsonElement jsonElement : getJO().get("profiles").getAsJsonArray()) { integers.add(jsonElement.getAsJsonObject().get("membershipType").getAsInt()); } @@ -270,11 +344,11 @@ public List getCharacters() throws APIException { JsonObject jsonObject = hu.urlRequestGET("https://www.bungie.net/Platform/Destiny2/" + getMembershipType() + "/Profile/" + getID() + "/?components=Profiles,Characters").getAsJsonObject("Response"); JsonArray ja = jsonObject.getAsJsonObject("profile").getAsJsonObject("data").getAsJsonArray("characterIds"); - if (ja == null || ja.size() == 0) { + if(ja == null || ja.size() == 0) { return null; } - for (JsonElement je : ja) { + for(JsonElement je : ja) { String id = je.getAsString(); destinyCharacters.add(new DestinyCharacter(jsonObject.getAsJsonObject("characters").getAsJsonObject("data").getAsJsonObject(id), this, id)); // destinyCharacters.add(new DestinyCharacter(this, je.getAsString())); @@ -284,19 +358,20 @@ public List getCharacters() throws APIException { } /** - * Returns the character under this Bungie account that matches the Class provided - * + * Returns the character under this Bungie account that matches the Class provided. + *
* This works well only if the user has 1 character of each type. If a user has two warlocks * for example, it will throw an IllegalStateException because it cannot choose between them. - * @param destinyClass + * + * @param destinyClass The Destiny class that you want to search for. * @return The character if there is one, and is not a duplicate */ public DestinyCharacter getCharacterOfType(DestinyCharacter.DestinyClass destinyClass) throws APIException { DestinyCharacter toReturn = null; - for (DestinyCharacter destinyCharacter : this.getCharacters()) { - if (destinyCharacter.getD2class() == destinyClass) { - if (toReturn != null) { + for(DestinyCharacter destinyCharacter : this.getCharacters()) { + if(destinyCharacter.getD2class() == destinyClass) { + if(toReturn != null) { throw new IllegalStateException("There is more than one active character on this account with the same Class type. You will not be able to use this method."); } @@ -308,7 +383,7 @@ public DestinyCharacter getCharacterOfType(DestinyCharacter.DestinyClass destiny } /** - * Adds up all of the time played across all characters + * Adds up all of the time played across all characters in minutes * * @return The time played of this user, in minutes */ @@ -321,7 +396,7 @@ public int getTimePlayed() throws APIException { } /** - * Gets the clan that this user is a member of + * Gets the clan that this user is a member of. */ public Clan getClan() throws APIException { if (clan != null) { return clan; } @@ -334,6 +409,7 @@ public Clan getClan() throws APIException { /** * If the user is currently in a clan + * * @return Returns true if the user is a member of a clan */ public boolean isAMemberOfAClan() throws APIException { @@ -350,7 +426,8 @@ public void allowClanInvites(boolean allowInvites) { } /** - * Determines if the user is online + * Determines if the user is online by comparing the last time they logged off and the number of minutes in their current game session + * * @return Returns true if the user is online */ public boolean isOnline() throws APIException { @@ -372,10 +449,10 @@ public boolean isOnline() throws APIException { public ActivityInfo getCurrentActivityInfo() throws APIException { JsonObject data = hu.urlRequestGET("https://www.bungie.net/Platform/Destiny2/" + getMembershipType() + "/Profile/" + getID() + "/?components=204").getAsJsonObject("Response").getAsJsonObject("characterActivities").getAsJsonObject("data"); - for (DestinyCharacter character : getCharacters()) { - if (data.has(character.getCharacterID())) { + for(DestinyCharacter character : getCharacters()) { + if(data.has(character.getCharacterID())) { String hash = data.getAsJsonObject(character.getCharacterID()).get("currentActivityHash").getAsString(); - if (!hash.equals("0")) { + if(!hash.equals("0")) { return new ActivityInfo(hash); } } @@ -385,16 +462,16 @@ public ActivityInfo getCurrentActivityInfo() throws APIException { } /** - * Request to join the specified clan + * Request to join the specified clan, requires OAuth enabled for current user */ public void requestToJoinClan(Clan clan) throws APIException { hu.urlRequestPOSTOauth("https://www.bungie.net/Platform/GroupV2/" + clan.getClanID() + "/Members/Apply/" + getMembershipType() + "/"); } /** - * Get the Destiny profile object to be used to pull most data + * Get the Destiny profile object to be used to pull most data. * Uses the preferred platform profile if it has been declared or - * it will select the first profile in the profiles array + * it will select the first profile in the profiles array. */ public JsonObject getJE() throws APIException { if (je == null) { @@ -407,18 +484,18 @@ public JsonObject getJE() throws APIException { } // Some users may have cross saved a console account to their pc account - for (JsonElement jsonElement : getJO().getAsJsonArray("profilesWithErrors")) { - if (jsonElement.getAsJsonObject().getAsJsonObject("infoCard").get("membershipType").getAsInt() == intendedPlatform) { + for(JsonElement jsonElement : getJO().getAsJsonArray("profilesWithErrors")) { + if(jsonElement.getAsJsonObject().getAsJsonObject("infoCard").get("membershipType").getAsInt() == intendedPlatform) { JsonObject temp = jsonElement.getAsJsonObject().getAsJsonObject("infoCard"); displayName = temp.get("displayName").getAsString(); isPublic = temp.get("isPublic").getAsBoolean(); crossSaveOverride = temp.get("crossSaveOverride").getAsInt(); // If a user has a profileWithErrors we have to get most info from a profile that is not in error - for (JsonElement jsonElement1 : getJO().getAsJsonArray("profiles")) { + for(JsonElement jsonElement1 : getJO().getAsJsonArray("profiles")) { // Does the main profile have the intended platform in its applicablemembershipTypes? - for (JsonElement jsonElement2 : jsonElement1.getAsJsonObject().getAsJsonArray("applicableMembershipTypes")) { - if (jsonElement2.getAsInt() == intendedPlatform) { + for(JsonElement jsonElement2 : jsonElement1.getAsJsonObject().getAsJsonArray("applicableMembershipTypes")) { + if(jsonElement2.getAsInt() == intendedPlatform) { je = jsonElement1.getAsJsonObject(); return je; } @@ -428,8 +505,8 @@ public JsonObject getJE() throws APIException { } } else { // Make sure that the profile has the id for the bungieuser that you want - for (JsonElement jsonElement : getJO().getAsJsonArray("profiles")) { - if (jsonElement.getAsJsonObject().get("membershipId").getAsString().equals(getID())) { + for(JsonElement jsonElement : getJO().getAsJsonArray("profiles")) { + if(jsonElement.getAsJsonObject().get("membershipId").getAsString().equals(getID())) { je = jsonElement.getAsJsonObject(); return je; } @@ -443,6 +520,11 @@ public JsonObject getJE() throws APIException { return je; } + /** + * Set the platform that you intend to pull information for, in case the user has multiple profiles. + * Deprecated due to a lack of use after the introduction of cross save + * @param destinyPlatform The specific platform you want user info for + */ @Deprecated public void setIntendedPlatform(DestinyPlatform destinyPlatform) { intendedPlatform = destinyPlatform.getPlatformCode(); @@ -450,7 +532,7 @@ public void setIntendedPlatform(DestinyPlatform destinyPlatform) { } public InventoryManager getInventoryManager() { - if (inventoryManager == null) { + if(inventoryManager == null) { inventoryManager = new InventoryManager(this); } diff --git a/src/main/java/net/dec4234/javadestinyapi/material/user/DestinyCharacter.java b/src/main/java/net/dec4234/javadestinyapi/material/user/DestinyCharacter.java index ad06555..07c5777 100644 --- a/src/main/java/net/dec4234/javadestinyapi/material/user/DestinyCharacter.java +++ b/src/main/java/net/dec4234/javadestinyapi/material/user/DestinyCharacter.java @@ -25,6 +25,11 @@ import java.util.Date; import java.util.List; +/** + * A DestinyCharacter is a character on a user's account like a Warlock, Titan, or Hunter. + *
+ * There is a lot of character-specific information that is captured within this class. + */ public class DestinyCharacter extends ContentFramework { private BungieUser bungieUser; @@ -221,6 +226,12 @@ public InventoryItem getItemInSlot(InventoryItem.ItemLocation itemLocation) { } */ + /** + * Get a list of inventory items present in this character's inventory. + *
+ * This is an OAUTH action that required inventory reading permission. + * @return A list of inventory items, equipped or unequipped + */ public List getAllItemsInInventory() throws APIException { JsonArray jsonArray = hu.urlRequestGETOauth("https://www.bungie.net/Platform/Destiny2/" + getMembershipType() + "/Profile/" + bungieUser.getID() + "/Character/" + getCharacterID() + "/?components=201").getAsJsonObject("Response").getAsJsonObject("inventory").getAsJsonObject("data").getAsJsonArray("items"); @@ -249,6 +260,12 @@ public List getAllItemsInInventory() throws APIException { return list; } + /** + * TODO: complete + *
+ * This function is currently incomplete. If you would like to contribute, please create a pull request on GitHub + * @return A list of loadouts on this character + */ public List getLoadouts() throws APIException { hu.urlRequestGETOauth(HttpUtils.URL_BASE + "/Destiny2/" + getMembershipType() + "/Profile/" + bungieUser.getID() + "/Character/" + getCharacterID() + "/?components=206,201"); @@ -339,6 +356,9 @@ private Race evaluateRace(String raceHash) throws APIException { return null; } + /** + * Gender of this character. Male or Female + */ public enum Gender { MALE("Male"), FEMALE("Female"); @@ -353,6 +373,9 @@ private Gender(String value) { } + /** + * The class of this character. Hunter, Titan or Warlock + */ public enum DestinyClass { HUNTER("Hunter"), TITAN("Titan"), @@ -367,6 +390,9 @@ private DestinyClass(String value) { public String getValue() { return value; } } + /** + * The race of this character. Awoken, Exo or Human + */ public enum Race { AWOKEN("Awoken"), EXO("Exo"),