diff --git a/BungieSharper.Entities/Common/Entities.Common.Models.cs b/BungieSharper.Entities/Common/Entities.Common.Models.cs index 4d04b4b..fe766f9 100644 --- a/BungieSharper.Entities/Common/Entities.Common.Models.cs +++ b/BungieSharper.Entities/Common/Entities.Common.Models.cs @@ -140,6 +140,12 @@ public class Destiny2CoreSettings [JsonPropertyName("currentRankProgressionHashes")] public IEnumerable CurrentRankProgressionHashes { get; set; } + [JsonPropertyName("insertPlugFreeProtectedPlugItemHashes")] + public IEnumerable InsertPlugFreeProtectedPlugItemHashes { get; set; } + + [JsonPropertyName("insertPlugFreeBlockedSocketTypeHashes")] + public IEnumerable InsertPlugFreeBlockedSocketTypeHashes { get; set; } + [JsonPropertyName("undiscoveredCollectibleImage")] public string UndiscoveredCollectibleImage { get; set; } diff --git a/BungieSharper.Entities/Destiny/Entities.Destiny.HistoricalStats.Definitions.cs b/BungieSharper.Entities/Destiny/Entities.Destiny.HistoricalStats.Definitions.cs index 5a16332..a7e468f 100644 --- a/BungieSharper.Entities/Destiny/Entities.Destiny.HistoricalStats.Definitions.cs +++ b/BungieSharper.Entities/Destiny/Entities.Destiny.HistoricalStats.Definitions.cs @@ -86,7 +86,8 @@ public enum DestinyActivityModeType : int Momentum = 81, Dungeon = 82, Sundial = 83, - TrialsOfOsiris = 84 + TrialsOfOsiris = 84, + Dares = 85 } public class DestinyHistoricalStatsDefinition diff --git a/BungieSharper.Entities/Destiny/Entities.Destiny.Requests.Actions.cs b/BungieSharper.Entities/Destiny/Entities.Destiny.Requests.Actions.cs index c30c620..7c9f594 100644 --- a/BungieSharper.Entities/Destiny/Entities.Destiny.Requests.Actions.cs +++ b/BungieSharper.Entities/Destiny/Entities.Destiny.Requests.Actions.cs @@ -20,6 +20,7 @@ public class DestinyCharacterActionRequest public class DestinyItemActionRequest { + /// The instance ID of the item for this action request. [JsonPropertyName("itemId")] public long ItemId { get; set; } @@ -38,6 +39,7 @@ public class DestinyPostmasterTransferRequest [JsonPropertyName("stackSize")] public int StackSize { get; set; } + /// The instance ID of the item for this action request. [JsonPropertyName("itemId")] public long ItemId { get; set; } @@ -65,6 +67,7 @@ public class DestinyItemStateRequest [JsonPropertyName("state")] public bool State { get; set; } + /// The instance ID of the item for this action request. [JsonPropertyName("itemId")] public long ItemId { get; set; } @@ -127,4 +130,21 @@ public enum DestinySocketArrayType : int Default = 0, Intrinsic = 1 } + + public class DestinyInsertPlugsFreeActionRequest + { + /// The plugs being inserted. + [JsonPropertyName("plug")] + public Destiny.Requests.Actions.DestinyInsertPlugsRequestEntry Plug { get; set; } + + /// The instance ID of the item for this action request. + [JsonPropertyName("itemId")] + public long ItemId { get; set; } + + [JsonPropertyName("characterId")] + public long CharacterId { get; set; } + + [JsonPropertyName("membershipType")] + public BungieMembershipType MembershipType { get; set; } + } } \ No newline at end of file diff --git a/BungieSharper.Entities/Destiny/Entities.Destiny.Requests.cs b/BungieSharper.Entities/Destiny/Entities.Destiny.Requests.cs index 03de521..a96d887 100644 --- a/BungieSharper.Entities/Destiny/Entities.Destiny.Requests.cs +++ b/BungieSharper.Entities/Destiny/Entities.Destiny.Requests.cs @@ -13,6 +13,7 @@ public class DestinyItemTransferRequest [JsonPropertyName("transferToVault")] public bool TransferToVault { get; set; } + /// The instance ID of the item for this action request. [JsonPropertyName("itemId")] public long ItemId { get; set; } diff --git a/BungieSharper.Entities/Destiny/Entities.Destiny.cs b/BungieSharper.Entities/Destiny/Entities.Destiny.cs index f5cef72..baaadfa 100644 --- a/BungieSharper.Entities/Destiny/Entities.Destiny.cs +++ b/BungieSharper.Entities/Destiny/Entities.Destiny.cs @@ -241,7 +241,10 @@ public enum DestinyVendorProgressionType : int Default = 0, /// Progression from ranks in ritual content. For example: Crucible (Shaxx), Gambit (Drifter), and Season 13 Battlegrounds (War Table). - Ritual = 1 + Ritual = 1, + + /// A vendor progression with no seasonal refresh. For example: Xur in the Eternity destination for the 30th Anniversary. + NoSeasonalRefresh = 2 } /// @@ -857,7 +860,9 @@ public enum DestinyGameVersions : int Forsaken = 8, YearTwoAnnualPass = 16, Shadowkeep = 32, - BeyondLight = 64 + BeyondLight = 64, + Anniversary30th = 128, + TheWitchQueen = 256 } /// @@ -1433,7 +1438,10 @@ public enum DestinyVendorItemState : int BestDeal = 32768, /// This indicates that the sale item is popular. - Popular = 65536 + Popular = 65536, + + /// This indicates that the sale item is free. + Free = 131072 } /// diff --git a/BungieSharper.Entities/Exceptions/Entities.Exceptions.cs b/BungieSharper.Entities/Exceptions/Entities.Exceptions.cs index 63d96c2..a99458c 100644 --- a/BungieSharper.Entities/Exceptions/Entities.Exceptions.cs +++ b/BungieSharper.Entities/Exceptions/Entities.Exceptions.cs @@ -424,6 +424,7 @@ public enum PlatformErrorCodes : int IgnoreNotFound = 1007, IgnoreUserGloballyIgnored = 1008, IgnoreUserIgnored = 1009, + TargetUserIgnored = 1010, NotificationSettingInvalid = 1100, PsnApiExpiredAccessToken = 1204, PSNExForbidden = 1205, @@ -808,6 +809,10 @@ public enum PlatformErrorCodes : int ErrorBungieFriendsAlreadyFriends = 3903, ErrorBungieFriendsUnableToRemoveRequest = 3904, ErrorBungieFriendsUnableToRemove = 3905, - ErrorBungieFriendsIdenticalSourceTarget = 3906 + ErrorBungieFriendsIdenticalSourceTarget = 3906, + ErrorBungieFriendsSelf = 3907, + ErrorBungieBlockSelf = 3908, + ErrorBungieFriendsListFull = 3910, + ErrorBungieBlockListFull = 3911 } } \ No newline at end of file diff --git a/BungieSharper.Entities/User/Entities.User.cs b/BungieSharper.Entities/User/Entities.User.cs index 3dad6b2..68878b9 100644 --- a/BungieSharper.Entities/User/Entities.User.cs +++ b/BungieSharper.Entities/User/Entities.User.cs @@ -314,6 +314,21 @@ public class UserSearchResponseDetail public IEnumerable DestinyMemberships { get; set; } } + public class UserSearchPrefixRequest + { + [JsonPropertyName("displayNamePrefix")] + public string DisplayNamePrefix { get; set; } + } + + public class ExactSearchRequest + { + [JsonPropertyName("displayName")] + public string DisplayName { get; set; } + + [JsonPropertyName("displayNameCode")] + public short DisplayNameCode { get; set; } + } + /// /// The set of all email subscription/opt-in settings and definitions. /// diff --git a/BungieSharper.Tests/BaseTests.cs b/BungieSharper.Tests/BaseTests.cs index 0720ef2..f5cbbf2 100644 --- a/BungieSharper.Tests/BaseTests.cs +++ b/BungieSharper.Tests/BaseTests.cs @@ -1,9 +1,5 @@ using BungieSharper.Entities; -using BungieSharper.Entities.Destiny.Definitions; -using System.Collections.Generic; -using System.IO; using System.Linq; -using System.Text.Json; using System.Threading.Tasks; using Xunit; @@ -19,22 +15,22 @@ public BaseTests(TestClientFixture clientFixture) } [Fact] - public async Task GetManifestTask() + public async Task GetManifestTest() { var manifestData = await ClientFixture.TestClient.Api.Destiny2_GetDestinyManifest(); Assert.False(string.IsNullOrEmpty(manifestData.Version)); } [Fact] - public async Task SearchUserTest_Steam() + public async Task PostUserSearchTest() { const BungieMembershipType expectedMembershipType = BungieMembershipType.TigerSteam; const long expectedMembershipId = 4611686018467948914; const string expectedDisplayName = "Cytraen"; const short expectedDisplayNameCode = 2213; - var actualCards = (await ClientFixture.TestClient.Api.Destiny2_SearchDestinyPlayer( - $"{expectedDisplayName}#{expectedDisplayNameCode}", BungieMembershipType.All + var actualCards = (await ClientFixture.TestClient.Api.Destiny2_SearchDestinyPlayerByBungieName( + BungieMembershipType.All, new Entities.User.ExactSearchRequest { DisplayName = expectedDisplayName, DisplayNameCode = expectedDisplayNameCode } )).ToList(); Assert.Equal(4, actualCards.Count); @@ -47,56 +43,5 @@ public async Task SearchUserTest_Steam() Assert.Equal(expectedDisplayName, userCard.BungieGlobalDisplayName); Assert.Equal(expectedDisplayNameCode, userCard.BungieGlobalDisplayNameCode); } - - [Fact] - public async Task DownloadStringAndDeserialize() - { - const string expectedName = "Midnight Coup"; - const string expectedFlavorText = - "The conspirators were too afraid to kill me—rightly so. " + - "I am the beloved father of the people, and the glorious mob would not suffer my death. " + - "My sentence was exile."; - const uint expectedDamageTypeHash = 3373582085; - - var manifestData = await ClientFixture.TestClient.Api.Destiny2_GetDestinyManifest(); - var str = await ClientFixture.TestClient.DownloadString( - manifestData.JsonWorldComponentContentPaths["en"]["DestinyInventoryItemDefinition"] - ); - var inventoryItems = JsonSerializer.Deserialize>(str); - var item = inventoryItems[1128225405]; - - Assert.Equal(expectedName, item.DisplayProperties.Name); - Assert.Equal(expectedFlavorText, item.FlavorText); - Assert.Equal(expectedDamageTypeHash, item.DefaultDamageTypeHash); - } - - [Fact] - public async Task DownloadFileAndDeserialize() - { - const string fileName = "inventoryItems.json"; - const string expectedName = "Midnight Coup"; - const string expectedFlavorText = - "The conspirators were too afraid to kill me—rightly so. " + - "I am the beloved father of the people, and the glorious mob would not suffer my death. " + - "My sentence was exile."; - const uint expectedDamageTypeHash = 3373582085; - - var manifestData = await ClientFixture.TestClient.Api.Destiny2_GetDestinyManifest(); - - await using var fileStream = new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.ReadWrite); - await ClientFixture.TestClient.DownloadFile( - manifestData.JsonWorldComponentContentPaths["en"]["DestinyInventoryItemDefinition"], fileStream - ); - fileStream.Close(); - - var fileText = await File.ReadAllTextAsync(fileName); - - var inventoryItems = JsonSerializer.Deserialize>(fileText); - var item = inventoryItems[1128225405]; - - Assert.Equal(expectedName, item.DisplayProperties.Name); - Assert.Equal(expectedFlavorText, item.FlavorText); - Assert.Equal(expectedDamageTypeHash, item.DefaultDamageTypeHash); - } } } \ No newline at end of file diff --git a/BungieSharper.Tests/TestClient.cs b/BungieSharper.Tests/TestClient.cs index bb7fb21..83f0be7 100644 --- a/BungieSharper.Tests/TestClient.cs +++ b/BungieSharper.Tests/TestClient.cs @@ -13,7 +13,7 @@ public TestClientFixture() { ApiKey = Environment.GetEnvironmentVariable("TEST_BUNGIE_API_KEY") ?? throw new NullReferenceException(), UserAgent = $"BungieSharper.Tests/{typeof(TestClientFixture).Assembly.GetName().Version!.ToString(3)} (+github.com/ashakoor/BungieSharper)", - RateLimit = 50 + RateLimit = 10 }; TestClient = new BungieApiClient(config); diff --git a/BungieSharper/Endpoints/Destiny2.cs b/BungieSharper/Endpoints/Destiny2.cs index 61a81d4..ef55b26 100644 --- a/BungieSharper/Endpoints/Destiny2.cs +++ b/BungieSharper/Endpoints/Destiny2.cs @@ -42,15 +42,14 @@ public partial class Endpoints /// /// Returns a list of Destiny memberships given a global Bungie Display Name. This method will hide overridden memberships due to cross save. /// - /// The full bungie global display name to look up, include the # and the code at the end. This is an exact match lookup. /// A valid non-BungieNet membership type, or All. Indicates which memberships to return. You probably want this set to All. /// The OAuth access token to autheticate the request with. /// The to observe. - public Task> Destiny2_SearchDestinyPlayer(string displayName, Entities.BungieMembershipType membershipType, string? authToken = null, CancellationToken cancelToken = default) + public Task> Destiny2_SearchDestinyPlayerByBungieName(Entities.BungieMembershipType membershipType, Entities.User.ExactSearchRequest requestBody, string? authToken = null, CancellationToken cancelToken = default) { return _apiAccessor.ApiRequestAsync>( - new Uri($"Destiny2/SearchDestinyPlayer/{membershipType}/{Uri.EscapeDataString(displayName)}/", UriKind.Relative), - null, HttpMethod.Get, authToken, cancelToken + new Uri($"Destiny2/SearchDestinyPlayerByBungieName/{membershipType}/", UriKind.Relative), + new StringContent(JsonSerializer.Serialize(requestBody), System.Text.Encoding.UTF8, "application/json"), HttpMethod.Post, authToken, cancelToken ); } @@ -313,6 +312,20 @@ public Task Destiny2_SetQuestTrackedState(Entities.Destiny.Requests.Actions ); } + /// + /// Insert a 'free' plug into an item's socket. This does not require 'Advanced Write Action' authorization and is available to 3rd-party apps, but will only work on 'free and reversible' socket actions (Perks, Armor Mods, Shaders, Ornaments, etc.). You must have a valid Destiny Account, and the character must either be in a social space, in orbit, or offline. + /// Requires OAuth2 scope(s): MoveEquipDestinyItems + /// + /// The OAuth access token to autheticate the request with. + /// The to observe. + public Task Destiny2_InsertSocketPlugFree(Entities.Destiny.Requests.Actions.DestinyInsertPlugsFreeActionRequest requestBody, string? authToken = null, CancellationToken cancelToken = default) + { + return _apiAccessor.ApiRequestAsync( + new Uri($"Destiny2/Actions/Items/InsertSocketPlugFree/", UriKind.Relative), + new StringContent(JsonSerializer.Serialize(requestBody), System.Text.Encoding.UTF8, "application/json"), HttpMethod.Post, authToken, cancelToken + ); + } + /// /// Gets the available post game carnage report for the activity ID. /// diff --git a/BungieSharper/Endpoints/User.cs b/BungieSharper/Endpoints/User.cs index b923fa9..76934f5 100644 --- a/BungieSharper/Endpoints/User.cs +++ b/BungieSharper/Endpoints/User.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Net.Http; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; @@ -95,7 +96,7 @@ public partial class Endpoints } /// - /// Given the prefix of a global display name, returns all users who share that name. + /// [OBSOLETE] Do not use this to search users, use SearchByGlobalNamePost instead. /// /// The display name prefix you're looking for. /// The zero-based page of results you desire. @@ -108,5 +109,19 @@ public partial class Endpoints null, HttpMethod.Get, authToken, cancelToken ); } + + /// + /// Given the prefix of a global display name, returns all users who share that name. + /// + /// The zero-based page of results you desire. + /// The OAuth access token to autheticate the request with. + /// The to observe. + public Task User_SearchByGlobalNamePost(int page, Entities.User.UserSearchPrefixRequest requestBody, string? authToken = null, CancellationToken cancelToken = default) + { + return _apiAccessor.ApiRequestAsync( + new Uri($"User/Search/GlobalName/{page}/", UriKind.Relative), + new StringContent(JsonSerializer.Serialize(requestBody), System.Text.Encoding.UTF8, "application/json"), HttpMethod.Post, authToken, cancelToken + ); + } } } \ No newline at end of file