diff --git a/.gitignore b/.gitignore index 4e1cd4a5..ec658c42 100644 --- a/.gitignore +++ b/.gitignore @@ -262,4 +262,8 @@ paket-files/ # Python Tools for Visual Studio (PTVS) __pycache__/ -*.pyc \ No newline at end of file +*.pyc + +# binary files and texts notes +**/*.bin +**/*.txt \ No newline at end of file diff --git a/InstaSharper.Examples/InstaSharper.Examples.csproj b/InstaSharper.Examples/InstaSharper.Examples.csproj index 02341bb4..2cb95bf7 100644 --- a/InstaSharper.Examples/InstaSharper.Examples.csproj +++ b/InstaSharper.Examples/InstaSharper.Examples.csproj @@ -34,7 +34,7 @@ 4 - + False ..\InstaSharper\bin\release\net452\InstaSharper.dll @@ -52,10 +52,12 @@ + + diff --git a/InstaSharper.Examples/Program.cs b/InstaSharper.Examples/Program.cs index 724504e0..b5c11fb3 100644 --- a/InstaSharper.Examples/Program.cs +++ b/InstaSharper.Examples/Program.cs @@ -1,10 +1,10 @@ using System; using System.Collections.Generic; +using System.IO; using System.Threading.Tasks; using InstaSharper.API; using InstaSharper.API.Builder; using InstaSharper.Classes; -using InstaSharper.Classes.Android.DeviceInfo; using InstaSharper.Examples.Samples; using InstaSharper.Logger; @@ -34,52 +34,79 @@ public static async Task MainAsync() var userSession = new UserSessionData { UserName = "username", - Password = "password" + Password = "password" }; // create new InstaApi instance using Builder - var device = AndroidDeviceGenerator.GetByName(AndroidDevices.SAMSUNG_NOTE3); - var requestMessage = ApiRequestMessage.FromDevice(device); _instaApi = InstaApiBuilder.CreateBuilder() .SetUser(userSession) - .SetApiRequestMessage(requestMessage) - .UseLogger(new DebugLogger(LogLevel.Info)) // use logger for requests and debug messages + .UseLogger(new DebugLogger(LogLevel.Exceptions)) // use logger for requests and debug messages .SetRequestDelay(TimeSpan.FromSeconds(2)) .Build(); - // login - Console.WriteLine($"Logging in as {userSession.UserName}"); - var logInResult = await _instaApi.LoginAsync(); - if (!logInResult.Succeeded) + + const string stateFile = "state.bin"; + try { - Console.WriteLine($"Unable to login: {logInResult.Info.Message}"); + if (File.Exists(stateFile)) + { + Console.WriteLine("Loading state from file"); + Stream fs = File.OpenRead(stateFile); + fs.Seek(0, SeekOrigin.Begin); + _instaApi.LoadStateDataFromStream(fs); + } } - else + catch (Exception e) { - Console.WriteLine("Press 1 to start basic demo samples"); - Console.WriteLine("Press 2 to start upload photo demo sample"); - Console.WriteLine("Press 3 to start comment media demo sample"); - Console.WriteLine("Press 4 to start stories demo sample"); - Console.WriteLine("Press 5 to start demo with saving state of API instance"); - Console.WriteLine("Press 6 to start messaging demo sample"); + Console.WriteLine(e); + } - var samplesMap = new Dictionary + if (!_instaApi.IsUserAuthenticated) + { + // login + Console.WriteLine($"Logging in as {userSession.UserName}"); + var logInResult = await _instaApi.LoginAsync(); + if (!logInResult.Succeeded) { - [ConsoleKey.D1] = new Basics(_instaApi), - [ConsoleKey.D2] = new UploadPhoto(_instaApi), - [ConsoleKey.D3] = new CommentMedia(_instaApi), - [ConsoleKey.D4] = new Stories(_instaApi), - [ConsoleKey.D5] = new SaveLoadState(_instaApi), - [ConsoleKey.D6] = new Messaging(_instaApi) - }; - var key = Console.ReadKey(); - Console.WriteLine(Environment.NewLine); - if (samplesMap.ContainsKey(key.Key)) - await samplesMap[key.Key].DoShow(); - Console.WriteLine("Done. Press esc key to exit..."); - - key = Console.ReadKey(); - return key.Key == ConsoleKey.Escape; + Console.WriteLine($"Unable to login: {logInResult.Info.Message}"); + return false; + } + } + var state = _instaApi.GetStateDataAsStream(); + using (var fileStream = File.Create(stateFile)) + { + state.Seek(0, SeekOrigin.Begin); + state.CopyTo(fileStream); } + + Console.WriteLine("Press 1 to start basic demo samples"); + Console.WriteLine("Press 2 to start upload photo demo sample"); + Console.WriteLine("Press 3 to start comment media demo sample"); + Console.WriteLine("Press 4 to start stories demo sample"); + Console.WriteLine("Press 5 to start demo with saving state of API instance"); + Console.WriteLine("Press 6 to start messaging demo sample"); + Console.WriteLine("Press 7 to start location demo sample"); + Console.WriteLine("Press 8 to start collections demo sample"); + + var samplesMap = new Dictionary + { + [ConsoleKey.D1] = new Basics(_instaApi), + [ConsoleKey.D2] = new UploadPhoto(_instaApi), + [ConsoleKey.D3] = new CommentMedia(_instaApi), + [ConsoleKey.D4] = new Stories(_instaApi), + [ConsoleKey.D5] = new SaveLoadState(_instaApi), + [ConsoleKey.D6] = new Messaging(_instaApi), + [ConsoleKey.D7] = new LocationSample(_instaApi), + [ConsoleKey.D8] = new CollectionSample(_instaApi) + + }; + var key = Console.ReadKey(); + Console.WriteLine(Environment.NewLine); + if (samplesMap.ContainsKey(key.Key)) + await samplesMap[key.Key].DoShow(); + Console.WriteLine("Done. Press esc key to exit..."); + + key = Console.ReadKey(); + return key.Key == ConsoleKey.Escape; } catch (Exception ex) { @@ -87,8 +114,9 @@ public static async Task MainAsync() } finally { - var logoutResult = Task.Run(() => _instaApi.LogoutAsync()).GetAwaiter().GetResult(); - if (logoutResult.Succeeded) Console.WriteLine("Logout succeed"); + // perform that if user needs to logged out + // var logoutResult = Task.Run(() => _instaApi.LogoutAsync()).GetAwaiter().GetResult(); + // if (logoutResult.Succeeded) Console.WriteLine("Logout succeed"); } return false; } diff --git a/InstaSharper.Examples/Samples/Basics.cs b/InstaSharper.Examples/Samples/Basics.cs index bcb21e24..a3624496 100644 --- a/InstaSharper.Examples/Samples/Basics.cs +++ b/InstaSharper.Examples/Samples/Basics.cs @@ -2,6 +2,7 @@ using System.Linq; using System.Threading.Tasks; using InstaSharper.API; +using InstaSharper.Classes; using InstaSharper.Examples.Utils; namespace InstaSharper.Examples.Samples @@ -27,16 +28,19 @@ public async Task DoShow() Console.WriteLine( $"Logged in: username - {currentUser.Value.UserName}, full name - {currentUser.Value.FullName}"); - // get self followers - var followers = await _instaApi.GetUserFollowersAsync(currentUser.Value.UserName, 5); - Console.WriteLine($"Count of followers [{currentUser.Value.UserName}]:{followers.Value.Count}"); + // get followers of user 'elonmusk' + var followers = await _instaApi.GetUserFollowersAsync("elonmusk", + PaginationParameters.MaxPagesToLoad(5) + .StartFromId("AQAC8w90POWyM7zMjHWmO9vsZNL_TuLp6FR506_C_y3fUAjlCclrIDI2RdSGvur5UjLrq4Cq7NJN8QUhHG-vpbT6pCLB5X9crDxBOHUEuNJ4fA")); + Console.WriteLine($"Count of followers [elonmusk]:{followers.Value.Count}"); + Console.WriteLine($"Next id will be: '{followers.Value.NextId}'"); // get self folling - var following = await _instaApi.GetUserFollowingAsync(currentUser.Value.UserName, 5); + var following = await _instaApi.GetUserFollowingAsync(currentUser.Value.UserName, PaginationParameters.MaxPagesToLoad(5)); Console.WriteLine($"Count of following [{currentUser.Value.UserName}]:{following.Value.Count}"); // get self user's media, latest 5 pages - var currentUserMedia = await _instaApi.GetUserMediaAsync(currentUser.Value.UserName, 5); + var currentUserMedia = await _instaApi.GetUserMediaAsync(currentUser.Value.UserName, PaginationParameters.MaxPagesToLoad(5)); if (currentUserMedia.Succeeded) { Console.WriteLine($"Media count [{currentUser.Value.UserName}]: {currentUserMedia.Value.Count}"); @@ -45,7 +49,7 @@ public async Task DoShow() } //get user time line feed, latest 5 pages - var userFeed = await _instaApi.GetUserTimelineFeedAsync(5); + var userFeed = await _instaApi.GetUserTimelineFeedAsync(PaginationParameters.MaxPagesToLoad(5)); if (userFeed.Succeeded) { Console.WriteLine( @@ -62,7 +66,7 @@ public async Task DoShow() } // get tag feed, latest 5 pages - var tagFeed = await _instaApi.GetTagFeedAsync("quadcopter", 5); + var tagFeed = await _instaApi.GetTagFeedAsync("quadcopter", PaginationParameters.MaxPagesToLoad(5)); if (tagFeed.Succeeded) { Console.WriteLine( diff --git a/InstaSharper.Examples/Samples/CollectionSample.cs b/InstaSharper.Examples/Samples/CollectionSample.cs new file mode 100644 index 00000000..210f9fb1 --- /dev/null +++ b/InstaSharper.Examples/Samples/CollectionSample.cs @@ -0,0 +1,27 @@ +using System; +using System.Threading.Tasks; +using InstaSharper.API; + +namespace InstaSharper.Examples.Samples +{ + internal class CollectionSample : IDemoSample + { + private readonly IInstaApi _instaApi; + + public CollectionSample(IInstaApi instaApi) + { + _instaApi = instaApi; + } + + public async Task DoShow() + { + // get all collections of current user + var collections = await _instaApi.GetCollectionsAsync(); + Console.WriteLine($"Loaded {collections.Value.Items.Count} collections for current user"); + foreach (var instaCollection in collections.Value.Items) + { + Console.WriteLine($"Collection: name={instaCollection.CollectionName}, id={instaCollection.CollectionId}"); + } + } + } +} \ No newline at end of file diff --git a/InstaSharper.Examples/Samples/LocationSample.cs b/InstaSharper.Examples/Samples/LocationSample.cs new file mode 100644 index 00000000..ed67743e --- /dev/null +++ b/InstaSharper.Examples/Samples/LocationSample.cs @@ -0,0 +1,37 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using InstaSharper.API; +using InstaSharper.Classes; + +namespace InstaSharper.Examples.Samples +{ + internal class LocationSample : IDemoSample + { + private readonly IInstaApi _instaApi; + + public LocationSample(IInstaApi instaApi) + { + _instaApi = instaApi; + } + + public async Task DoShow() + { + // search for related locations near location with latitude = 55.753923, logitude = 37.620940 + // additionaly you can specify search query or just empty string + var result = await _instaApi.SearchLocation(55.753923, 37.620940, "square"); + Console.WriteLine($"Loaded {result.Value.Count} locations"); + var firstLocation = result.Value?.FirstOrDefault(); + if(firstLocation == null) + return; + Console.WriteLine($"Loading feed for location: name={firstLocation.Name}; id={firstLocation.ExternalId}."); + + var locationFeed = + await _instaApi.GetLocationFeed(long.Parse(firstLocation.ExternalId), PaginationParameters.MaxPagesToLoad(5)); + + Console.WriteLine(locationFeed.Succeeded + ? $"Loaded {locationFeed.Value.Medias?.Count} medias for location, total location medias: {locationFeed.Value.MediaCount}" + : $"Unable to load location '{firstLocation.Name}' feed"); + } + } +} \ No newline at end of file diff --git a/InstaSharper.Examples/Samples/Messaging.cs b/InstaSharper.Examples/Samples/Messaging.cs index ac68eb2c..d7d353a7 100644 --- a/InstaSharper.Examples/Samples/Messaging.cs +++ b/InstaSharper.Examples/Samples/Messaging.cs @@ -22,8 +22,8 @@ public async Task DoShow() Console.WriteLine("Unable to get ranked recipients"); return; } - Console.WriteLine($"Got {recipientsResult.Value.Items.Count} ranked threads"); - foreach (var thread in recipientsResult.Value.Items) + Console.WriteLine($"Got {recipientsResult.Value.Threads.Count} ranked threads"); + foreach (var thread in recipientsResult.Value.Threads) Console.WriteLine($"Threadname: {thread.ThreadTitle}, users: {thread.Users.Count}"); var inboxThreads = await _instaApi.GetDirectInboxAsync(); @@ -36,9 +36,14 @@ public async Task DoShow() foreach (var thread in inboxThreads.Value.Inbox.Threads) Console.WriteLine($"Threadname: {thread.Title}, users: {thread.Users.Count}"); var firstThread = inboxThreads.Value.Inbox.Threads.FirstOrDefault(); + // send message to specific thread var sendMessageResult = await _instaApi.SendDirectMessage($"{firstThread.Users.FirstOrDefault()?.Pk}", firstThread.ThreadId, "test"); Console.WriteLine(sendMessageResult.Succeeded ? "Message sent" : "Unable to send message"); + + // just send message to user (thread not specified) + sendMessageResult = await _instaApi.SendDirectMessage($"{firstThread.Users.FirstOrDefault()?.Pk}", string.Empty , "one more test"); + Console.WriteLine(sendMessageResult.Succeeded ? "Message sent" : "Unable to send message"); } } } \ No newline at end of file diff --git a/InstaSharper.Examples/Samples/SaveLoadState.cs b/InstaSharper.Examples/Samples/SaveLoadState.cs index 990da146..3430ed01 100644 --- a/InstaSharper.Examples/Samples/SaveLoadState.cs +++ b/InstaSharper.Examples/Samples/SaveLoadState.cs @@ -2,6 +2,7 @@ using System.Threading.Tasks; using InstaSharper.API; using InstaSharper.API.Builder; +using InstaSharper.Classes; namespace InstaSharper.Examples.Samples { @@ -25,6 +26,7 @@ public async Task DoShow() Console.WriteLine($"Got current user: {result.Value.UserName} using existing API instance"); var stream = _instaApi.GetStateDataAsStream(); var anotherInstance = InstaApiBuilder.CreateBuilder() + .SetUser(UserSessionData.Empty) .SetRequestDelay(TimeSpan.FromSeconds(2)) .Build(); anotherInstance.LoadStateDataFromStream(stream); diff --git a/InstaSharper.Tests/Classes/AuthenticatedTestFixture.cs b/InstaSharper.Tests/Classes/AuthenticatedTestFixture.cs index e6a3cabd..5f85e717 100644 --- a/InstaSharper.Tests/Classes/AuthenticatedTestFixture.cs +++ b/InstaSharper.Tests/Classes/AuthenticatedTestFixture.cs @@ -1,4 +1,5 @@ using System; +using System.IO; using System.Threading.Tasks; using InstaSharper.API; using InstaSharper.Classes; @@ -6,33 +7,48 @@ namespace InstaSharper.Tests.Classes { - public class AuthenticatedTestFixture : IDisposable + public class AuthenticatedTestFixture { private readonly string _password = Environment.GetEnvironmentVariable("instaapiuserpassword"); private readonly string _username = "alex_codegarage"; public AuthenticatedTestFixture() { - ApiInstance = TestHelpers.GetDefaultInstaApiInstance(new UserSessionData + ApiInstance = + TestHelpers.GetDefaultInstaApiInstance(UserSessionData.ForUsername(_username).WithPassword(_password)); + const string stateFile = "state.bin"; + try { - UserName = _username, - Password = _password - }); + if (File.Exists(stateFile)) + { + Stream fs = File.OpenRead(stateFile); + fs.Seek(0, SeekOrigin.Begin); + ApiInstance.LoadStateDataFromStream(fs); + if (ApiInstance.IsUserAuthenticated) + return; + } + } + catch (Exception e) + { + Console.WriteLine(e); + } var loginTask = Task.Run(ApiInstance.LoginAsync); + if (!loginTask.Wait(TimeSpan.FromSeconds(30))) throw new Exception($"Unable to login, user: {_username}, password: {_password}."); + + if (!loginTask.Result.Succeeded) return; + var state = ApiInstance.GetStateDataAsStream(); + using (var fileStream = File.Create(stateFile)) + { + state.Seek(0, SeekOrigin.Begin); + state.CopyTo(fileStream); + } } public IInstaApi ApiInstance { get; } - public void Dispose() - { - var logoutTask = Task.Run(ApiInstance.LogoutAsync); - if (!logoutTask.Wait(TimeSpan.FromSeconds(10))) - throw new Exception($"Not able to logout, user: {_username}, password: {_password}."); - } - public string GetUsername() { return _username; diff --git a/InstaSharper.Tests/Endpoints/CollectionTest.cs b/InstaSharper.Tests/Endpoints/CollectionTest.cs new file mode 100644 index 00000000..d5cb2a5c --- /dev/null +++ b/InstaSharper.Tests/Endpoints/CollectionTest.cs @@ -0,0 +1,68 @@ +using InstaSharper.Tests.Classes; +using Xunit; + +namespace InstaSharper.Tests.Endpoints +{ + [Trait("Category", "Endpoint")] + public class CollectionTest : IClassFixture + { + public CollectionTest(AuthenticatedTestFixture authInfo) + { + _authInfo = authInfo; + } + + private readonly AuthenticatedTestFixture _authInfo; + + [Theory] + [InlineData(17913091552048277)] + public async void GetCollectionByIdTest(long collectionId) + { + Assert.True(_authInfo.ApiInstance.IsUserAuthenticated); + var media = await _authInfo.ApiInstance.GetCollectionAsync(collectionId); + Assert.NotNull(media); + } + + [Theory] + [InlineData("New collection")] + public async void CreateCollectionTest(string collectionName) + { + Assert.True(_authInfo.ApiInstance.IsUserAuthenticated); + var newCollection = await _authInfo.ApiInstance.CreateCollectionAsync(collectionName); + var collectionList = await _authInfo.ApiInstance.GetCollectionsAsync(); + + Assert.NotNull(newCollection.Value); + Assert.True(newCollection.Value.CollectionName == collectionName); + Assert.NotNull(collectionList.Value); + } + + [Theory] + [InlineData(17913091552048277)] + public async void AddItemsToCollectionTest(long collectionId) + { + Assert.True(_authInfo.ApiInstance.IsUserAuthenticated); + var mediaItems = new[] {"1658893120999767931"}; + var result = await _authInfo.ApiInstance.AddItemsToCollectionAsync(collectionId, mediaItems); + + Assert.True(result.Succeeded); + Assert.NotNull(result.Value); + Assert.Equal(result.Value.CollectionId, collectionId); + } + + [Theory] + [InlineData(17913091552048277)] + public async void DeleteCollectionTest(long collectionId) + { + Assert.True(_authInfo.ApiInstance.IsUserAuthenticated); + var media = await _authInfo.ApiInstance.DeleteCollectionAsync(collectionId); + Assert.True(media.Succeeded); + } + + [Fact] + public async void GetCollectionsTest() + { + Assert.True(_authInfo.ApiInstance.IsUserAuthenticated); + var collections = await _authInfo.ApiInstance.GetCollectionsAsync(); + Assert.NotNull(collections); + } + } +} \ No newline at end of file diff --git a/InstaSharper.Tests/Endpoints/FeedTest.cs b/InstaSharper.Tests/Endpoints/FeedTest.cs index 7371f2be..b723aed6 100644 --- a/InstaSharper.Tests/Endpoints/FeedTest.cs +++ b/InstaSharper.Tests/Endpoints/FeedTest.cs @@ -1,4 +1,5 @@ using System.Linq; +using InstaSharper.Classes; using InstaSharper.Tests.Classes; using Xunit; @@ -20,7 +21,7 @@ public async void GetTagFeedTest(string tag) { Assert.True(_authInfo.ApiInstance.IsUserAuthenticated); - var result = await _authInfo.ApiInstance.GetTagFeedAsync(tag, 10); + var result = await _authInfo.ApiInstance.GetTagFeedAsync(tag, PaginationParameters.MaxPagesToLoad(10)); var tagFeed = result.Value; var anyMediaDuplicate = tagFeed.Medias.GroupBy(x => x.Code).Any(g => g.Count() > 1); var anyStoryDuplicate = tagFeed.Stories.GroupBy(x => x.Id).Any(g => g.Count() > 1); @@ -38,7 +39,7 @@ public async void GetUserTagFeedTest(string username) { Assert.True(_authInfo.ApiInstance.IsUserAuthenticated); - var result = await _authInfo.ApiInstance.GetUserTagsAsync(username, 5); + var result = await _authInfo.ApiInstance.GetUserTagsAsync(username, PaginationParameters.MaxPagesToLoad(5)); var tagFeed = result.Value; var anyMediaDuplicate = tagFeed.GroupBy(x => x.Code).Any(g => g.Count() > 1); //assert @@ -48,7 +49,6 @@ public async void GetUserTagFeedTest(string username) } [Theory] - [InlineData(260955581)] [InlineData(267685466)] [InlineData(466579064)] public async void GetUserReelFeedTest(long userPk) @@ -66,7 +66,7 @@ public async void GetUserReelFeedTest(long userPk) public async void ExploreTest() { Assert.True(_authInfo.ApiInstance.IsUserAuthenticated); - var result = await _authInfo.ApiInstance.GetExploreFeedAsync(3); + var result = await _authInfo.ApiInstance.GetExploreFeedAsync(PaginationParameters.MaxPagesToLoad(5)); var exploreGeed = result.Value; var anyMediaDuplicate = exploreGeed.Medias.GroupBy(x => x.Code).Any(g => g.Count() > 1); //assert @@ -80,7 +80,8 @@ public async void GetFollowingRecentActivityFeedTest() { Assert.True(_authInfo.ApiInstance.IsUserAuthenticated); - var getFeedResult = await _authInfo.ApiInstance.GetFollowingRecentActivityAsync(5); + var getFeedResult = + await _authInfo.ApiInstance.GetFollowingRecentActivityAsync(PaginationParameters.MaxPagesToLoad(5)); var folloowingRecentFeed = getFeedResult.Value; //assert @@ -94,7 +95,8 @@ public async void GetRecentActivityFeedTest() { Assert.True(_authInfo.ApiInstance.IsUserAuthenticated); - var getFeedResult = await _authInfo.ApiInstance.GetRecentActivityAsync(5); + var getFeedResult = + await _authInfo.ApiInstance.GetRecentActivityAsync(PaginationParameters.MaxPagesToLoad(5)); var ownRecentFeed = getFeedResult.Value; //assert Assert.True(getFeedResult.Succeeded); @@ -107,7 +109,8 @@ public async void GetUserFeedTest() { Assert.True(_authInfo.ApiInstance.IsUserAuthenticated); - var getFeedResult = await _authInfo.ApiInstance.GetUserTimelineFeedAsync(1); + var getFeedResult = + await _authInfo.ApiInstance.GetUserTimelineFeedAsync(PaginationParameters.MaxPagesToLoad(3)); var feed = getFeedResult.Value; var anyDuplicate = feed.Medias.GroupBy(x => x.Code).Any(g => g.Count() > 1); var anyStoryDuplicate = feed.Stories.GroupBy(x => x.Id).Any(g => g.Count() > 1); @@ -124,7 +127,7 @@ public async void GetUserLikeFeedTest() { Assert.True(_authInfo.ApiInstance.IsUserAuthenticated); - var getFeedResult = await _authInfo.ApiInstance.GetLikeFeedAsync(2); + var getFeedResult = await _authInfo.ApiInstance.GetLikeFeedAsync(PaginationParameters.MaxPagesToLoad(5)); var feed = getFeedResult.Value; var anyDuplicate = feed.GroupBy(x => x.Code).Any(g => g.Count() > 1); diff --git a/InstaSharper.Tests/Endpoints/FollowersTest.cs b/InstaSharper.Tests/Endpoints/FollowersTest.cs index 9eb78fcc..e108737c 100644 --- a/InstaSharper.Tests/Endpoints/FollowersTest.cs +++ b/InstaSharper.Tests/Endpoints/FollowersTest.cs @@ -1,4 +1,5 @@ using System.Linq; +using InstaSharper.Classes; using InstaSharper.Tests.Classes; using Xunit; @@ -20,7 +21,8 @@ public async void GetUserFollowersTest(string username) { Assert.True(_authInfo.ApiInstance.IsUserAuthenticated); - var result = await _authInfo.ApiInstance.GetUserFollowersAsync(username, 5); + var result = + await _authInfo.ApiInstance.GetUserFollowersAsync(username, PaginationParameters.MaxPagesToLoad(5)); var followers = result.Value; var anyDuplicate = followers.GroupBy(x => x.Pk).Any(g => g.Count() > 1); @@ -36,7 +38,8 @@ public async void GetUserFollowingTest(string username) { Assert.True(_authInfo.ApiInstance.IsUserAuthenticated); - var result = await _authInfo.ApiInstance.GetUserFollowingAsync(username, 10); + var result = + await _authInfo.ApiInstance.GetUserFollowingAsync(username, PaginationParameters.MaxPagesToLoad(5)); var followings = result.Value; var anyDuplicate = followings.GroupBy(x => x.Pk).Any(g => g.Count() > 1); @@ -62,12 +65,29 @@ public async void FollowUnfollowUserTest(long userId) Assert.False(unFollowResult.Value.Following); } + [Theory] + [InlineData(196754384)] + public async void BlockUnblockUserTest(long userId) + { + Assert.True(_authInfo.ApiInstance.IsUserAuthenticated); + + var blockResult = await _authInfo.ApiInstance.BlockUserAsync(userId); + var unBlockResult = await _authInfo.ApiInstance.UnBlockUserAsync(userId); + //assert + Assert.True(blockResult.Succeeded); + Assert.True(unBlockResult.Succeeded); + + Assert.True(blockResult.Value.Blocking); + Assert.False(unBlockResult.Value.Blocking); + } + [Fact] public async void GetCurrentUserFollwersTest() { Assert.True(_authInfo.ApiInstance.IsUserAuthenticated); - var result = await _authInfo.ApiInstance.GetCurrentUserFollowersAsync(); + var result = + await _authInfo.ApiInstance.GetCurrentUserFollowersAsync(PaginationParameters.MaxPagesToLoad(5)); var followers = result.Value; //assert Assert.True(result.Succeeded); diff --git a/InstaSharper.Tests/Endpoints/LocationTest.cs b/InstaSharper.Tests/Endpoints/LocationTest.cs new file mode 100644 index 00000000..703e9334 --- /dev/null +++ b/InstaSharper.Tests/Endpoints/LocationTest.cs @@ -0,0 +1,50 @@ +using System.Linq; +using InstaSharper.Classes; +using InstaSharper.Tests.Classes; +using Xunit; + +namespace InstaSharper.Tests.Endpoints +{ + [Trait("Category", "Endpoint")] + public class LocationTest : IClassFixture + { + private readonly AuthenticatedTestFixture _authInfo; + + public LocationTest(AuthenticatedTestFixture authInfo) + { + _authInfo = authInfo; + } + + [Theory] + [InlineData("winter")] + public async void SearchLocation(string searchQuery) + { + Assert.True(_authInfo.ApiInstance.IsUserAuthenticated); + + var result = await _authInfo.ApiInstance.SearchLocation(59.9384401, 30.3162374, searchQuery); + + //assert + Assert.True(result.Succeeded); + Assert.NotNull(result.Value); + Assert.True(result.Value.Any(location => location.Name.ToLowerInvariant().Contains(searchQuery))); + } + + [Theory] + [InlineData(109408589078990)] + public async void GetLocationFeed(long locationId) + { + Assert.True(_authInfo.ApiInstance.IsUserAuthenticated); + + var result = + await _authInfo.ApiInstance.GetLocationFeed(locationId, PaginationParameters.MaxPagesToLoad(15)); + var locationFeed = result.Value; + var anyMediaDuplicate = locationFeed.Medias.GroupBy(x => x.Code).Any(g => g.Count() > 1); + var anyRankedDuplicate = locationFeed.RankedMedias.GroupBy(x => x.Code).Any(g => g.Count() > 1); + //assert + Assert.True(result.Succeeded); + Assert.NotNull(result.Value); + Assert.False(anyMediaDuplicate); + Assert.False(anyRankedDuplicate); + } + } +} \ No newline at end of file diff --git a/InstaSharper.Tests/Endpoints/MediaTest.cs b/InstaSharper.Tests/Endpoints/MediaTest.cs index ee5d8fc2..9bda3b4e 100644 --- a/InstaSharper.Tests/Endpoints/MediaTest.cs +++ b/InstaSharper.Tests/Endpoints/MediaTest.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using InstaSharper.Classes; using InstaSharper.Classes.Models; using InstaSharper.Tests.Classes; using Xunit; @@ -41,7 +42,8 @@ public async void GetMediaLikersTest(string mediaId) public async void GetMediaCommentsTest(string mediaId) { Assert.True(_authInfo.ApiInstance.IsUserAuthenticated); - var comments = await _authInfo.ApiInstance.GetMediaCommentsAsync(mediaId, 3); + var comments = + await _authInfo.ApiInstance.GetMediaCommentsAsync(mediaId, PaginationParameters.MaxPagesToLoad(5)); var anyDuplicate = comments.Value.Comments.GroupBy(x => x.Pk).Any(g => g.Count() > 1); @@ -50,13 +52,14 @@ public async void GetMediaCommentsTest(string mediaId) } [Theory] - [InlineData("alex_codegarage")] + [InlineData("microsoftstore")] public async void GetUserMediaListTest(string userToFetch) { Assert.True(_authInfo.ApiInstance.IsUserAuthenticated); var random = new Random(DateTime.Now.Millisecond); - var posts = await _authInfo.ApiInstance.GetUserMediaAsync(userToFetch, 3); + var posts = await _authInfo.ApiInstance.GetUserMediaAsync(userToFetch, + PaginationParameters.MaxPagesToLoad(3)); var anyDuplicate = posts.Value.GroupBy(x => x.Code).Any(g => g.Count() > 1); Assert.NotNull(posts); @@ -103,5 +106,36 @@ public async void EditMediaTest(string mediaId, string caption) var editMedia = await _authInfo.ApiInstance.EditMediaAsync(mediaId, caption); Assert.True(editMedia.Value); } + + [Theory] + [InlineData("https://www.instagram.com/p/BbfsZ-3jbZF/")] + public async void GetMediaIdFromUrlTest(string url) + { + var uri = new Uri(url); + + Assert.True(_authInfo.ApiInstance.IsUserAuthenticated); + var mediaId = await _authInfo.ApiInstance.GetMediaIdFromUrlAsync(uri); + Assert.True(mediaId.Succeeded); + } + + [Theory] + [InlineData("https://www.instagramwrongurl.com/p/BbfsZ-3jbZF/")] + public async void GetMediaIdFromMalformedUrlTest(string url) + { + var uri = new Uri(url); + + Assert.True(_authInfo.ApiInstance.IsUserAuthenticated); + var mediaId = await _authInfo.ApiInstance.GetMediaIdFromUrlAsync(uri); + Assert.False(mediaId.Succeeded); + } + + [Theory] + [InlineData("1635541101392510862_25025320")] + public async void GetShareLinkFromMediaId(string mediaId) + { + Assert.True(_authInfo.ApiInstance.IsUserAuthenticated); + var url = await _authInfo.ApiInstance.GetShareLinkFromMediaIdAsync(mediaId); + Assert.True(url.Succeeded); + } } } \ No newline at end of file diff --git a/InstaSharper.Tests/Endpoints/MessagingTest.cs b/InstaSharper.Tests/Endpoints/MessagingTest.cs index 0a84ce0f..d900703f 100644 --- a/InstaSharper.Tests/Endpoints/MessagingTest.cs +++ b/InstaSharper.Tests/Endpoints/MessagingTest.cs @@ -29,12 +29,12 @@ public async void GetDirectInboxThreadByIdTest(string threadId) public async void SendDirectMessageTest(string user) { Assert.True(_authInfo.ApiInstance.IsUserAuthenticated); - var text = "this is test"; var result = - await _authInfo.ApiInstance.SendDirectMessage(user, "340282366841710300949128137443944319108", text); - + await _authInfo.ApiInstance.SendDirectMessage(user, string.Empty, text); Assert.True(result.Succeeded); + Assert.NotNull(result.Value); + Assert.True(result.Value.Count > 0); } [Fact] diff --git a/InstaSharper.Tests/Endpoints/StoryTest.cs b/InstaSharper.Tests/Endpoints/StoryTest.cs index 0cce1303..2860c550 100644 --- a/InstaSharper.Tests/Endpoints/StoryTest.cs +++ b/InstaSharper.Tests/Endpoints/StoryTest.cs @@ -16,14 +16,14 @@ public StoryTest(AuthenticatedTestFixture authInfo) private readonly AuthenticatedTestFixture _authInfo; [Theory] - [InlineData(1129166614)] + [InlineData(267685466)] private async void GetUserStoryTest(long userId) { Assert.True(_authInfo.ApiInstance.IsUserAuthenticated); var result = await _authInfo.ApiInstance.GetUserStoryAsync(userId); - var stories = result.Value; + var story = result.Value; Assert.True(result.Succeeded); - Assert.NotNull(stories); + Assert.NotNull(story); } [Fact] diff --git a/InstaSharper.Tests/Endpoints/TwoFactorAuthTest.cs b/InstaSharper.Tests/Endpoints/TwoFactorAuthTest.cs new file mode 100644 index 00000000..31ea9ae7 --- /dev/null +++ b/InstaSharper.Tests/Endpoints/TwoFactorAuthTest.cs @@ -0,0 +1,97 @@ +using System; +using System.IO; +using System.Threading; +using InstaSharper.Classes; +using InstaSharper.Tests.Utils; +using Xunit; +using Xunit.Abstractions; + +namespace InstaSharper.Tests.Endpoints +{ + [Collection("Endpoints")] + public class TwoFactorAuthTest + { + public TwoFactorAuthTest(ITestOutputHelper output) + { + _output = output; + } + + private readonly ITestOutputHelper _output; + + [Fact] + public async void User2FaLoginFailTest() + { + var username = "thisasif"; + var password = Environment.GetEnvironmentVariable("instaapiuserpassword"); + + var apiInstance = + TestHelpers.GetDefaultInstaApiInstance(new UserSessionData + { + UserName = username, + Password = password + }); + _output.WriteLine("Got API instance"); + + var loginResult = await apiInstance.LoginAsync(); + Assert.False(loginResult.Succeeded); + Assert.False(apiInstance.IsUserAuthenticated); + + Assert.True(loginResult.Value == InstaLoginResult.TwoFactorRequired); + + var login2FactorAuth = await apiInstance.TwoFactorLoginAsync("666666"); + + Assert.False(login2FactorAuth.Succeeded); + } + + [Fact] + public async void User2FaLoginSuccessTest() + { + var username = "thisasif"; + var password = Environment.GetEnvironmentVariable("instaapiuserpassword"); + + var apiInstance = + TestHelpers.GetDefaultInstaApiInstance(new UserSessionData + { + UserName = username, + Password = password + }); + _output.WriteLine("Got API instance"); + + var loginResult = await apiInstance.LoginAsync(); + Assert.False(loginResult.Succeeded); + Assert.False(apiInstance.IsUserAuthenticated); + + Assert.True(loginResult.Value == InstaLoginResult.TwoFactorRequired); + + Thread.Sleep(20000); + + var content = File.ReadAllText(@"C:\tmp\codice.txt"); + + var login2FactorAuth = await apiInstance.TwoFactorLoginAsync(content); + + Assert.True(login2FactorAuth.Succeeded); + Assert.True(apiInstance.IsUserAuthenticated); + } + + [Fact] + public async void User2FaTest() + { + var username = "thisasif"; + var password = Environment.GetEnvironmentVariable("instaapiuserpassword"); + + var apiInstance = + TestHelpers.GetDefaultInstaApiInstance(new UserSessionData + { + UserName = username, + Password = password + }); + _output.WriteLine("Got API instance"); + + var loginResult = await apiInstance.LoginAsync(); + Assert.False(loginResult.Succeeded); + Assert.False(apiInstance.IsUserAuthenticated); + + Assert.True(loginResult.Value == InstaLoginResult.TwoFactorRequired); + } + } +} \ No newline at end of file diff --git a/InstaSharper.Tests/Endpoints/UploadTest.cs b/InstaSharper.Tests/Endpoints/UploadTest.cs index 10902083..2ed46520 100644 --- a/InstaSharper.Tests/Endpoints/UploadTest.cs +++ b/InstaSharper.Tests/Endpoints/UploadTest.cs @@ -32,5 +32,33 @@ public async void UploadImage() Assert.True(result.Succeeded); Assert.NotNull(result.Value); } + + [Fact] + public async void UploadImagesAsAlbumTest() + { + Assert.True(_authInfo.ApiInstance.IsUserAuthenticated); + + var mediaImage = new InstaImage + { + Height = 512, + Width = 512, + URI = new Uri(@"C:\tmp\1.jpg", UriKind.Absolute).LocalPath + }; + + var mediaImage1 = new InstaImage + { + Height = 512, + Width = 512, + URI = new Uri(@"C:\tmp\2.jpg", UriKind.Absolute).LocalPath + }; + + var result = + await _authInfo.ApiInstance.UploadPhotosAlbumAsync(new[] {mediaImage, mediaImage1}, + "Collection of design"); + + //assert + Assert.True(result.Succeeded); + Assert.NotNull(result.Value); + } } } \ No newline at end of file diff --git a/InstaSharper.Tests/Infrastructure/RankedRecipientsTest.cs b/InstaSharper.Tests/Infrastructure/RankedRecipientsTest.cs new file mode 100644 index 00000000..d13503f8 --- /dev/null +++ b/InstaSharper.Tests/Infrastructure/RankedRecipientsTest.cs @@ -0,0 +1,438 @@ +using InstaSharper.Classes.ResponseWrappers; +using InstaSharper.Helpers; +using Newtonsoft.Json; +using Xunit; + +namespace InstaSharper.Tests.Infrastructure +{ + [Trait("Category", "Infrastructure")] + public class RankedRecipientsTest + { + private const string testJson = @"{ + ""ranked_recipients"": [{ + ""thread"": { + ""thread_id"": ""340282366841710300949128128698734336052"", + ""users"": [{ + ""pk"": 1635609778, + ""username"": ""alr_knight"", + ""full_name"": ""Knight"", + ""is_private"": true, + ""profile_pic_url"": ""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/18160263_191502241369975_8628660834639282176_a.jpg"", + ""profile_pic_id"": ""1503031289182596054_1635609778"", + ""is_verified"": false, + ""has_anonymous_profile_picture"": false + }], + ""canonical"": true, + ""named"": false, + ""thread_title"": ""alr_knight"", + ""pending"": false, + ""thread_type"": ""private"", + ""viewer_id"": 1647718432 + } + }, + { + ""thread"": { + ""thread_id"": ""340282366841710300949128137502093309895"", + ""users"": [{ + ""pk"": 1970280312, + ""username"": ""ali.enzo2003"", + ""full_name"": ""Ali akbar jokar"", + ""is_private"": true, + ""profile_pic_url"": ""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/22221360_117045119049079_2160406946895626240_n.jpg"", + ""profile_pic_id"": ""1619200818765710419_1970280312"", + ""is_verified"": false, + ""has_anonymous_profile_picture"": false + }], + ""canonical"": true, + ""named"": false, + ""thread_title"": ""ali.enzo2003"", + ""pending"": false, + ""thread_type"": ""private"", + ""viewer_id"": 1647718432 + } + }, + { + ""thread"": { + ""thread_id"": ""340282366841710300949128176621874302520"", + ""users"": [{ + ""pk"": 1083654223, + ""username"": ""hootanht"", + ""full_name"": ""Hootan HT"", + ""is_private"": false, + ""profile_pic_url"": ""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/23421265_159622818107137_4034734531750658048_n.jpg"", + ""profile_pic_id"": ""1645900906660662890_1083654223"", + ""is_verified"": false, + ""has_anonymous_profile_picture"": false + }], + ""canonical"": true, + ""named"": false, + ""thread_title"": ""hootanht"", + ""pending"": false, + ""thread_type"": ""private"", + ""viewer_id"": 1647718432 + } + }, + { + ""user"": { + ""pk"": 1693680351, + ""username"": ""farnaz.mzh20"", + ""full_name"": ""Farnaz Mozhgani\u2764\ud83d\udc69\u200d\ud83d\udcbb \u0641\u0631\u0646\u0627\u0632"", + ""is_private"": true, + ""profile_pic_url"": ""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/23507176_1824437314513972_7352310486865543168_n.jpg"", + ""profile_pic_id"": ""1648949686839436879_1693680351"", + ""is_verified"": false, + ""has_anonymous_profile_picture"": false, + ""coeff_weight"": 0.0 + } + }, + { + ""user"": { + ""pk"": 1458048765, + ""username"": ""k.salamati"", + ""full_name"": ""kourosh salamati"", + ""is_private"": true, + ""profile_pic_url"": ""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/22639548_744397965747635_470940668630401024_n.jpg"", + ""profile_pic_id"": ""1632272046988117813_1458048765"", + ""is_verified"": false, + ""has_anonymous_profile_picture"": false, + ""coeff_weight"": 0.0 + } + }, + { + ""user"": { + ""pk"": 6288988520, + ""username"": ""miad.95879593"", + ""full_name"": ""miad.9587"", + ""is_private"": false, + ""profile_pic_url"": ""https://scontent-mxp1-1.cdninstagram.com/t51.2885-19/11906329_960233084022564_1448528159_a.jpg"", + ""is_verified"": false, + ""has_anonymous_profile_picture"": true, + ""coeff_weight"": 0.0 + } + }, + { + ""user"": { + ""pk"": 4700872585, + ""username"": ""amirali.life1997"", + ""full_name"": ""AMIR\u2764Ali"", + ""is_private"": false, + ""profile_pic_url"": ""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/17332903_1356564087699739_7016453540491034624_a.jpg"", + ""profile_pic_id"": ""1472296969970953382_4700872585"", + ""is_verified"": false, + ""has_anonymous_profile_picture"": false, + ""coeff_weight"": 0.0 + } + }, + { + ""user"": { + ""pk"": 5614099053, + ""username"": ""ese5973"", + ""full_name"": ""ese"", + ""is_private"": false, + ""profile_pic_url"": ""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/22159207_785051548363223_2327166594125398016_n.jpg"", + ""profile_pic_id"": ""1618638727914373626_5614099053"", + ""is_verified"": false, + ""has_anonymous_profile_picture"": false, + ""coeff_weight"": 0.0 + } + }, + { + ""user"": { + ""pk"": 5982940248, + ""username"": ""njd_mhmdmhmd"", + ""full_name"": ""\u0645\u062d\u0645\u062f \u0645\u062d\u0645\u062f \u0646\u0698\u0627\u062f"", + ""is_private"": false, + ""profile_pic_url"": ""https://scontent-mxp1-1.cdninstagram.com/t51.2885-19/11906329_960233084022564_1448528159_a.jpg"", + ""is_verified"": false, + ""has_anonymous_profile_picture"": true, + ""coeff_weight"": 0.0 + } + }, + { + ""user"": { + ""pk"": 5677513936, + ""username"": ""nilooofar.re"", + ""full_name"": ""Nilooofar.re"", + ""is_private"": false, + ""profile_pic_url"": ""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/19535013_1592941304080493_2890533623230889984_a.jpg"", + ""profile_pic_id"": ""1550741736980990941_5677513936"", + ""is_verified"": false, + ""has_anonymous_profile_picture"": false, + ""coeff_weight"": 0.0 + } + }, + { + ""user"": { + ""pk"": 5760970028, + ""username"": ""hsh_1993"", + ""full_name"": ""Hamed"", + ""is_private"": false, + ""profile_pic_url"": ""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/20904862_116006422352140_4342957413729566720_a.jpg"", + ""profile_pic_id"": ""1585317022022179386_5760970028"", + ""is_verified"": false, + ""has_anonymous_profile_picture"": false, + ""coeff_weight"": 0.0 + } + }, + { + ""user"": { + ""pk"": 1044853632, + ""username"": ""atn_1993"", + ""full_name"": ""ATN"", + ""is_private"": true, + ""profile_pic_url"": ""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/20184492_1907934619457872_8098029874465210368_a.jpg"", + ""profile_pic_id"": ""1563642139068355862_1044853632"", + ""is_verified"": false, + ""has_anonymous_profile_picture"": false, + ""coeff_weight"": 0.0 + } + }, + { + ""user"": { + ""pk"": 5717455301, + ""username"": ""8309kazeron"", + ""full_name"": ""Mohammad 8309kazeron"", + ""is_private"": false, + ""profile_pic_url"": ""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/20479076_1391591357543894_7587999269659869184_a.jpg"", + ""profile_pic_id"": ""1571721063779623132_5717455301"", + ""is_verified"": false, + ""has_anonymous_profile_picture"": false, + ""coeff_weight"": 0.0 + } + }, + { + ""user"": { + ""pk"": 5678455485, + ""username"": ""mstfy662"", + ""full_name"": ""\u0645\u0635\u0637\u0641\u06cc"", + ""is_private"": false, + ""profile_pic_url"": ""https://scontent-mxp1-1.cdninstagram.com/t51.2885-19/11906329_960233084022564_1448528159_a.jpg"", + ""is_verified"": false, + ""has_anonymous_profile_picture"": true, + ""coeff_weight"": 0.0 + } + }, + { + ""user"": { + ""pk"": 4776464077, + ""username"": ""rwin.ak"", + ""full_name"": ""Rwin"", + ""is_private"": true, + ""profile_pic_url"": ""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/20482199_713939045481173_4217748736115212288_a.jpg"", + ""profile_pic_id"": ""1572672385144738831_4776464077"", + ""is_verified"": false, + ""has_anonymous_profile_picture"": false, + ""coeff_weight"": 0.0 + } + }, + { + ""user"": { + ""pk"": 5561289395, + ""username"": ""sl_mayyy"", + ""full_name"": ""May"", + ""is_private"": false, + ""profile_pic_url"": ""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/23596152_306141666540750_2722580335870083072_n.jpg"", + ""profile_pic_id"": ""1648821021405184938_5561289395"", + ""is_verified"": false, + ""has_anonymous_profile_picture"": false, + ""coeff_weight"": 0.0 + } + }, + { + ""user"": { + ""pk"": 1665097951, + ""username"": ""madt1375"", + ""full_name"": ""\u0026\u0026matin\u0026\u0026\ud83d\udc49 Hakina Matata"", + ""is_private"": true, + ""profile_pic_url"": ""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/19985838_1493714687346911_6311131670384738304_a.jpg"", + ""profile_pic_id"": ""1559723410019089129_1665097951"", + ""is_verified"": false, + ""has_anonymous_profile_picture"": false, + ""coeff_weight"": 0.0 + } + }, + { + ""user"": { + ""pk"": 4172003794, + ""username"": ""bahareh_malekpour"", + ""full_name"": ""Bahareh"", + ""is_private"": true, + ""profile_pic_url"": ""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/15057170_1247864588617496_449409619618430976_a.jpg"", + ""profile_pic_id"": ""1387121674563167477_4172003794"", + ""is_verified"": false, + ""has_anonymous_profile_picture"": false, + ""coeff_weight"": 0.0 + } + }, + { + ""user"": { + ""pk"": 1977441402, + ""username"": ""yousafriaz6"", + ""full_name"": ""Raja Yousaf Riaz"", + ""is_private"": false, + ""profile_pic_url"": ""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/15625524_180356095772072_4276718035793870848_a.jpg"", + ""profile_pic_id"": ""1412858842651739747_1977441402"", + ""is_verified"": false, + ""has_anonymous_profile_picture"": false, + ""coeff_weight"": 0.0 + } + }, + { + ""user"": { + ""pk"": 5360387201, + ""username"": ""ali.d2698"", + ""full_name"": ""Ali.D2698"", + ""is_private"": true, + ""profile_pic_url"": ""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/18014047_117319205487829_6246258770654003200_a.jpg"", + ""profile_pic_id"": ""1497713138212502207_5360387201"", + ""is_verified"": false, + ""has_anonymous_profile_picture"": false, + ""coeff_weight"": 0.0 + } + }, + { + ""user"": { + ""pk"": 1263972398, + ""username"": ""unknow0.o"", + ""full_name"": ""farZin0da"", + ""is_private"": true, + ""profile_pic_url"": ""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/18380268_383330582067431_7009299585264779264_a.jpg"", + ""profile_pic_id"": ""1513241329025427304_1263972398"", + ""is_verified"": false, + ""has_anonymous_profile_picture"": false, + ""coeff_weight"": 0.0 + } + }, + { + ""user"": { + ""pk"": 3917685878, + ""username"": ""azin_zahabi"", + ""full_name"": ""azinzahabi"", + ""is_private"": true, + ""profile_pic_url"": ""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/15534828_351621061879017_1749654446712815616_a.jpg"", + ""profile_pic_id"": ""1411707140427458562_3917685878"", + ""is_verified"": false, + ""has_anonymous_profile_picture"": false, + ""coeff_weight"": 0.0 + } + }, + { + ""user"": { + ""pk"": 1413636874, + ""username"": ""shahryar.ashtari"", + ""full_name"": ""shary famous"", + ""is_private"": true, + ""profile_pic_url"": ""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/13388487_1727117410888429_587178138_a.jpg"", + ""profile_pic_id"": ""1281397482828254031_1413636874"", + ""is_verified"": false, + ""has_anonymous_profile_picture"": false, + ""coeff_weight"": 0.0 + } + }, + { + ""user"": { + ""pk"": 890888289, + ""username"": ""ekhtad"", + ""full_name"": ""Mostafa Fakoori"", + ""is_private"": true, + ""profile_pic_url"": ""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/1660619_178819552491836_2121922682_a.jpg"", + ""is_verified"": false, + ""has_anonymous_profile_picture"": false, + ""coeff_weight"": 0.0 + } + }, + { + ""user"": { + ""pk"": 1504001049, + ""username"": ""saber_marandi"", + ""full_name"": ""saber"", + ""is_private"": false, + ""profile_pic_url"": ""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/23498266_143087636334813_3285276048702308352_n.jpg"", + ""profile_pic_id"": ""1646826333928264659_1504001049"", + ""is_verified"": false, + ""has_anonymous_profile_picture"": false, + ""coeff_weight"": 0.0 + } + }, + { + ""user"": { + ""pk"": 3183327996, + ""username"": ""arash_doooonnnn"", + ""full_name"": ""arash \ud83d\udc97\ud83c\udd70\ud83d\udc97"", + ""is_private"": true, + ""profile_pic_url"": ""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/17494568_1349544698446502_3670486137857638400_a.jpg"", + ""profile_pic_id"": ""1484548657205383055_3183327996"", + ""is_verified"": false, + ""has_anonymous_profile_picture"": false, + ""coeff_weight"": 0.0 + } + }, + { + ""user"": { + ""pk"": 1177836418, + ""username"": ""bardiarya"", + ""full_name"": ""bardi.arya11"", + ""is_private"": true, + ""profile_pic_url"": ""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/16229152_225821371157909_6646071452262989824_a.jpg"", + ""profile_pic_id"": ""1447028028453472548_1177836418"", + ""is_verified"": false, + ""has_anonymous_profile_picture"": false, + ""coeff_weight"": 0.0 + } + }, + { + ""user"": { + ""pk"": 4069861549, + ""username"": ""shayan_behdinian"", + ""full_name"": ""Shayan Behdinian"", + ""is_private"": true, + ""profile_pic_url"": ""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/23279276_127835277902850_7169559637010677760_n.jpg"", + ""profile_pic_id"": ""1642356985624045981_4069861549"", + ""is_verified"": false, + ""has_anonymous_profile_picture"": false, + ""coeff_weight"": 0.0 + } + }, + { + ""user"": { + ""pk"": 4051680176, + ""username"": ""aseman_sard"", + ""full_name"": ""Open Page:18/10/1395"", + ""is_private"": true, + ""profile_pic_url"": ""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/23594227_377303179371808_132910790926663680_n.jpg"", + ""profile_pic_id"": ""1649597462907807731_4051680176"", + ""is_verified"": false, + ""has_anonymous_profile_picture"": false, + ""coeff_weight"": 0.0 + } + }, + { + ""user"": { + ""pk"": 315442394, + ""username"": ""arian_yeganegi"", + ""full_name"": ""Arian"", + ""is_private"": true, + ""profile_pic_url"": ""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/21433649_146016685993808_8510183569073635328_a.jpg"", + ""profile_pic_id"": ""1600099344663909473_315442394"", + ""is_verified"": false, + ""has_anonymous_profile_picture"": false, + ""coeff_weight"": 0.0 + } + }], + ""expires"": 7200, + ""filtered"": true, + ""request_id"": """", + ""rank_token"": ""69b1c5f4-91a3-4a07-b259-ff9b25ea52d1"", + ""status"": ""ok"" +}"; + + [Fact] + public void ConvertRankedRecipientsTest() + { + var responseRecipients = JsonConvert.DeserializeObject(testJson); + var fabric = ConvertersHelper.GetDefaultFabric(); + var result = fabric.GetRecipientsConverter(responseRecipients).Convert(); + Assert.NotNull(result); + } + } +} \ No newline at end of file diff --git a/InstaSharper.Tests/Infrastructure/RecipientsConverterTests.cs b/InstaSharper.Tests/Infrastructure/RecipientsConverterTests.cs new file mode 100644 index 00000000..9ae28ad1 --- /dev/null +++ b/InstaSharper.Tests/Infrastructure/RecipientsConverterTests.cs @@ -0,0 +1,449 @@ +using InstaSharper.Classes.ResponseWrappers; +using InstaSharper.Helpers; +using Newtonsoft.Json; +using Xunit; + +namespace InstaSharper.Tests.Infrastructure +{ + [Trait("Category", "Infrastructure")] + public class RecipientsConverterTests + { + private const string testJson = @"{ + ""ranked_recipients"":[ + { + ""thread"":{ + ""thread_id"":""340282366841710300949128128698734336052"", + ""users"":[ + { + ""pk"":1635609778, + ""username"":""alr_knight"", + ""full_name"":""Knight"", + ""is_private"":true, + ""profile_pic_url"":""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/18160263_191502241369975_8628660834639282176_a.jpg"", + ""profile_pic_id"":""1503031289182596054_1635609778"", + ""is_verified"":false, + ""has_anonymous_profile_picture"":false + } + ], + ""canonical"":true, + ""named"":false, + ""thread_title"":""alr_knight"", + ""pending"":false, + ""thread_type"":""private"", + ""viewer_id"":1647718432 + } + }, + { + ""thread"":{ + ""thread_id"":""340282366841710300949128137502093309895"", + ""users"":[ + { + ""pk"":1970280312, + ""username"":""ali.enzo2003"", + ""full_name"":""Ali akbar jokar"", + ""is_private"":true, + ""profile_pic_url"":""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/22221360_117045119049079_2160406946895626240_n.jpg"", + ""profile_pic_id"":""1619200818765710419_1970280312"", + ""is_verified"":false, + ""has_anonymous_profile_picture"":false + } + ], + ""canonical"":true, + ""named"":false, + ""thread_title"":""ali.enzo2003"", + ""pending"":false, + ""thread_type"":""private"", + ""viewer_id"":1647718432 + } + }, + { + ""thread"":{ + ""thread_id"":""340282366841710300949128176621874302520"", + ""users"":[ + { + ""pk"":1083654223, + ""username"":""hootanht"", + ""full_name"":""Hootan HT"", + ""is_private"":false, + ""profile_pic_url"":""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/23421265_159622818107137_4034734531750658048_n.jpg"", + ""profile_pic_id"":""1645900906660662890_1083654223"", + ""is_verified"":false, + ""has_anonymous_profile_picture"":false + } + ], + ""canonical"":true, + ""named"":false, + ""thread_title"":""hootanht"", + ""pending"":false, + ""thread_type"":""private"", + ""viewer_id"":1647718432 + } + }, + { + ""user"":{ + ""pk"":1693680351, + ""username"":""farnaz.mzh20"", + ""full_name"":""Farnaz Mozhgani\u2764\ud83d\udc69\u200d\ud83d\udcbb \u0641\u0631\u0646\u0627\u0632"", + ""is_private"":true, + ""profile_pic_url"":""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/23507176_1824437314513972_7352310486865543168_n.jpg"", + ""profile_pic_id"":""1648949686839436879_1693680351"", + ""is_verified"":false, + ""has_anonymous_profile_picture"":false, + ""coeff_weight"":0.0 + } + }, + { + ""user"":{ + ""pk"":1458048765, + ""username"":""k.salamati"", + ""full_name"":""kourosh salamati"", + ""is_private"":true, + ""profile_pic_url"":""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/22639548_744397965747635_470940668630401024_n.jpg"", + ""profile_pic_id"":""1632272046988117813_1458048765"", + ""is_verified"":false, + ""has_anonymous_profile_picture"":false, + ""coeff_weight"":0.0 + } + }, + { + ""user"":{ + ""pk"":6288988520, + ""username"":""miad.95879593"", + ""full_name"":""miad.9587"", + ""is_private"":false, + ""profile_pic_url"":""https://scontent-mxp1-1.cdninstagram.com/t51.2885-19/11906329_960233084022564_1448528159_a.jpg"", + ""is_verified"":false, + ""has_anonymous_profile_picture"":true, + ""coeff_weight"":0.0 + } + }, + { + ""user"":{ + ""pk"":4700872585, + ""username"":""amirali.life1997"", + ""full_name"":""AMIR\u2764Ali"", + ""is_private"":false, + ""profile_pic_url"":""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/17332903_1356564087699739_7016453540491034624_a.jpg"", + ""profile_pic_id"":""1472296969970953382_4700872585"", + ""is_verified"":false, + ""has_anonymous_profile_picture"":false, + ""coeff_weight"":0.0 + } + }, + { + ""user"":{ + ""pk"":5614099053, + ""username"":""ese5973"", + ""full_name"":""ese"", + ""is_private"":false, + ""profile_pic_url"":""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/22159207_785051548363223_2327166594125398016_n.jpg"", + ""profile_pic_id"":""1618638727914373626_5614099053"", + ""is_verified"":false, + ""has_anonymous_profile_picture"":false, + ""coeff_weight"":0.0 + } + }, + { + ""user"":{ + ""pk"":5982940248, + ""username"":""njd_mhmdmhmd"", + ""full_name"":""\u0645\u062d\u0645\u062f \u0645\u062d\u0645\u062f \u0646\u0698\u0627\u062f"", + ""is_private"":false, + ""profile_pic_url"":""https://scontent-mxp1-1.cdninstagram.com/t51.2885-19/11906329_960233084022564_1448528159_a.jpg"", + ""is_verified"":false, + ""has_anonymous_profile_picture"":true, + ""coeff_weight"":0.0 + } + }, + { + ""user"":{ + ""pk"":5677513936, + ""username"":""nilooofar.re"", + ""full_name"":""Nilooofar.re"", + ""is_private"":false, + ""profile_pic_url"":""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/19535013_1592941304080493_2890533623230889984_a.jpg"", + ""profile_pic_id"":""1550741736980990941_5677513936"", + ""is_verified"":false, + ""has_anonymous_profile_picture"":false, + ""coeff_weight"":0.0 + } + }, + { + ""user"":{ + ""pk"":5760970028, + ""username"":""hsh_1993"", + ""full_name"":""Hamed"", + ""is_private"":false, + ""profile_pic_url"":""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/20904862_116006422352140_4342957413729566720_a.jpg"", + ""profile_pic_id"":""1585317022022179386_5760970028"", + ""is_verified"":false, + ""has_anonymous_profile_picture"":false, + ""coeff_weight"":0.0 + } + }, + { + ""user"":{ + ""pk"":1044853632, + ""username"":""atn_1993"", + ""full_name"":""ATN"", + ""is_private"":true, + ""profile_pic_url"":""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/20184492_1907934619457872_8098029874465210368_a.jpg"", + ""profile_pic_id"":""1563642139068355862_1044853632"", + ""is_verified"":false, + ""has_anonymous_profile_picture"":false, + ""coeff_weight"":0.0 + } + }, + { + ""user"":{ + ""pk"":5717455301, + ""username"":""8309kazeron"", + ""full_name"":""Mohammad 8309kazeron"", + ""is_private"":false, + ""profile_pic_url"":""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/20479076_1391591357543894_7587999269659869184_a.jpg"", + ""profile_pic_id"":""1571721063779623132_5717455301"", + ""is_verified"":false, + ""has_anonymous_profile_picture"":false, + ""coeff_weight"":0.0 + } + }, + { + ""user"":{ + ""pk"":5678455485, + ""username"":""mstfy662"", + ""full_name"":""\u0645\u0635\u0637\u0641\u06cc"", + ""is_private"":false, + ""profile_pic_url"":""https://scontent-mxp1-1.cdninstagram.com/t51.2885-19/11906329_960233084022564_1448528159_a.jpg"", + ""is_verified"":false, + ""has_anonymous_profile_picture"":true, + ""coeff_weight"":0.0 + } + }, + { + ""user"":{ + ""pk"":4776464077, + ""username"":""rwin.ak"", + ""full_name"":""Rwin"", + ""is_private"":true, + ""profile_pic_url"":""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/20482199_713939045481173_4217748736115212288_a.jpg"", + ""profile_pic_id"":""1572672385144738831_4776464077"", + ""is_verified"":false, + ""has_anonymous_profile_picture"":false, + ""coeff_weight"":0.0 + } + }, + { + ""user"":{ + ""pk"":5561289395, + ""username"":""sl_mayyy"", + ""full_name"":""May"", + ""is_private"":false, + ""profile_pic_url"":""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/23596152_306141666540750_2722580335870083072_n.jpg"", + ""profile_pic_id"":""1648821021405184938_5561289395"", + ""is_verified"":false, + ""has_anonymous_profile_picture"":false, + ""coeff_weight"":0.0 + } + }, + { + ""user"":{ + ""pk"":1665097951, + ""username"":""madt1375"", + ""full_name"":""\u0026\u0026matin\u0026\u0026\ud83d\udc49 Hakina Matata"", + ""is_private"":true, + ""profile_pic_url"":""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/19985838_1493714687346911_6311131670384738304_a.jpg"", + ""profile_pic_id"":""1559723410019089129_1665097951"", + ""is_verified"":false, + ""has_anonymous_profile_picture"":false, + ""coeff_weight"":0.0 + } + }, + { + ""user"":{ + ""pk"":4172003794, + ""username"":""bahareh_malekpour"", + ""full_name"":""Bahareh"", + ""is_private"":true, + ""profile_pic_url"":""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/15057170_1247864588617496_449409619618430976_a.jpg"", + ""profile_pic_id"":""1387121674563167477_4172003794"", + ""is_verified"":false, + ""has_anonymous_profile_picture"":false, + ""coeff_weight"":0.0 + } + }, + { + ""user"":{ + ""pk"":1977441402, + ""username"":""yousafriaz6"", + ""full_name"":""Raja Yousaf Riaz"", + ""is_private"":false, + ""profile_pic_url"":""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/15625524_180356095772072_4276718035793870848_a.jpg"", + ""profile_pic_id"":""1412858842651739747_1977441402"", + ""is_verified"":false, + ""has_anonymous_profile_picture"":false, + ""coeff_weight"":0.0 + } + }, + { + ""user"":{ + ""pk"":5360387201, + ""username"":""ali.d2698"", + ""full_name"":""Ali.D2698"", + ""is_private"":true, + ""profile_pic_url"":""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/18014047_117319205487829_6246258770654003200_a.jpg"", + ""profile_pic_id"":""1497713138212502207_5360387201"", + ""is_verified"":false, + ""has_anonymous_profile_picture"":false, + ""coeff_weight"":0.0 + } + }, + { + ""user"":{ + ""pk"":1263972398, + ""username"":""unknow0.o"", + ""full_name"":""farZin0da"", + ""is_private"":true, + ""profile_pic_url"":""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/18380268_383330582067431_7009299585264779264_a.jpg"", + ""profile_pic_id"":""1513241329025427304_1263972398"", + ""is_verified"":false, + ""has_anonymous_profile_picture"":false, + ""coeff_weight"":0.0 + } + }, + { + ""user"":{ + ""pk"":3917685878, + ""username"":""azin_zahabi"", + ""full_name"":""azinzahabi"", + ""is_private"":true, + ""profile_pic_url"":""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/15534828_351621061879017_1749654446712815616_a.jpg"", + ""profile_pic_id"":""1411707140427458562_3917685878"", + ""is_verified"":false, + ""has_anonymous_profile_picture"":false, + ""coeff_weight"":0.0 + } + }, + { + ""user"":{ + ""pk"":1413636874, + ""username"":""shahryar.ashtari"", + ""full_name"":""shary famous"", + ""is_private"":true, + ""profile_pic_url"":""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/13388487_1727117410888429_587178138_a.jpg"", + ""profile_pic_id"":""1281397482828254031_1413636874"", + ""is_verified"":false, + ""has_anonymous_profile_picture"":false, + ""coeff_weight"":0.0 + } + }, + { + ""user"":{ + ""pk"":890888289, + ""username"":""ekhtad"", + ""full_name"":""Mostafa Fakoori"", + ""is_private"":true, + ""profile_pic_url"":""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/1660619_178819552491836_2121922682_a.jpg"", + ""is_verified"":false, + ""has_anonymous_profile_picture"":false, + ""coeff_weight"":0.0 + } + }, + { + ""user"":{ + ""pk"":1504001049, + ""username"":""saber_marandi"", + ""full_name"":""saber"", + ""is_private"":false, + ""profile_pic_url"":""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/23498266_143087636334813_3285276048702308352_n.jpg"", + ""profile_pic_id"":""1646826333928264659_1504001049"", + ""is_verified"":false, + ""has_anonymous_profile_picture"":false, + ""coeff_weight"":0.0 + } + }, + { + ""user"":{ + ""pk"":3183327996, + ""username"":""arash_doooonnnn"", + ""full_name"":""arash \ud83d\udc97\ud83c\udd70\ud83d\udc97"", + ""is_private"":true, + ""profile_pic_url"":""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/17494568_1349544698446502_3670486137857638400_a.jpg"", + ""profile_pic_id"":""1484548657205383055_3183327996"", + ""is_verified"":false, + ""has_anonymous_profile_picture"":false, + ""coeff_weight"":0.0 + } + }, + { + ""user"":{ + ""pk"":1177836418, + ""username"":""bardiarya"", + ""full_name"":""bardi.arya11"", + ""is_private"":true, + ""profile_pic_url"":""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/16229152_225821371157909_6646071452262989824_a.jpg"", + ""profile_pic_id"":""1447028028453472548_1177836418"", + ""is_verified"":false, + ""has_anonymous_profile_picture"":false, + ""coeff_weight"":0.0 + } + }, + { + ""user"":{ + ""pk"":4069861549, + ""username"":""shayan_behdinian"", + ""full_name"":""Shayan Behdinian"", + ""is_private"":true, + ""profile_pic_url"":""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/23279276_127835277902850_7169559637010677760_n.jpg"", + ""profile_pic_id"":""1642356985624045981_4069861549"", + ""is_verified"":false, + ""has_anonymous_profile_picture"":false, + ""coeff_weight"":0.0 + } + }, + { + ""user"":{ + ""pk"":4051680176, + ""username"":""aseman_sard"", + ""full_name"":""Open Page:18/10/1395"", + ""is_private"":true, + ""profile_pic_url"":""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/23594227_377303179371808_132910790926663680_n.jpg"", + ""profile_pic_id"":""1649597462907807731_4051680176"", + ""is_verified"":false, + ""has_anonymous_profile_picture"":false, + ""coeff_weight"":0.0 + } + }, + { + ""user"":{ + ""pk"":315442394, + ""username"":""arian_yeganegi"", + ""full_name"":""Arian"", + ""is_private"":true, + ""profile_pic_url"":""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/21433649_146016685993808_8510183569073635328_a.jpg"", + ""profile_pic_id"":""1600099344663909473_315442394"", + ""is_verified"":false, + ""has_anonymous_profile_picture"":false, + ""coeff_weight"":0.0 + } + } + ], + ""expires"":7200, + ""filtered"":true, + ""request_id"":"""", + ""rank_token"":""69b1c5f4-91a3-4a07-b259-ff9b25ea52d1"", + ""status"":""ok"" +}"; + + [Fact] + public void RecipientsResponseConverterTests() + { + var responseRecipients = JsonConvert.DeserializeObject(testJson); + var fabric = ConvertersHelper.GetDefaultFabric(); + var converter = fabric.GetRecipientsConverter(responseRecipients); + var result = converter.Convert(); + Assert.NotNull(result); + Assert.True(result.Threads.Count == 3); + Assert.True(result.Users.Count == 27); + } + } +} \ No newline at end of file diff --git a/InstaSharper.Tests/Infrastructure/StoryReelFeedTest.cs b/InstaSharper.Tests/Infrastructure/StoryReelFeedTest.cs new file mode 100644 index 00000000..f3875fc8 --- /dev/null +++ b/InstaSharper.Tests/Infrastructure/StoryReelFeedTest.cs @@ -0,0 +1,989 @@ +using InstaSharper.Classes.ResponseWrappers; +using InstaSharper.Helpers; +using Newtonsoft.Json; +using Xunit; + +namespace InstaSharper.Tests.Infrastructure +{ + [Trait("Category", "Infrastructure")] + public class StoryReelFeedTest + { + private const string testJson = @"{ + ""tray"": [{ + ""id"": 959317915, + ""latest_reel_media"": 1510854409, + ""expiring_at"": 1510940809, + ""seen"": 0.0, + ""can_reply"": true, + ""can_reshare"": true, + ""reel_type"": ""user_reel"", + ""user"": { + ""pk"": 959317915, + ""username"": ""_.hadifaraji._"", + ""full_name"": ""\u202d10110010011010110101\u202c"", + ""is_private"": false, + ""profile_pic_url"": ""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/23348221_112308092876279_8403103806681776128_n.jpg"", + ""profile_pic_id"": ""1644487357511490058_959317915"", + ""is_verified"": false + }, + ""ranked_position"": 1, + ""seen_ranked_position"": 1, + ""muted"": false, + ""prefetch_count"": 1, + ""has_besties_media"": false, + ""items"": [{ + ""taken_at"": 1510854409, + ""pk"": 1649488959569722177, + ""id"": ""1649488959569722177_959317915"", + ""device_timestamp"": 1510854409, + ""media_type"": 1, + ""code"": ""BbkKXgoFPtB"", + ""client_cache_key"": ""MTY0OTQ4ODk1OTU2OTcyMjE3Nw==.2"", + ""filter_type"": 0, + ""image_versions2"": { + ""candidates"": [{ + ""width"": 390, + ""height"": 640, + ""url"": ""https://scontent-cdg2-1.cdninstagram.com/vp/5b80afebea64bd895a658199f19f9869/5A1066B8/t58.9792-15/e35/12852417_1521254644618497_6599033683401768960_n.jpg?ig_cache_key=MTY0OTQ4ODk1OTU2OTcyMjE3Nw%3D%3D.2"" + }, + { + ""width"": 240, + ""height"": 393, + ""url"": ""https://scontent-cdg2-1.cdninstagram.com/vp/933128cc05a9be5c251f4c902173bc1a/5A108FFD/t58.9792-15/e35/p240x240/12852417_1521254644618497_6599033683401768960_n.jpg?ig_cache_key=MTY0OTQ4ODk1OTU2OTcyMjE3Nw%3D%3D.2"" + }] + }, + ""original_width"": 390, + ""original_height"": 640, + ""caption_position"": 0.0, + ""is_reel_media"": true, + ""user"": { + ""pk"": 959317915, + ""username"": ""_.hadifaraji._"", + ""full_name"": ""\u202d10110010011010110101\u202c"", + ""is_private"": false, + ""profile_pic_url"": ""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/23348221_112308092876279_8403103806681776128_n.jpg"", + ""profile_pic_id"": ""1644487357511490058_959317915"", + ""is_verified"": false, + ""has_anonymous_profile_picture"": false, + ""is_unpublished"": false, + ""is_favorite"": false + }, + ""caption"": null, + ""caption_is_edited"": false, + ""photo_of_you"": false, + ""can_viewer_save"": true, + ""organic_tracking_token"": ""eyJ2ZXJzaW9uIjo1LCJwYXlsb2FkIjp7ImlzX2FuYWx5dGljc190cmFja2VkIjpmYWxzZSwidXVpZCI6IjVjOTRjZWY5MWFlYjQ2MDM5MzVhYWQzMTk0NjYzMjYzMTY0OTQ4ODk1OTU2OTcyMjE3NyIsInNlcnZlcl90b2tlbiI6IjE1MTA5MTczMTI0NjN8MTY0OTQ4ODk1OTU2OTcyMjE3N3wxNjQ3NzE4NDMyfDcwMjdmY2EyM2NhOTZkMmEzOTFiZGY4NWNhMzhkZmZjYmVlOGM0MTdkMGEzY2VkNzc3YTBmN2FkZWI5MjIxNTgifSwic2lnbmF0dXJlIjoiIn0="", + ""expiring_at"": 1510940809, + ""reel_mentions"": [], + ""story_locations"": [], + ""story_events"": [], + ""story_hashtags"": [], + ""story_polls"": [], + ""story_feed_media"": [], + ""can_reshare"": true, + ""supports_reel_reactions"": false + }] + }, + { + ""id"": 1391943577, + ""latest_reel_media"": 1510906144, + ""expiring_at"": 1510992544, + ""seen"": 0.0, + ""can_reply"": true, + ""can_reshare"": true, + ""reel_type"": ""user_reel"", + ""user"": { + ""pk"": 1391943577, + ""username"": ""m.tiger"", + ""full_name"": ""TIGER"", + ""is_private"": false, + ""profile_pic_url"": ""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/19050833_1879222825660819_7733253637681446912_a.jpg"", + ""profile_pic_id"": ""1534651095636688138_1391943577"", + ""is_verified"": false + }, + ""ranked_position"": 2, + ""seen_ranked_position"": 2, + ""muted"": false, + ""prefetch_count"": 0, + ""has_besties_media"": false, + ""items"": [{ + ""taken_at"": 1510906144, + ""pk"": 1649922808552366367, + ""id"": ""1649922808552366367_1391943577"", + ""device_timestamp"": 1510906144517, + ""media_type"": 1, + ""code"": ""BbltA1-lCEf"", + ""client_cache_key"": ""MTY0OTkyMjgwODU1MjM2NjM2Nw==.2"", + ""filter_type"": 0, + ""image_versions2"": { + ""candidates"": [{ + ""width"": 1080, + ""height"": 1776, + ""url"": ""https://scontent-cdg2-1.cdninstagram.com/vp/f977e87ccecae5dbe607a57314dcc39d/5A1060C1/t58.9792-15/e35/16994222_160272154575559_6605841983955009536_n.jpg?se=7\u0026ig_cache_key=MTY0OTkyMjgwODU1MjM2NjM2Nw%3D%3D.2"" + }, + { + ""width"": 240, + ""height"": 394, + ""url"": ""https://scontent-cdg2-1.cdninstagram.com/vp/bcf53deeb7abb6a0043f558f2b607a64/5A105032/t58.9792-15/e35/p240x240/16994222_160272154575559_6605841983955009536_n.jpg?ig_cache_key=MTY0OTkyMjgwODU1MjM2NjM2Nw%3D%3D.2"" + }] + }, + ""original_width"": 1080, + ""original_height"": 1776, + ""caption_position"": 0.0, + ""is_reel_media"": true, + ""user"": { + ""pk"": 1391943577, + ""username"": ""m.tiger"", + ""full_name"": ""TIGER"", + ""is_private"": false, + ""profile_pic_url"": ""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/19050833_1879222825660819_7733253637681446912_a.jpg"", + ""profile_pic_id"": ""1534651095636688138_1391943577"", + ""is_verified"": false, + ""has_anonymous_profile_picture"": false, + ""is_unpublished"": false, + ""is_favorite"": false + }, + ""caption"": null, + ""caption_is_edited"": false, + ""photo_of_you"": false, + ""can_viewer_save"": true, + ""organic_tracking_token"": ""eyJ2ZXJzaW9uIjo1LCJwYXlsb2FkIjp7ImlzX2FuYWx5dGljc190cmFja2VkIjpmYWxzZSwidXVpZCI6IjVjOTRjZWY5MWFlYjQ2MDM5MzVhYWQzMTk0NjYzMjYzMTY0OTkyMjgwODU1MjM2NjM2NyIsInNlcnZlcl90b2tlbiI6IjE1MTA5MTczMTI0NjN8MTY0OTkyMjgwODU1MjM2NjM2N3wxNjQ3NzE4NDMyfDUwNjQxZDE5ODEwNGEwYzI4ZDEwMmE0NjdhYWM5MDU5ODg4MmNmYTUzZjdmYzMwNzFiMGVkNjA4ZTZmNWY3MzQifSwic2lnbmF0dXJlIjoiIn0="", + ""expiring_at"": 1510992544, + ""reel_mentions"": [], + ""story_locations"": [], + ""story_events"": [], + ""story_hashtags"": [], + ""story_polls"": [], + ""story_feed_media"": [], + ""can_reshare"": true, + ""supports_reel_reactions"": false + }] + }, + { + ""id"": 3206926093, + ""latest_reel_media"": 1510908005, + ""expiring_at"": 1510994405, + ""seen"": 0.0, + ""can_reply"": false, + ""can_reshare"": true, + ""reel_type"": ""user_reel"", + ""user"": { + ""pk"": 3206926093, + ""username"": ""ho3in0272"", + ""full_name"": ""Ho3in"", + ""is_private"": false, + ""profile_pic_url"": ""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/17817785_456510021347810_7510459570775392256_a.jpg"", + ""profile_pic_id"": ""1487521582481690239_3206926093"", + ""is_verified"": false + }, + ""ranked_position"": 3, + ""seen_ranked_position"": 3, + ""muted"": false, + ""prefetch_count"": 0, + ""has_besties_media"": false, + ""items"": [{ + ""taken_at"": 1510908005, + ""pk"": 1649938507261317983, + ""id"": ""1649938507261317983_3206926093"", + ""device_timestamp"": 1510908005432, + ""media_type"": 1, + ""code"": ""BblwlSijJNf"", + ""client_cache_key"": ""MTY0OTkzODUwNzI2MTMxNzk4Mw==.2"", + ""filter_type"": 0, + ""image_versions2"": { + ""candidates"": [{ + ""width"": 540, + ""height"": 960, + ""url"": ""https://scontent-cdg2-1.cdninstagram.com/vp/742a4dd0ccecdf63b33f2d76c9cfb19a/5A102C1B/t58.9792-15/e35/20775352_130201331028706_1523758292432584704_n.jpg?ig_cache_key=MTY0OTkzODUwNzI2MTMxNzk4Mw%3D%3D.2"" + }, + { + ""width"": 240, + ""height"": 426, + ""url"": ""https://scontent-cdg2-1.cdninstagram.com/vp/d5bca24e2f10ff54e883dbf9a353a49b/5A102D88/t58.9792-15/e35/p240x240/20775352_130201331028706_1523758292432584704_n.jpg?ig_cache_key=MTY0OTkzODUwNzI2MTMxNzk4Mw%3D%3D.2"" + }] + }, + ""original_width"": 540, + ""original_height"": 960, + ""caption_position"": 0.0, + ""is_reel_media"": true, + ""user"": { + ""pk"": 3206926093, + ""username"": ""ho3in0272"", + ""full_name"": ""Ho3in"", + ""is_private"": false, + ""profile_pic_url"": ""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/17817785_456510021347810_7510459570775392256_a.jpg"", + ""profile_pic_id"": ""1487521582481690239_3206926093"", + ""is_verified"": false, + ""has_anonymous_profile_picture"": false, + ""is_unpublished"": false, + ""is_favorite"": false + }, + ""caption"": null, + ""caption_is_edited"": false, + ""photo_of_you"": false, + ""can_viewer_save"": true, + ""organic_tracking_token"": ""eyJ2ZXJzaW9uIjo1LCJwYXlsb2FkIjp7ImlzX2FuYWx5dGljc190cmFja2VkIjpmYWxzZSwidXVpZCI6IjVjOTRjZWY5MWFlYjQ2MDM5MzVhYWQzMTk0NjYzMjYzMTY0OTkzODUwNzI2MTMxNzk4MyIsInNlcnZlcl90b2tlbiI6IjE1MTA5MTczMTI0NjN8MTY0OTkzODUwNzI2MTMxNzk4M3wxNjQ3NzE4NDMyfDVkMTU0MTNhODc4ZGY5ZWMyYmMxZWQwYmU0MWYyMTc5NjJmYzFjMzFlM2M2YjAzZjgxYzNhZDM0YTgxMjkzZjAifSwic2lnbmF0dXJlIjoiIn0="", + ""expiring_at"": 1510994405, + ""imported_taken_at"": 1510907979, + ""reel_mentions"": [], + ""story_locations"": [], + ""story_events"": [], + ""story_hashtags"": [], + ""story_polls"": [], + ""story_feed_media"": [], + ""can_reshare"": true, + ""supports_reel_reactions"": false + }] + }, + { + ""id"": 2057461165, + ""latest_reel_media"": 1510862508, + ""expiring_at"": 1510948908, + ""seen"": 0.0, + ""can_reply"": true, + ""can_reshare"": true, + ""reel_type"": ""user_reel"", + ""user"": { + ""pk"": 2057461165, + ""username"": ""ayria.b"", + ""full_name"": ""ayria"", + ""is_private"": true, + ""profile_pic_url"": ""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/22157464_122412968458824_5056550025048358912_n.jpg"", + ""profile_pic_id"": ""1618215817073442624_2057461165"", + ""is_verified"": false + }, + ""ranked_position"": 4, + ""seen_ranked_position"": 4, + ""muted"": false, + ""prefetch_count"": 0, + ""has_besties_media"": false, + ""items"": [{ + ""taken_at"": 1510862378, + ""pk"": 1649556176310660131, + ""id"": ""1649556176310660131_2057461165"", + ""device_timestamp"": 373165517843589, + ""media_type"": 1, + ""code"": ""BbkZppGDGwjoXRukhmmEIU0EAw4nI-qPUuHc780"", + ""client_cache_key"": ""MTY0OTU1NjE3NjMxMDY2MDEzMQ==.2"", + ""filter_type"": 0, + ""image_versions2"": { + ""candidates"": [{ + ""width"": 1080, + ""height"": 1920, + ""url"": ""https://scontent-cdg2-1.cdninstagram.com/vp/31cffb90989f0ebc1027f1d7cf3dd0a0/5A105D39/t58.9792-15/e35/17568037_129576284428483_7628768821515386880_n.jpg?se=7\u0026ig_cache_key=MTY0OTU1NjE3NjMxMDY2MDEzMQ%3D%3D.2"" + }, + { + ""width"": 240, + ""height"": 426, + ""url"": ""https://scontent-cdg2-1.cdninstagram.com/vp/41c19e515b51c92502412ca69ef1519c/5A105322/t58.9792-15/e35/p240x240/17568037_129576284428483_7628768821515386880_n.jpg?ig_cache_key=MTY0OTU1NjE3NjMxMDY2MDEzMQ%3D%3D.2"" + }] + }, + ""original_width"": 1080, + ""original_height"": 1920, + ""caption_position"": 0.0, + ""is_reel_media"": true, + ""user"": { + ""pk"": 2057461165, + ""username"": ""ayria.b"", + ""full_name"": ""ayria"", + ""is_private"": true, + ""profile_pic_url"": ""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/22157464_122412968458824_5056550025048358912_n.jpg"", + ""profile_pic_id"": ""1618215817073442624_2057461165"", + ""is_verified"": false, + ""has_anonymous_profile_picture"": false, + ""is_unpublished"": false, + ""is_favorite"": false + }, + ""caption"": null, + ""caption_is_edited"": false, + ""photo_of_you"": false, + ""can_viewer_save"": true, + ""organic_tracking_token"": ""eyJ2ZXJzaW9uIjo1LCJwYXlsb2FkIjp7ImlzX2FuYWx5dGljc190cmFja2VkIjpmYWxzZSwidXVpZCI6IjVjOTRjZWY5MWFlYjQ2MDM5MzVhYWQzMTk0NjYzMjYzMTY0OTU1NjE3NjMxMDY2MDEzMSIsInNlcnZlcl90b2tlbiI6IjE1MTA5MTczMTI0NjN8MTY0OTU1NjE3NjMxMDY2MDEzMXwxNjQ3NzE4NDMyfDY5NTI1MGVkN2NhMWNmNWMyNWEwZGMxZTRmOTg4Y2MyZTVlYTIyNTc5N2I0OWE1NDNiZTZhOWUwM2QzOGQyZDQifSwic2lnbmF0dXJlIjoiIn0="", + ""expiring_at"": 1510948778, + ""imported_taken_at"": 1510862091, + ""reel_mentions"": [], + ""story_locations"": [], + ""story_events"": [], + ""story_hashtags"": [], + ""story_polls"": [], + ""story_feed_media"": [], + ""can_reshare"": true, + ""supports_reel_reactions"": false + }, + { + ""taken_at"": 1510862508, + ""pk"": 1649556777279099195, + ""id"": ""1649556777279099195_2057461165"", + ""device_timestamp"": 373296285005034, + ""media_type"": 1, + ""code"": ""BbkZyYyjxE7yF9PECklEfkGDt6C3kodZ2Ss0Nc0"", + ""client_cache_key"": ""MTY0OTU1Njc3NzI3OTA5OTE5NQ==.2"", + ""filter_type"": 0, + ""image_versions2"": { + ""candidates"": [{ + ""width"": 1080, + ""height"": 1920, + ""url"": ""https://scontent-cdg2-1.cdninstagram.com/vp/c70b71f1afd04a7016a0b06fe02af7a6/5A103CD4/t58.9792-15/e35/18448471_189954088227361_2974203919778971648_n.jpg?se=7\u0026ig_cache_key=MTY0OTU1Njc3NzI3OTA5OTE5NQ%3D%3D.2"" + }, + { + ""width"": 240, + ""height"": 426, + ""url"": ""https://scontent-cdg2-1.cdninstagram.com/vp/4d22109912171cdd294f38faaa0cc0d0/5A1081C3/t58.9792-15/e35/p240x240/18448471_189954088227361_2974203919778971648_n.jpg?ig_cache_key=MTY0OTU1Njc3NzI3OTA5OTE5NQ%3D%3D.2"" + }] + }, + ""original_width"": 1080, + ""original_height"": 1920, + ""caption_position"": 0.0, + ""is_reel_media"": true, + ""user"": { + ""pk"": 2057461165, + ""username"": ""ayria.b"", + ""full_name"": ""ayria"", + ""is_private"": true, + ""profile_pic_url"": ""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/22157464_122412968458824_5056550025048358912_n.jpg"", + ""profile_pic_id"": ""1618215817073442624_2057461165"", + ""is_verified"": false, + ""has_anonymous_profile_picture"": false, + ""is_unpublished"": false, + ""is_favorite"": false + }, + ""caption"": null, + ""caption_is_edited"": false, + ""photo_of_you"": false, + ""can_viewer_save"": true, + ""organic_tracking_token"": ""eyJ2ZXJzaW9uIjo1LCJwYXlsb2FkIjp7ImlzX2FuYWx5dGljc190cmFja2VkIjpmYWxzZSwidXVpZCI6IjVjOTRjZWY5MWFlYjQ2MDM5MzVhYWQzMTk0NjYzMjYzMTY0OTU1Njc3NzI3OTA5OTE5NSIsInNlcnZlcl90b2tlbiI6IjE1MTA5MTczMTI0NjN8MTY0OTU1Njc3NzI3OTA5OTE5NXwxNjQ3NzE4NDMyfDE4MmFmNTE5YWQ5ZmFhMjlmM2Q1ODliMjA0MThhZDRkYmJkNjcwM2IwMWMwOTY5N2Q0MGI4MGFiZjIzZmMwNmMifSwic2lnbmF0dXJlIjoiIn0="", + ""expiring_at"": 1510948908, + ""imported_taken_at"": 1510845400, + ""reel_mentions"": [], + ""story_locations"": [], + ""story_events"": [], + ""story_hashtags"": [], + ""story_polls"": [], + ""story_feed_media"": [], + ""can_reshare"": true, + ""supports_reel_reactions"": false + }] + }, + { + ""id"": 1489869781, + ""latest_reel_media"": 1510903812, + ""expiring_at"": 1510990212, + ""seen"": 0.0, + ""can_reply"": true, + ""can_reshare"": true, + ""reel_type"": ""user_reel"", + ""user"": { + ""pk"": 1489869781, + ""username"": ""ali_akbari_1994"", + ""full_name"": ""Ali Akbari"", + ""is_private"": false, + ""profile_pic_url"": ""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/11008223_1642746932642809_2053218239_a.jpg"", + ""is_verified"": false + }, + ""ranked_position"": 5, + ""seen_ranked_position"": 5, + ""muted"": false, + ""prefetch_count"": 0, + ""has_besties_media"": false + }, + { + ""id"": 5482540873, + ""latest_reel_media"": 1510900559, + ""expiring_at"": 1510986959, + ""seen"": 0.0, + ""can_reply"": true, + ""can_reshare"": true, + ""reel_type"": ""user_reel"", + ""user"": { + ""pk"": 5482540873, + ""username"": ""_kimixe"", + ""full_name"": ""\u269c\u06a9\u0640\u06cc\u0645\u06cc\u0640\u0627 \u062f\u0631\u064e\u062e\u0640\u0640\u0640\u0634\u0627\u0646\u06cc\ud83d\udc78\ud83c\udffb"", + ""is_private"": false, + ""profile_pic_url"": ""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/21436207_272984799873060_3437969849836371968_a.jpg"", + ""profile_pic_id"": ""1600899676696408725_5482540873"", + ""is_verified"": false + }, + ""ranked_position"": 6, + ""seen_ranked_position"": 6, + ""muted"": false, + ""prefetch_count"": 0, + ""has_besties_media"": false + }, + { + ""id"": 3066946863, + ""latest_reel_media"": 1510831151, + ""expiring_at"": 1510917551, + ""seen"": 0.0, + ""can_reply"": false, + ""can_reshare"": true, + ""reel_type"": ""user_reel"", + ""user"": { + ""pk"": 3066946863, + ""username"": ""video__tanz"", + ""full_name"": ""\ud83d\ude02 Video Tanz \ud83d\ude02"", + ""is_private"": false, + ""profile_pic_url"": ""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/23279217_302064020275335_2450216195975020544_n.jpg"", + ""profile_pic_id"": ""1489831773834723134_3066946863"", + ""is_verified"": false + }, + ""ranked_position"": 7, + ""seen_ranked_position"": 7, + ""muted"": false, + ""prefetch_count"": 0, + ""has_besties_media"": false + }, + { + ""id"": 251137241, + ""latest_reel_media"": 1510847715, + ""expiring_at"": 1510934115, + ""seen"": 0.0, + ""can_reply"": true, + ""can_reshare"": true, + ""reel_type"": ""user_reel"", + ""user"": { + ""pk"": 251137241, + ""username"": ""svetabily"", + ""full_name"": ""Sveta Bilyalova"", + ""is_private"": false, + ""profile_pic_url"": ""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/20398229_871582456329183_4794696546299936768_a.jpg"", + ""profile_pic_id"": ""1566324341121474832_251137241"", + ""is_verified"": true + }, + ""ranked_position"": 8, + ""seen_ranked_position"": 8, + ""muted"": false, + ""prefetch_count"": 0, + ""has_besties_media"": false + }, + { + ""id"": 3405890655, + ""latest_reel_media"": 1510914372, + ""expiring_at"": 1511000772, + ""seen"": 0.0, + ""can_reply"": true, + ""can_reshare"": true, + ""reel_type"": ""user_reel"", + ""user"": { + ""pk"": 3405890655, + ""username"": ""saburi_ali"", + ""full_name"": ""\u2604Ali\u26a1\ufe0fSaburi\ud83d\udd38"", + ""is_private"": false, + ""profile_pic_url"": ""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/22637548_764050613801850_6552322718602100736_n.jpg"", + ""profile_pic_id"": ""1629689145662538438_3405890655"", + ""is_verified"": false + }, + ""ranked_position"": 9, + ""seen_ranked_position"": 9, + ""muted"": false, + ""prefetch_count"": 0, + ""has_besties_media"": false + }, + { + ""id"": 3551724865, + ""latest_reel_media"": 1510895779, + ""expiring_at"": 1510982179, + ""seen"": 0.0, + ""can_reply"": true, + ""can_reshare"": false, + ""reel_type"": ""user_reel"", + ""user"": { + ""pk"": 3551724865, + ""username"": ""mehdi_shtoori"", + ""full_name"": ""mehdi_shatoori"", + ""is_private"": false, + ""profile_pic_url"": ""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/23594874_186693181881005_6648547247506063360_n.jpg"", + ""profile_pic_id"": ""1649578282054939172_3551724865"", + ""is_verified"": false + }, + ""ranked_position"": 10, + ""seen_ranked_position"": 10, + ""muted"": false, + ""prefetch_count"": 0, + ""has_besties_media"": false + }, + { + ""id"": 38334017, + ""latest_reel_media"": 1510852186, + ""expiring_at"": 1510938586, + ""seen"": 0.0, + ""can_reply"": false, + ""can_reshare"": true, + ""reel_type"": ""user_reel"", + ""user"": { + ""pk"": 38334017, + ""username"": ""tohi"", + ""full_name"": ""Tohi"", + ""is_private"": false, + ""profile_pic_url"": ""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/22157401_1164828236984070_3300847736101797888_n.jpg"", + ""profile_pic_id"": ""1617773928800265440_38334017"", + ""is_verified"": true + }, + ""ranked_position"": 11, + ""seen_ranked_position"": 11, + ""muted"": false, + ""prefetch_count"": 0, + ""has_besties_media"": false + }, + { + ""id"": 271236276, + ""latest_reel_media"": 1510832936, + ""expiring_at"": 1510919336, + ""seen"": 0.0, + ""can_reply"": true, + ""can_reshare"": true, + ""reel_type"": ""user_reel"", + ""user"": { + ""pk"": 271236276, + ""username"": ""donyadadrasan"", + ""full_name"": ""\u062f\u0646\u06cc\u0627"", + ""is_private"": false, + ""profile_pic_url"": ""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/23498668_1768894090082015_6493720728821563392_n.jpg"", + ""profile_pic_id"": ""1645538026368962501_271236276"", + ""is_verified"": false + }, + ""ranked_position"": 12, + ""seen_ranked_position"": 12, + ""muted"": false, + ""prefetch_count"": 0, + ""has_besties_media"": false + }, + { + ""id"": 1510966691, + ""latest_reel_media"": 1510916639, + ""expiring_at"": 1511003039, + ""seen"": 0.0, + ""can_reply"": false, + ""can_reshare"": true, + ""reel_type"": ""user_reel"", + ""user"": { + ""pk"": 1510966691, + ""username"": ""lisaandlena"", + ""full_name"": ""Lisa and Lena | Germany\u00ae"", + ""is_private"": false, + ""profile_pic_url"": ""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/20184530_1714340778874426_8644379796467351552_a.jpg"", + ""profile_pic_id"": ""1564408562074097932_1510966691"", + ""is_verified"": true + }, + ""ranked_position"": 13, + ""seen_ranked_position"": 13, + ""muted"": false, + ""prefetch_count"": 0, + ""has_besties_media"": false + }, + { + ""id"": 3282438449, + ""latest_reel_media"": 1510914206, + ""expiring_at"": 1511000606, + ""seen"": 0.0, + ""can_reply"": true, + ""can_reshare"": true, + ""reel_type"": ""user_reel"", + ""user"": { + ""pk"": 3282438449, + ""username"": ""sepehr.k.a.80"", + ""full_name"": ""sepehr.k.a.80"", + ""is_private"": false, + ""profile_pic_url"": ""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/20759440_133387480611580_3356761454312161280_a.jpg"", + ""profile_pic_id"": ""1578253411031495954_3282438449"", + ""is_verified"": false + }, + ""ranked_position"": 14, + ""seen_ranked_position"": 14, + ""muted"": false, + ""prefetch_count"": 0, + ""has_besties_media"": false + }, + { + ""id"": 1561068104, + ""latest_reel_media"": 1510856008, + ""expiring_at"": 1510942408, + ""seen"": 0.0, + ""can_reply"": true, + ""can_reshare"": true, + ""reel_type"": ""user_reel"", + ""user"": { + ""pk"": 1561068104, + ""username"": ""negin_xaniaristm"", + ""full_name"": ""\ud83d\udc8e\u06cc\u0647 \u0646\u06af\u06cc\u0640^\u2022^\u0640\u0646 \u062e\u0633\u0631\u0648\u06cc\u0633\u062a\u200c\u062a\u06cc\u200c\u0627\u0650\u0645\u06cc\ud83d\udc8e"", + ""is_private"": false, + ""profile_pic_url"": ""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/23507807_128028924552207_1606758449528438784_n.jpg"", + ""profile_pic_id"": ""1646591608245492499_1561068104"", + ""is_verified"": false + }, + ""ranked_position"": 16, + ""seen_ranked_position"": 16, + ""muted"": false, + ""prefetch_count"": 0, + ""has_besties_media"": false + }, + { + ""id"": 10245870, + ""latest_reel_media"": 1510907836, + ""expiring_at"": 1510994236, + ""seen"": 0.0, + ""can_reply"": true, + ""can_reshare"": true, + ""reel_type"": ""user_reel"", + ""user"": { + ""pk"": 10245870, + ""username"": ""amandacerny"", + ""full_name"": ""Amanda Cerny"", + ""is_private"": false, + ""profile_pic_url"": ""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/22637117_1696268193770897_1780284289452081152_n.jpg"", + ""profile_pic_id"": ""1628386071727763294_10245870"", + ""is_verified"": true + }, + ""ranked_position"": 17, + ""seen_ranked_position"": 17, + ""muted"": false, + ""prefetch_count"": 0, + ""has_besties_media"": false + }, + { + ""id"": 2244850095, + ""latest_reel_media"": 1510871575, + ""expiring_at"": 1510957975, + ""seen"": 0.0, + ""can_reply"": true, + ""can_reshare"": true, + ""reel_type"": ""user_reel"", + ""user"": { + ""pk"": 2244850095, + ""username"": ""funtv_ir"", + ""full_name"": ""FunTV \u272a \u0641\u0627\u0646 \u062a\u06cc \u0648\u06cc"", + ""is_private"": false, + ""profile_pic_url"": ""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/21827728_905466152934893_4542388546467528704_n.jpg"", + ""profile_pic_id"": ""1568133572379051405_2244850095"", + ""is_verified"": false + }, + ""ranked_position"": 18, + ""seen_ranked_position"": 18, + ""muted"": false, + ""prefetch_count"": 0, + ""has_besties_media"": false + }, + { + ""id"": 12299923, + ""latest_reel_media"": 1510877891, + ""expiring_at"": 1510964291, + ""seen"": 0.0, + ""can_reply"": true, + ""can_reshare"": true, + ""reel_type"": ""user_reel"", + ""user"": { + ""pk"": 12299923, + ""username"": ""andreaespadatv"", + ""full_name"": ""\u15e9\u144e\u15ea\u1587E\u15e9 E\u1515\u146d\u15e9\u15ea\u15e9"", + ""is_private"": false, + ""profile_pic_url"": ""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/20905332_1992303564377138_6941782205450420224_a.jpg"", + ""profile_pic_id"": ""1585831165661932658_12299923"", + ""is_verified"": true + }, + ""ranked_position"": 19, + ""seen_ranked_position"": 19, + ""muted"": false, + ""prefetch_count"": 0, + ""has_besties_media"": false + }, + { + ""id"": 1646749288, + ""latest_reel_media"": 1510906828, + ""expiring_at"": 1510993228, + ""seen"": 0.0, + ""can_reply"": false, + ""can_reshare"": true, + ""reel_type"": ""user_reel"", + ""user"": { + ""pk"": 1646749288, + ""username"": ""jazabtarin_cliphaa"", + ""full_name"": ""\ud83d\udd4b\u0628\u0633\u0645 \u0627\u0644\u0644\u0647 \u0627\u0644\u0631\u062d\u0645\u0646 \u0627\u0644\u0631\u062d\u06cc\u0645\ud83d\udd4b"", + ""is_private"": false, + ""profile_pic_url"": ""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/23347372_180675915816021_4310249219135897600_n.jpg"", + ""profile_pic_id"": ""1501663558492258008_1646749288"", + ""is_verified"": false + }, + ""ranked_position"": 20, + ""seen_ranked_position"": 20, + ""muted"": false, + ""prefetch_count"": 0, + ""has_besties_media"": false + }, + { + ""id"": 1259128889, + ""latest_reel_media"": 1510904116, + ""expiring_at"": 1510990516, + ""seen"": 0.0, + ""can_reply"": true, + ""can_reshare"": true, + ""reel_type"": ""user_reel"", + ""user"": { + ""pk"": 1259128889, + ""username"": ""clipp_video"", + ""full_name"": ""\u06a9\u0644\u06cc\u067e \u0648\u06cc\u062f\u0626\u0648(\u062e\u0646\u062f\u0647 \u0641\u0627\u0646 \u0633\u0631\u06af\u0631\u0645\u06cc )"", + ""is_private"": false, + ""profile_pic_url"": ""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/22429821_148439145763324_7717555759847833600_n.jpg"", + ""profile_pic_id"": ""1308169938154726997_1259128889"", + ""is_verified"": false + }, + ""ranked_position"": 21, + ""seen_ranked_position"": 21, + ""muted"": false, + ""prefetch_count"": 0, + ""has_besties_media"": false + }, + { + ""id"": 442625, + ""latest_reel_media"": 1510890967, + ""expiring_at"": 1510977367, + ""seen"": 0.0, + ""can_reply"": true, + ""can_reshare"": true, + ""reel_type"": ""user_reel"", + ""user"": { + ""pk"": 442625, + ""username"": ""journeydan"", + ""full_name"": ""Daniel Bader"", + ""is_private"": false, + ""profile_pic_url"": ""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/11348095_814564621974849_480326611_a.jpg"", + ""is_verified"": false + }, + ""ranked_position"": 22, + ""seen_ranked_position"": 22, + ""muted"": false, + ""prefetch_count"": 0, + ""has_besties_media"": false + }, + { + ""id"": 2437674150, + ""latest_reel_media"": 1510862618, + ""expiring_at"": 1510949018, + ""seen"": 0.0, + ""can_reply"": true, + ""can_reshare"": true, + ""reel_type"": ""user_reel"", + ""user"": { + ""pk"": 2437674150, + ""username"": ""dubsmash_clip_ir"", + ""full_name"": ""\u062f\u0627\u0628\u0633\u0645\u0634_\u06a9\u0644\u06cc\u067e \u0627\u06cc\u0631\u0627\u0646\u06cc"", + ""is_private"": false, + ""profile_pic_url"": ""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/23507577_1989357354677188_6071511854348238848_n.jpg"", + ""profile_pic_id"": ""1565995338656106798_2437674150"", + ""is_verified"": false + }, + ""ranked_position"": 23, + ""seen_ranked_position"": 23, + ""muted"": false, + ""prefetch_count"": 0, + ""has_besties_media"": false + }, + { + ""id"": 1451667254, + ""latest_reel_media"": 1510907597, + ""expiring_at"": 1510993997, + ""seen"": 0.0, + ""can_reply"": true, + ""can_reshare"": true, + ""reel_type"": ""user_reel"", + ""user"": { + ""pk"": 1451667254, + ""username"": ""ir___photographer"", + ""full_name"": ""Photography Ideas"", + ""is_private"": false, + ""profile_pic_url"": ""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/20759619_110898839576437_1401961498183467008_a.jpg"", + ""profile_pic_id"": ""1579067129738486356_1451667254"", + ""is_verified"": false + }, + ""ranked_position"": 24, + ""seen_ranked_position"": 24, + ""muted"": false, + ""prefetch_count"": 0, + ""has_besties_media"": false + }, + { + ""id"": 1548541077, + ""latest_reel_media"": 1510845566, + ""expiring_at"": 1510931966, + ""seen"": 0.0, + ""can_reply"": true, + ""can_reshare"": true, + ""reel_type"": ""user_reel"", + ""user"": { + ""pk"": 1548541077, + ""username"": ""hanjare_talaei"", + ""full_name"": ""hanjare talaei \ud83c\udf99\u062d\u0646\u062c\u0631\u0647 \u0637\u0644\u0627\u064a\u0649"", + ""is_private"": false, + ""profile_pic_url"": ""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/23101843_1849658215074801_563374214585778176_n.jpg"", + ""profile_pic_id"": ""1640555686213844652_1548541077"", + ""is_verified"": false + }, + ""ranked_position"": 25, + ""seen_ranked_position"": 25, + ""muted"": false, + ""prefetch_count"": 0, + ""has_besties_media"": false + }], + ""post_live"": { + ""post_live_items"": [{ + ""pk"": ""post_live_1383543459"", + ""user"": { + ""pk"": 1383543459, + ""username"": ""780ir"", + ""full_name"": ""\u0647\u0641\u0640 \u0647\u0634\u062a\u0627\u062f | #\u0667\u0668\u0660*"", + ""is_private"": false, + ""profile_pic_url"": ""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/21980259_360063074444115_3710275616830914560_n.jpg"", + ""profile_pic_id"": ""1532382334209046138_1383543459"", + ""is_verified"": false + }, + ""broadcasts"": [{ + ""id"": 17908970164059694, + ""broadcast_status"": ""post_live"", + ""dash_manifest"": ""\u003cMPD xmlns=\""urn:mpeg:dash:schema:mpd:2011\"" minBufferTime=\""PT1.500S\"" type=\""static\"" mediaPresentationDuration=\""PT0H16M24.622S\"" maxSegmentDuration=\""PT0H0M2.000S\"" profiles=\""urn:mpeg:dash:profile:isoff-on-demand:2011,http://dashif.org/guidelines/dash264\""\u003e\u003cPeriod duration=\""PT0H16M24.622S\""\u003e\u003cAdaptationSet segmentAlignment=\""true\"" maxWidth=\""396\"" maxHeight=\""704\"" maxFrameRate=\""16000/544\"" par=\""396:704\"" lang=\""und\"" subsegmentAlignment=\""true\"" subsegmentStartsWithSAP=\""1\""\u003e\u003cRepresentation id=\""17895622549102837v\"" mimeType=\""video/mp4\"" codecs=\""avc1.4d401f\"" width=\""396\"" height=\""704\"" frameRate=\""16000/544\"" sar=\""1:1\"" startWithSAP=\""1\"" bandwidth=\""582064\"" FBQualityClass=\""sd\"" FBQualityLabel=\""396w\""\u003e\u003cBaseURL\u003ehttps://scontent-cdg2-1.cdninstagram.com/t72.12950-16/10000000_1740452772915569_6670090197872410624_n.mp4\u003c/BaseURL\u003e\u003cSegmentBase indexRangeExact=\""true\"" indexRange=\""899-6930\""\u003e\u003cInitialization range=\""0-898\""/\u003e\u003c/SegmentBase\u003e\u003c/Representation\u003e\u003c/AdaptationSet\u003e\u003cAdaptationSet segmentAlignment=\""true\"" lang=\""und\"" subsegmentAlignment=\""true\"" subsegmentStartsWithSAP=\""1\""\u003e\u003cRepresentation id=\""17895622549102837a\"" mimeType=\""audio/mp4\"" codecs=\""mp4a.40.2\"" audioSamplingRate=\""44100\"" startWithSAP=\""1\"" bandwidth=\""50283\""\u003e\u003cAudioChannelConfiguration schemeIdUri=\""urn:mpeg:dash:23003:3:audio_channel_configuration:2011\"" value=\""2\""/\u003e\u003cBaseURL\u003ehttps://scontent-cdg2-1.cdninstagram.com/t72.12950-16/23586292_132647884105636_1354989047184883712_n.mp4\u003c/BaseURL\u003e\u003cSegmentBase indexRangeExact=\""true\"" indexRange=\""835-6794\""\u003e\u003cInitialization range=\""0-834\""/\u003e\u003c/SegmentBase\u003e\u003c/Representation\u003e\u003c/AdaptationSet\u003e\u003c/Period\u003e\u003c/MPD\u003e"", + ""expire_at"": 1510939433, + ""encoding_tag"": ""instagram_dash_remuxed"", + ""internal_only"": false, + ""number_of_qualities"": 1, + ""cover_frame_url"": ""https://scontent-cdg2-1.cdninstagram.com/t51.9792-15/18979052_757168924407392_3730758058167500800_n.jpg"", + ""broadcast_owner"": { + ""pk"": 1383543459, + ""username"": ""780ir"", + ""full_name"": ""\u0647\u0641\u0640 \u0647\u0634\u062a\u0627\u062f | #\u0667\u0668\u0660*"", + ""is_private"": false, + ""profile_pic_url"": ""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/21980259_360063074444115_3710275616830914560_n.jpg"", + ""profile_pic_id"": ""1532382334209046138_1383543459"", + ""friendship_status"": { + ""following"": true, + ""followed_by"": false, + ""blocking"": false, + ""is_private"": false, + ""incoming_request"": false, + ""outgoing_request"": false, + ""is_bestie"": false + }, + ""is_verified"": false + }, + ""published_time"": 1510852005, + ""media_id"": ""1649468551528436844_1383543459"", + ""broadcast_message"": """", + ""organic_tracking_token"": ""eyJ2ZXJzaW9uIjo1LCJwYXlsb2FkIjp7ImlzX2FuYWx5dGljc190cmFja2VkIjp0cnVlLCJ1dWlkIjoiNWM5NGNlZjkxYWViNDYwMzkzNWFhZDMxOTQ2NjMyNjMxNjQ5NDY4NTUxNTI4NDM2ODQ0Iiwic2VydmVyX3Rva2VuIjoiMTUxMDkxNzMxMjM2OXwxNjQ5NDY4NTUxNTI4NDM2ODQ0fDE2NDc3MTg0MzJ8NjM2ODAyZmVjMzA5MDBjN2YzZDY3ZWQzZTgxODliYmM5M2ViZWI4MTFiYzIwNzdhMzM2ZjMxMDI4ZjIzYWJlMiJ9LCJzaWduYXR1cmUiOiIifQ=="" + }, + { + ""id"": 17883870826134571, + ""broadcast_status"": ""post_live"", + ""dash_manifest"": ""\u003cMPD xmlns=\""urn:mpeg:dash:schema:mpd:2011\"" minBufferTime=\""PT1.500S\"" type=\""static\"" mediaPresentationDuration=\""PT0H14M6.835S\"" maxSegmentDuration=\""PT0H0M2.000S\"" profiles=\""urn:mpeg:dash:profile:isoff-on-demand:2011,http://dashif.org/guidelines/dash264\""\u003e\u003cPeriod duration=\""PT0H14M6.835S\""\u003e\u003cAdaptationSet segmentAlignment=\""true\"" maxWidth=\""396\"" maxHeight=\""704\"" maxFrameRate=\""16000/528\"" par=\""396:704\"" lang=\""und\"" subsegmentAlignment=\""true\"" subsegmentStartsWithSAP=\""1\""\u003e\u003cRepresentation id=\""17892232666080922v\"" mimeType=\""video/mp4\"" codecs=\""avc1.4d401f\"" width=\""396\"" height=\""704\"" frameRate=\""16000/528\"" sar=\""1:1\"" startWithSAP=\""1\"" bandwidth=\""537007\"" FBQualityClass=\""sd\"" FBQualityLabel=\""396w\""\u003e\u003cBaseURL\u003ehttps://scontent-cdg2-1.cdninstagram.com/t72.12950-16/10000000_170347730220671_9006027248360226816_n.mp4\u003c/BaseURL\u003e\u003cSegmentBase indexRangeExact=\""true\"" indexRange=\""899-6054\""\u003e\u003cInitialization range=\""0-898\""/\u003e\u003c/SegmentBase\u003e\u003c/Representation\u003e\u003c/AdaptationSet\u003e\u003cAdaptationSet segmentAlignment=\""true\"" lang=\""und\"" subsegmentAlignment=\""true\"" subsegmentStartsWithSAP=\""1\""\u003e\u003cRepresentation id=\""17892232666080922a\"" mimeType=\""audio/mp4\"" codecs=\""mp4a.40.2\"" audioSamplingRate=\""44100\"" startWithSAP=\""1\"" bandwidth=\""50316\""\u003e\u003cAudioChannelConfiguration schemeIdUri=\""urn:mpeg:dash:23003:3:audio_channel_configuration:2011\"" value=\""2\""/\u003e\u003cBaseURL\u003ehttps://scontent-cdg2-1.cdninstagram.com/t72.12950-16/18743538_2044045792491235_3540743782959939584_n.mp4\u003c/BaseURL\u003e\u003cSegmentBase indexRangeExact=\""true\"" indexRange=\""835-5966\""\u003e\u003cInitialization range=\""0-834\""/\u003e\u003c/SegmentBase\u003e\u003c/Representation\u003e\u003c/AdaptationSet\u003e\u003c/Period\u003e\u003c/MPD\u003e"", + ""expire_at"": 1510945469, + ""encoding_tag"": ""instagram_dash_remuxed"", + ""internal_only"": false, + ""number_of_qualities"": 1, + ""cover_frame_url"": ""https://scontent-cdg2-1.cdninstagram.com/t51.9792-15/16751331_1936086796654040_1708202798216118272_n.jpg"", + ""broadcast_owner"": { + ""pk"": 1383543459, + ""username"": ""780ir"", + ""full_name"": ""\u0647\u0641\u0640 \u0647\u0634\u062a\u0627\u062f | #\u0667\u0668\u0660*"", + ""is_private"": false, + ""profile_pic_url"": ""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/21980259_360063074444115_3710275616830914560_n.jpg"", + ""profile_pic_id"": ""1532382334209046138_1383543459"", + ""friendship_status"": { + ""following"": true, + ""followed_by"": false, + ""blocking"": false, + ""is_private"": false, + ""incoming_request"": false, + ""outgoing_request"": false, + ""is_bestie"": false + }, + ""is_verified"": false + }, + ""published_time"": 1510858155, + ""media_id"": ""1649520175491644674_1383543459"", + ""broadcast_message"": """", + ""organic_tracking_token"": ""eyJ2ZXJzaW9uIjo1LCJwYXlsb2FkIjp7ImlzX2FuYWx5dGljc190cmFja2VkIjp0cnVlLCJ1dWlkIjoiNWM5NGNlZjkxYWViNDYwMzkzNWFhZDMxOTQ2NjMyNjMxNjQ5NTIwMTc1NDkxNjQ0Njc0Iiwic2VydmVyX3Rva2VuIjoiMTUxMDkxNzMxMjM3NnwxNjQ5NTIwMTc1NDkxNjQ0Njc0fDE2NDc3MTg0MzJ8MGE5OTlhNWY1NjM2ZDM2ZmMwNWYwZDFhYWNiMGUzY2QyNzhlOTY1NGE4YjY4MmQ0YzMwMGIyZWNkY2U0MDFmMiJ9LCJzaWduYXR1cmUiOiIifQ=="" + }, + { + ""id"": 17881002145166791, + ""broadcast_status"": ""post_live"", + ""dash_manifest"": ""\u003cMPD xmlns=\""urn:mpeg:dash:schema:mpd:2011\"" minBufferTime=\""PT1.500S\"" type=\""static\"" mediaPresentationDuration=\""PT0H14M30.033S\"" maxSegmentDuration=\""PT0H0M2.000S\"" profiles=\""urn:mpeg:dash:profile:isoff-on-demand:2011,http://dashif.org/guidelines/dash264\""\u003e\u003cPeriod duration=\""PT0H14M30.033S\""\u003e\u003cAdaptationSet segmentAlignment=\""true\"" maxWidth=\""396\"" maxHeight=\""704\"" maxFrameRate=\""16000/544\"" par=\""396:704\"" lang=\""und\"" subsegmentAlignment=\""true\"" subsegmentStartsWithSAP=\""1\""\u003e\u003cRepresentation id=\""17908553083038374v\"" mimeType=\""video/mp4\"" codecs=\""avc1.4d401f\"" width=\""396\"" height=\""704\"" frameRate=\""16000/544\"" sar=\""1:1\"" startWithSAP=\""1\"" bandwidth=\""772699\"" FBQualityClass=\""sd\"" FBQualityLabel=\""396w\""\u003e\u003cBaseURL\u003ehttps://scontent-cdg2-1.cdninstagram.com/t72.12950-16/10000000_1950041318580288_3640943338456088576_n.mp4\u003c/BaseURL\u003e\u003cSegmentBase indexRangeExact=\""true\"" indexRange=\""899-6174\""\u003e\u003cInitialization range=\""0-898\""/\u003e\u003c/SegmentBase\u003e\u003c/Representation\u003e\u003c/AdaptationSet\u003e\u003cAdaptationSet segmentAlignment=\""true\"" lang=\""und\"" subsegmentAlignment=\""true\"" subsegmentStartsWithSAP=\""1\""\u003e\u003cRepresentation id=\""17908553083038374a\"" mimeType=\""audio/mp4\"" codecs=\""mp4a.40.2\"" audioSamplingRate=\""44100\"" startWithSAP=\""1\"" bandwidth=\""50308\""\u003e\u003cAudioChannelConfiguration schemeIdUri=\""urn:mpeg:dash:23003:3:audio_channel_configuration:2011\"" value=\""2\""/\u003e\u003cBaseURL\u003ehttps://scontent-cdg2-1.cdninstagram.com/t72.12950-16/19097229_134461557323949_4728992527447752704_n.mp4\u003c/BaseURL\u003e\u003cSegmentBase indexRangeExact=\""true\"" indexRange=\""835-6098\""\u003e\u003cInitialization range=\""0-834\""/\u003e\u003c/SegmentBase\u003e\u003c/Representation\u003e\u003c/AdaptationSet\u003e\u003c/Period\u003e\u003c/MPD\u003e"", + ""expire_at"": 1510920674, + ""encoding_tag"": ""instagram_dash_remuxed"", + ""internal_only"": false, + ""number_of_qualities"": 1, + ""cover_frame_url"": ""https://scontent-cdg2-1.cdninstagram.com/t51.9792-15/14964412_932613113564850_5738793949246521344_n.jpg"", + ""broadcast_owner"": { + ""pk"": 1383543459, + ""username"": ""780ir"", + ""full_name"": ""\u0647\u0641\u0640 \u0647\u0634\u062a\u0627\u062f | #\u0667\u0668\u0660*"", + ""is_private"": false, + ""profile_pic_url"": ""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/21980259_360063074444115_3710275616830914560_n.jpg"", + ""profile_pic_id"": ""1532382334209046138_1383543459"", + ""friendship_status"": { + ""following"": true, + ""followed_by"": false, + ""blocking"": false, + ""is_private"": false, + ""incoming_request"": false, + ""outgoing_request"": false, + ""is_bestie"": false + }, + ""is_verified"": false + }, + ""published_time"": 1510833390, + ""media_id"": ""1649312402740902617_1383543459"", + ""broadcast_message"": """", + ""organic_tracking_token"": ""eyJ2ZXJzaW9uIjo1LCJwYXlsb2FkIjp7ImlzX2FuYWx5dGljc190cmFja2VkIjp0cnVlLCJ1dWlkIjoiNWM5NGNlZjkxYWViNDYwMzkzNWFhZDMxOTQ2NjMyNjMxNjQ5MzEyNDAyNzQwOTAyNjE3Iiwic2VydmVyX3Rva2VuIjoiMTUxMDkxNzMxMjM4NnwxNjQ5MzEyNDAyNzQwOTAyNjE3fDE2NDc3MTg0MzJ8ZmUwYWNiYjdiNmViOTViNWU3YThjYzVlNzMxMGQ5ZTY1ZTIzZWY0MWI2NTdmNWZkZDA5Yjg3NTQzMDI2MWY0ZCJ9LCJzaWduYXR1cmUiOiIifQ=="" + }, + { + ""id"": 17909222266016333, + ""broadcast_status"": ""post_live"", + ""dash_manifest"": ""\u003cMPD xmlns=\""urn:mpeg:dash:schema:mpd:2011\"" minBufferTime=\""PT1.500S\"" type=\""static\"" mediaPresentationDuration=\""PT0H8M20.366S\"" maxSegmentDuration=\""PT0H0M2.000S\"" profiles=\""urn:mpeg:dash:profile:isoff-on-demand:2011,http://dashif.org/guidelines/dash264\""\u003e\u003cPeriod duration=\""PT0H8M20.366S\""\u003e\u003cAdaptationSet segmentAlignment=\""true\"" maxWidth=\""396\"" maxHeight=\""704\"" maxFrameRate=\""16000/528\"" par=\""396:704\"" lang=\""und\"" subsegmentAlignment=\""true\"" subsegmentStartsWithSAP=\""1\""\u003e\u003cRepresentation id=\""17848696471221278v\"" mimeType=\""video/mp4\"" codecs=\""avc1.4d401f\"" width=\""396\"" height=\""704\"" frameRate=\""16000/528\"" sar=\""1:1\"" startWithSAP=\""1\"" bandwidth=\""766114\"" FBQualityClass=\""sd\"" FBQualityLabel=\""396w\""\u003e\u003cBaseURL\u003ehttps://scontent-cdg2-1.cdninstagram.com/t72.12950-16/10000000_130426674268267_3521803917782417408_n.mp4\u003c/BaseURL\u003e\u003cSegmentBase indexRangeExact=\""true\"" indexRange=\""899-4014\""\u003e\u003cInitialization range=\""0-898\""/\u003e\u003c/SegmentBase\u003e\u003c/Representation\u003e\u003c/AdaptationSet\u003e\u003cAdaptationSet segmentAlignment=\""true\"" lang=\""und\"" subsegmentAlignment=\""true\"" subsegmentStartsWithSAP=\""1\""\u003e\u003cRepresentation id=\""17848696471221278a\"" mimeType=\""audio/mp4\"" codecs=\""mp4a.40.2\"" audioSamplingRate=\""44100\"" startWithSAP=\""1\"" bandwidth=\""50493\""\u003e\u003cAudioChannelConfiguration schemeIdUri=\""urn:mpeg:dash:23003:3:audio_channel_configuration:2011\"" value=\""2\""/\u003e\u003cBaseURL\u003ehttps://scontent-cdg2-1.cdninstagram.com/t72.12950-16/19104379_545857249086594_8255680261132386304_n.mp4\u003c/BaseURL\u003e\u003cSegmentBase indexRangeExact=\""true\"" indexRange=\""835-3878\""\u003e\u003cInitialization range=\""0-834\""/\u003e\u003c/SegmentBase\u003e\u003c/Representation\u003e\u003c/AdaptationSet\u003e\u003c/Period\u003e\u003c/MPD\u003e"", + ""expire_at"": 1510987021, + ""encoding_tag"": ""instagram_dash_remuxed"", + ""internal_only"": false, + ""number_of_qualities"": 1, + ""cover_frame_url"": ""https://scontent-cdg2-1.cdninstagram.com/t51.9792-15/16994044_137486973681364_2459237027455959040_n.jpg"", + ""broadcast_owner"": { + ""pk"": 1383543459, + ""username"": ""780ir"", + ""full_name"": ""\u0647\u0641\u0640 \u0647\u0634\u062a\u0627\u062f | #\u0667\u0668\u0660*"", + ""is_private"": false, + ""profile_pic_url"": ""https://scontent-cdg2-1.cdninstagram.com/t51.2885-19/s150x150/21980259_360063074444115_3710275616830914560_n.jpg"", + ""profile_pic_id"": ""1532382334209046138_1383543459"", + ""friendship_status"": { + ""following"": true, + ""followed_by"": false, + ""blocking"": false, + ""is_private"": false, + ""incoming_request"": false, + ""outgoing_request"": false, + ""is_bestie"": false + }, + ""is_verified"": false + }, + ""published_time"": 1510900081, + ""media_id"": ""1649871864962943492_1383543459"", + ""broadcast_message"": """", + ""organic_tracking_token"": ""eyJ2ZXJzaW9uIjo1LCJwYXlsb2FkIjp7ImlzX2FuYWx5dGljc190cmFja2VkIjp0cnVlLCJ1dWlkIjoiNWM5NGNlZjkxYWViNDYwMzkzNWFhZDMxOTQ2NjMyNjMxNjQ5ODcxODY0OTYyOTQzNDkyIiwic2VydmVyX3Rva2VuIjoiMTUxMDkxNzMxMjM5M3wxNjQ5ODcxODY0OTYyOTQzNDkyfDE2NDc3MTg0MzJ8NTdlMThmMjZjZGZmZTE4ZTI4OGU1ODU3NDAzNzFkMzhmZmU1ZWMxYmI0NzYzOTcyMTYyNzM5YTBhMjczYTQ5YSJ9LCJzaWduYXR1cmUiOiIifQ=="" + }], + ""last_seen_broadcast_ts"": 0, + ""ranked_position"": 15, + ""seen_ranked_position"": 0, + ""muted"": false, + ""can_reply"": false, + ""can_reshare"": false + }] + }, + ""story_ranking_token"": ""6ed23f2e-b27b-4dc3-beba-a19a0439e384"", + ""broadcasts"": [], + ""face_filter_nux_version"": 4, + ""has_new_nux_story"": false, + ""status"": ""ok"" +}"; + + [Fact] + public void ConvertReelFeedTest() + { + var storyFeedResponse = JsonConvert.DeserializeObject(testJson); + var fabric = ConvertersHelper.GetDefaultFabric(); + var result = fabric.GetStoryFeedConverter(storyFeedResponse).Convert(); + Assert.NotNull(result); + } + } +} \ No newline at end of file diff --git a/InstaSharper.Tests/Infrastructure/UserConverterTest.cs b/InstaSharper.Tests/Infrastructure/UserConverterTest.cs new file mode 100644 index 00000000..808d4b1c --- /dev/null +++ b/InstaSharper.Tests/Infrastructure/UserConverterTest.cs @@ -0,0 +1,703 @@ +using System.Linq; +using InstaSharper.Classes.ResponseWrappers; +using InstaSharper.Helpers; +using Newtonsoft.Json; +using Xunit; + +namespace InstaSharper.Tests.Infrastructure +{ + [Trait("Category", "Infrastructure")] + public class UserConverterTest + { + private const string testJson = @"{ + ""num_results"": 36, + ""users"": [{ + ""pk"": 1816494776, + ""username"": ""codziennysuchar"", + ""full_name"": ""Codzienny Suchar Humor Memy"", + ""is_private"": false, + ""profile_pic_url"": ""https://scontent-frt3-2.cdninstagram.com/t51.2885-19/s150x150/11244021_1016245205072659_328649662_a.jpg"", + ""friendship_status"": { + ""following"": true, + ""is_private"": false, + ""incoming_request"": false, + ""outgoing_request"": false, + ""is_bestie"": false + }, + ""is_verified"": false, + ""has_anonymous_profile_picture"": false, + ""follower_count"": 463666, + ""byline"": ""463k followers"", + ""social_context"": ""Following"", + ""search_social_context"": ""Following"", + ""mutual_followers_count"": 8.0, + ""unseen_count"": 1 + }, { + ""pk"": 4390037188, + ""username"": ""_najlepsze.suchary"", + ""full_name"": ""Codzienny_suchar"", + ""is_private"": false, + ""profile_pic_url"": ""https://scontent-frt3-2.cdninstagram.com/t51.2885-19/s150x150/14582494_1641939916100038_3059248674581250048_n.jpg"", + ""profile_pic_id"": ""1423287750286768474_4390037188"", + ""friendship_status"": { + ""following"": false, + ""is_private"": false, + ""incoming_request"": false, + ""outgoing_request"": false, + ""is_bestie"": false + }, + ""is_verified"": false, + ""has_anonymous_profile_picture"": false, + ""follower_count"": 3398, + ""byline"": ""3398 followers"", + ""mutual_followers_count"": 0.0 + }, { + ""pk"": 2071863124, + ""username"": ""codzienny_suchar"", + ""full_name"": ""codzienny suchar"", + ""is_private"": false, + ""profile_pic_url"": ""https://scontent-frt3-2.cdninstagram.com/t51.2885-19/10518138_478803852289501_1249129511_a.jpg"", + ""friendship_status"": { + ""following"": false, + ""is_private"": false, + ""incoming_request"": false, + ""outgoing_request"": false, + ""is_bestie"": false + }, + ""is_verified"": false, + ""has_anonymous_profile_picture"": false, + ""follower_count"": 565, + ""byline"": ""565 followers"", + ""mutual_followers_count"": 4.23 + }, { + ""pk"": 2292120036, + ""username"": ""codziennysuchar_"", + ""full_name"": ""CodziennySucharek\u00a2"", + ""is_private"": false, + ""profile_pic_url"": ""https://scontent-frt3-2.cdninstagram.com/t51.2885-19/s150x150/12071063_791734954269121_1518685845_a.jpg"", + ""friendship_status"": { + ""following"": false, + ""is_private"": false, + ""incoming_request"": false, + ""outgoing_request"": false, + ""is_bestie"": false + }, + ""is_verified"": false, + ""has_anonymous_profile_picture"": false, + ""follower_count"": 85, + ""byline"": ""85 followers"", + ""mutual_followers_count"": 0.0 + }, { + ""pk"": 4299660758, + ""username"": ""codziennysuchar143"", + ""full_name"": ""#codziennysuchar"", + ""is_private"": false, + ""profile_pic_url"": ""https://scontent-frt3-2.cdninstagram.com/t51.2885-19/s150x150/14712311_1386416128048966_6072005659623161856_a.jpg"", + ""profile_pic_id"": ""1410766283506563741_4299660758"", + ""friendship_status"": { + ""following"": false, + ""is_private"": false, + ""incoming_request"": false, + ""outgoing_request"": false, + ""is_bestie"": false + }, + ""is_verified"": false, + ""has_anonymous_profile_picture"": false, + ""follower_count"": 45, + ""byline"": ""45 followers"", + ""mutual_followers_count"": 0.0 + }, { + ""pk"": 6223038622, + ""username"": ""_codzienny_suchar_"", + ""full_name"": ""codzienny suchar"", + ""is_private"": false, + ""profile_pic_url"": ""https://scontent-frt3-2.cdninstagram.com/t51.2885-19/s150x150/22580089_357978407993952_2464035214595194880_n.jpg"", + ""profile_pic_id"": ""1626063587179128120_6223038622"", + ""friendship_status"": { + ""following"": false, + ""is_private"": false, + ""incoming_request"": false, + ""outgoing_request"": false, + ""is_bestie"": false + }, + ""is_verified"": false, + ""has_anonymous_profile_picture"": false, + ""follower_count"": 32, + ""byline"": ""32 followers"", + ""mutual_followers_count"": 0.0 + }, { + ""pk"": 3186683532, + ""username"": ""_codzienny_suchar._"", + ""full_name"": """", + ""is_private"": false, + ""profile_pic_url"": ""https://scontent-frt3-2.cdninstagram.com/t51.2885-19/s150x150/13129611_465250017018877_1876717231_a.jpg"", + ""profile_pic_id"": ""1239795253686777462_3186683532"", + ""friendship_status"": { + ""following"": false, + ""is_private"": false, + ""incoming_request"": false, + ""outgoing_request"": false, + ""is_bestie"": false + }, + ""is_verified"": false, + ""has_anonymous_profile_picture"": false, + ""follower_count"": 40, + ""byline"": ""40 followers"", + ""mutual_followers_count"": 0.0 + }, { + ""pk"": 3479771356, + ""username"": ""_codziennysuchar_"", + ""full_name"": ""\ud83d\ude00 Codzienny suchar! \ud83d\ude00"", + ""is_private"": false, + ""profile_pic_url"": ""https://scontent-frt3-2.cdninstagram.com/t51.2885-19/s150x150/13531793_1617545068557916_1945776659_a.jpg"", + ""profile_pic_id"": ""1283581678362859682_3479771356"", + ""friendship_status"": { + ""following"": false, + ""is_private"": false, + ""incoming_request"": false, + ""outgoing_request"": false, + ""is_bestie"": false + }, + ""is_verified"": false, + ""has_anonymous_profile_picture"": false, + ""follower_count"": 19, + ""byline"": ""19 followers"", + ""mutual_followers_count"": 0.0 + }, { + ""pk"": 2969669697, + ""username"": ""codzienny__suchar"", + ""full_name"": ""codziennysuchar"", + ""is_private"": false, + ""profile_pic_url"": ""https://scontent-frt3-2.cdninstagram.com/t51.2885-19/s150x150/11349423_955148704532172_366023392_a.jpg"", + ""friendship_status"": { + ""following"": false, + ""is_private"": false, + ""incoming_request"": false, + ""outgoing_request"": false, + ""is_bestie"": false + }, + ""is_verified"": false, + ""has_anonymous_profile_picture"": false, + ""follower_count"": 49, + ""byline"": ""49 followers"", + ""mutual_followers_count"": 0.0 + }, { + ""pk"": 2267989776, + ""username"": ""codziennysuchar1"", + ""full_name"": ""codziennysuchar"", + ""is_private"": false, + ""profile_pic_url"": ""https://instagram.ftxl1-1.fna.fbcdn.net/t51.2885-19/11906329_960233084022564_1448528159_a.jpg"", + ""friendship_status"": { + ""following"": false, + ""is_private"": false, + ""incoming_request"": false, + ""outgoing_request"": false, + ""is_bestie"": false + }, + ""is_verified"": false, + ""has_anonymous_profile_picture"": true, + ""follower_count"": 10, + ""byline"": ""10 followers"", + ""mutual_followers_count"": 0.0 + }, { + ""pk"": 2365761043, + ""username"": ""codziennysuchareg"", + ""full_name"": ""codziennysuchareg"", + ""is_private"": false, + ""profile_pic_url"": ""https://scontent-frt3-2.cdninstagram.com/t51.2885-19/s150x150/12353364_846217685497843_396153855_a.jpg"", + ""friendship_status"": { + ""following"": false, + ""is_private"": false, + ""incoming_request"": false, + ""outgoing_request"": false, + ""is_bestie"": false + }, + ""is_verified"": false, + ""has_anonymous_profile_picture"": false, + ""follower_count"": 13, + ""byline"": ""13 followers"", + ""mutual_followers_count"": 0.0 + }, { + ""pk"": 4918495064, + ""username"": ""codziennysuchar10"", + ""full_name"": ""Codzienny Suchar"", + ""is_private"": false, + ""profile_pic_url"": ""https://scontent-frt3-2.cdninstagram.com/t51.2885-19/s150x150/17265880_157360331452947_2547629150919720960_a.jpg"", + ""profile_pic_id"": ""1479246617067586083_4918495064"", + ""friendship_status"": { + ""following"": false, + ""is_private"": false, + ""incoming_request"": false, + ""outgoing_request"": false, + ""is_bestie"": false + }, + ""is_verified"": false, + ""has_anonymous_profile_picture"": false, + ""follower_count"": 40, + ""byline"": ""40 followers"", + ""mutual_followers_count"": 0.0 + }, { + ""pk"": 2898853357, + ""username"": ""jdisowskyyx"", + ""full_name"": ""Codziennysuchar LOL\ud83d\ude1c\ud83d\ude04\ud83d\ude0e"", + ""is_private"": true, + ""profile_pic_url"": ""https://scontent-frt3-2.cdninstagram.com/t51.2885-19/s150x150/13092489_1032479350180187_480622312_a.jpg"", + ""profile_pic_id"": ""1240517337186335492_2898853357"", + ""friendship_status"": { + ""following"": false, + ""is_private"": true, + ""incoming_request"": false, + ""outgoing_request"": false, + ""is_bestie"": false + }, + ""is_verified"": false, + ""has_anonymous_profile_picture"": false, + ""follower_count"": 24, + ""byline"": ""24 followers"", + ""mutual_followers_count"": 0.0 + }, { + ""pk"": 2288964948, + ""username"": ""codzienny.suchar"", + ""full_name"": ""Suchar Codzienny"", + ""is_private"": false, + ""profile_pic_url"": ""https://scontent-frt3-2.cdninstagram.com/t51.2885-19/s150x150/12269875_1617130481885815_92792324_a.jpg"", + ""friendship_status"": { + ""following"": false, + ""is_private"": false, + ""incoming_request"": false, + ""outgoing_request"": false, + ""is_bestie"": false + }, + ""is_verified"": false, + ""has_anonymous_profile_picture"": false, + ""follower_count"": 64, + ""byline"": ""64 followers"", + ""mutual_followers_count"": ""wtf going on here 10"" + }, { + ""pk"": 2869035614, + ""username"": ""codzienny.sucharek"", + ""full_name"": ""Codzienny.sucharek"", + ""is_private"": false, + ""profile_pic_url"": ""https://scontent-frt3-2.cdninstagram.com/t51.2885-19/s150x150/12547256_584493841706454_1681254975_a.jpg"", + ""friendship_status"": { + ""following"": false, + ""is_private"": false, + ""incoming_request"": false, + ""outgoing_request"": false, + ""is_bestie"": false + }, + ""is_verified"": false, + ""has_anonymous_profile_picture"": false, + ""follower_count"": 21, + ""byline"": ""21 followers"", + ""mutual_followers_count"": 0.0 + }, { + ""pk"": 4373567288, + ""username"": ""codzienny_suchar_dnia"", + ""full_name"": ""Klara"", + ""is_private"": false, + ""profile_pic_url"": ""https://scontent-frt3-2.cdninstagram.com/t51.2885-19/s150x150/15803493_328997667500308_2950628599478091776_a.jpg"", + ""profile_pic_id"": ""1422395872512007684_4373567288"", + ""friendship_status"": { + ""following"": false, + ""is_private"": false, + ""incoming_request"": false, + ""outgoing_request"": false, + ""is_bestie"": false + }, + ""is_verified"": false, + ""has_anonymous_profile_picture"": false, + ""follower_count"": 6, + ""byline"": ""6 followers"", + ""mutual_followers_count"": 0.0 + }, { + ""pk"": 3141115423, + ""username"": ""taylornator1989"", + ""full_name"": ""POLECAM codziennysuchar!!!"", + ""is_private"": false, + ""profile_pic_url"": ""https://scontent-frt3-2.cdninstagram.com/t51.2885-19/s150x150/13187945_283320095335534_1866674286_a.jpg"", + ""profile_pic_id"": ""1260222356237230343_3141115423"", + ""friendship_status"": { + ""following"": false, + ""is_private"": false, + ""incoming_request"": false, + ""outgoing_request"": false, + ""is_bestie"": false + }, + ""is_verified"": false, + ""has_anonymous_profile_picture"": false, + ""follower_count"": 35, + ""byline"": ""35 followers"", + ""mutual_followers_count"": 0.0 + }, { + ""pk"": 4670037348, + ""username"": ""codzienny___suchar"", + ""full_name"": ""Suchary i Memy"", + ""is_private"": false, + ""profile_pic_url"": ""https://scontent-frt3-2.cdninstagram.com/t51.2885-19/s150x150/16465599_1842829409324069_1856675956463239168_a.jpg"", + ""profile_pic_id"": ""1453492070248861927_4670037348"", + ""friendship_status"": { + ""following"": false, + ""is_private"": false, + ""incoming_request"": false, + ""outgoing_request"": false, + ""is_bestie"": false + }, + ""is_verified"": false, + ""has_anonymous_profile_picture"": false, + ""follower_count"": 10, + ""byline"": ""10 followers"", + ""mutual_followers_count"": 0.0 + }, { + ""pk"": 3593042169, + ""username"": ""_codziennysuchar"", + ""full_name"": ""_codziennysuchar"", + ""is_private"": false, + ""profile_pic_url"": ""https://scontent-frt3-2.cdninstagram.com/t51.2885-19/s150x150/13767601_920771888051908_1782552445_a.jpg"", + ""profile_pic_id"": ""1303857380802939487_3593042169"", + ""friendship_status"": { + ""following"": false, + ""is_private"": false, + ""incoming_request"": false, + ""outgoing_request"": false, + ""is_bestie"": false + }, + ""is_verified"": false, + ""has_anonymous_profile_picture"": false, + ""follower_count"": 16, + ""byline"": ""16 followers"", + ""mutual_followers_count"": 0.0 + }, { + ""pk"": 6021270917, + ""username"": ""codzienny_suchar2004"", + ""full_name"": "".."", + ""is_private"": true, + ""profile_pic_url"": ""https://instagram.ftxl1-1.fna.fbcdn.net/t51.2885-19/11906329_960233084022564_1448528159_a.jpg"", + ""friendship_status"": { + ""following"": false, + ""is_private"": true, + ""incoming_request"": false, + ""outgoing_request"": false, + ""is_bestie"": false + }, + ""is_verified"": false, + ""has_anonymous_profile_picture"": true, + ""follower_count"": 10, + ""byline"": ""10 followers"", + ""mutual_followers_count"": 0.0 + }, { + ""pk"": 3431371153, + ""username"": ""codzienny.suchar_"", + ""full_name"": ""Codzienny Suchar"", + ""is_private"": false, + ""profile_pic_url"": ""https://scontent-frt3-2.cdninstagram.com/t51.2885-19/s150x150/13385911_1737718929816684_1453121998_a.jpg"", + ""profile_pic_id"": ""1276045232836840047_3431371153"", + ""friendship_status"": { + ""following"": false, + ""is_private"": false, + ""incoming_request"": false, + ""outgoing_request"": false, + ""is_bestie"": false + }, + ""is_verified"": false, + ""has_anonymous_profile_picture"": false, + ""follower_count"": 8, + ""byline"": ""8 followers"", + ""mutual_followers_count"": 0.0 + }, { + ""pk"": 2981506650, + ""username"": ""_codzienny_suchar"", + ""full_name"": """", + ""is_private"": false, + ""profile_pic_url"": ""https://scontent-frt3-2.cdninstagram.com/t51.2885-19/s150x150/10817631_479740075550561_1982476853_a.jpg"", + ""friendship_status"": { + ""following"": false, + ""is_private"": false, + ""incoming_request"": false, + ""outgoing_request"": false, + ""is_bestie"": false + }, + ""is_verified"": false, + ""has_anonymous_profile_picture"": false, + ""follower_count"": 10, + ""byline"": ""10 followers"", + ""mutual_followers_count"": 0.0 + }, { + ""pk"": 5892966471, + ""username"": ""codzienny_suchareczek"", + ""full_name"": ""\ud83d\ude02codzinna dawka \u015bmiechu\ud83d\ude02"", + ""is_private"": false, + ""profile_pic_url"": ""https://scontent-frt3-2.cdninstagram.com/t51.2885-19/s150x150/20904967_1343774645741537_2922481076037222400_a.jpg"", + ""profile_pic_id"": ""1583925224255829184_5892966471"", + ""friendship_status"": { + ""following"": false, + ""is_private"": false, + ""incoming_request"": false, + ""outgoing_request"": false, + ""is_bestie"": false + }, + ""is_verified"": false, + ""has_anonymous_profile_picture"": false, + ""follower_count"": 18, + ""byline"": ""18 followers"", + ""mutual_followers_count"": 0.0 + }, { + ""pk"": 3015019252, + ""username"": ""codzienny__sucharek"", + ""full_name"": """", + ""is_private"": false, + ""profile_pic_url"": ""https://scontent-frt3-2.cdninstagram.com/t51.2885-19/s150x150/10632224_471179623073948_2122634524_a.jpg"", + ""friendship_status"": { + ""following"": false, + ""is_private"": false, + ""incoming_request"": false, + ""outgoing_request"": false, + ""is_bestie"": false + }, + ""is_verified"": false, + ""has_anonymous_profile_picture"": false, + ""follower_count"": 4, + ""byline"": ""4 followers"", + ""mutual_followers_count"": 0.0 + }, { + ""pk"": 2141568556, + ""username"": ""codzienny_suchar_1"", + ""full_name"": ""codzienny Suchar"", + ""is_private"": true, + ""profile_pic_url"": ""https://scontent-frt3-2.cdninstagram.com/t51.2885-19/s150x150/12276961_1527174690937301_745676031_a.jpg"", + ""friendship_status"": { + ""following"": false, + ""is_private"": true, + ""incoming_request"": false, + ""outgoing_request"": false, + ""is_bestie"": false + }, + ""is_verified"": false, + ""has_anonymous_profile_picture"": false, + ""follower_count"": 64, + ""byline"": ""64 followers"", + ""mutual_followers_count"": 0.0 + }, { + ""pk"": 2248225276, + ""username"": ""codzienny_sucharek"", + ""full_name"": """", + ""is_private"": false, + ""profile_pic_url"": ""https://scontent-frt3-2.cdninstagram.com/t51.2885-19/s150x150/12338890_556623717824871_1971485021_a.jpg"", + ""friendship_status"": { + ""following"": false, + ""is_private"": false, + ""incoming_request"": false, + ""outgoing_request"": false, + ""is_bestie"": false + }, + ""is_verified"": false, + ""has_anonymous_profile_picture"": false, + ""follower_count"": 88, + ""byline"": ""88 followers"", + ""mutual_followers_count"": 0.0 + }, { + ""pk"": 2182521594, + ""username"": ""codziennysucharek"", + ""full_name"": ""codziennysucharek"", + ""is_private"": false, + ""profile_pic_url"": ""https://scontent-frt3-2.cdninstagram.com/t51.2885-19/s150x150/11950493_1021972774500543_1552776432_a.jpg"", + ""friendship_status"": { + ""following"": false, + ""is_private"": false, + ""incoming_request"": false, + ""outgoing_request"": false, + ""is_bestie"": false + }, + ""is_verified"": false, + ""has_anonymous_profile_picture"": false, + ""follower_count"": 26, + ""byline"": ""26 followers"", + ""mutual_followers_count"": 0.0 + }, { + ""pk"": 2246594474, + ""username"": ""codzienny_suchar_"", + ""full_name"": ""suchar"", + ""is_private"": false, + ""profile_pic_url"": ""https://instagram.ftxl1-1.fna.fbcdn.net/t51.2885-19/11906329_960233084022564_1448528159_a.jpg"", + ""friendship_status"": { + ""following"": false, + ""is_private"": false, + ""incoming_request"": false, + ""outgoing_request"": false, + ""is_bestie"": false + }, + ""is_verified"": false, + ""has_anonymous_profile_picture"": true, + ""follower_count"": 6, + ""byline"": ""6 followers"", + ""mutual_followers_count"": 0.0 + }, { + ""pk"": 3030330532, + ""username"": ""_____codziennysuchar_____"", + ""full_name"": """", + ""is_private"": false, + ""profile_pic_url"": ""https://instagram.ftxl1-1.fna.fbcdn.net/t51.2885-19/11906329_960233084022564_1448528159_a.jpg"", + ""friendship_status"": { + ""following"": false, + ""is_private"": false, + ""incoming_request"": false, + ""outgoing_request"": false, + ""is_bestie"": false + }, + ""is_verified"": false, + ""has_anonymous_profile_picture"": true, + ""follower_count"": 0, + ""byline"": ""0 followers"", + ""mutual_followers_count"": 0.0 + }, { + ""pk"": 3281163757, + ""username"": ""codziennysuchar2"", + ""full_name"": ""Suchar Codzienny"", + ""is_private"": false, + ""profile_pic_url"": ""https://scontent-frt3-2.cdninstagram.com/t51.2885-19/s150x150/13258846_1692403691024457_272839865_a.jpg"", + ""profile_pic_id"": ""1261036094132762712_3281163757"", + ""friendship_status"": { + ""following"": false, + ""is_private"": false, + ""incoming_request"": false, + ""outgoing_request"": false, + ""is_bestie"": false + }, + ""is_verified"": false, + ""has_anonymous_profile_picture"": false, + ""follower_count"": 1, + ""byline"": ""1 follower"", + ""mutual_followers_count"": 0.0 + }, { + ""pk"": 3214950254, + ""username"": ""codziennysucharekkk"", + ""full_name"": ""codzienny-sucharek"", + ""is_private"": false, + ""profile_pic_url"": ""https://instagram.ftxl1-1.fna.fbcdn.net/t51.2885-19/11906329_960233084022564_1448528159_a.jpg"", + ""friendship_status"": { + ""following"": false, + ""is_private"": false, + ""incoming_request"": false, + ""outgoing_request"": false, + ""is_bestie"": false + }, + ""is_verified"": false, + ""has_anonymous_profile_picture"": true, + ""follower_count"": 0, + ""byline"": ""0 followers"", + ""mutual_followers_count"": 0.0 + }, { + ""pk"": 3698151442, + ""username"": ""codziennysuchar8"", + ""full_name"": ""Suchar Dnia"", + ""is_private"": false, + ""profile_pic_url"": ""https://scontent-frt3-2.cdninstagram.com/t51.2885-19/s150x150/14073336_1683619201961289_1832154875_a.jpg"", + ""profile_pic_id"": ""1322280126667234202_3698151442"", + ""friendship_status"": { + ""following"": false, + ""is_private"": false, + ""incoming_request"": false, + ""outgoing_request"": false, + ""is_bestie"": false + }, + ""is_verified"": false, + ""has_anonymous_profile_picture"": false, + ""follower_count"": 6, + ""byline"": ""6 followers"", + ""mutual_followers_count"": 0.0 + }, { + ""pk"": 5352527941, + ""username"": ""codzienny_suchar.pl"", + ""full_name"": ""Sucharek"", + ""is_private"": true, + ""profile_pic_url"": ""https://scontent-frt3-2.cdninstagram.com/t51.2885-19/s150x150/17934680_1433837510000277_1688326230736109568_a.jpg"", + ""profile_pic_id"": ""1496469540404649383_5352527941"", + ""friendship_status"": { + ""following"": false, + ""is_private"": true, + ""incoming_request"": false, + ""outgoing_request"": false, + ""is_bestie"": false + }, + ""is_verified"": false, + ""has_anonymous_profile_picture"": false, + ""follower_count"": 0, + ""byline"": ""0 followers"", + ""mutual_followers_count"": 0.0 + }, { + ""pk"": 5612429408, + ""username"": ""codzienny_suchar1"", + ""full_name"": ""codzienny suchar"", + ""is_private"": false, + ""profile_pic_url"": ""https://scontent-frt3-2.cdninstagram.com/t51.2885-19/s150x150/19227720_307764163005012_1840796191059607552_a.jpg"", + ""profile_pic_id"": ""1539108474653437325_5612429408"", + ""friendship_status"": { + ""following"": false, + ""is_private"": false, + ""incoming_request"": false, + ""outgoing_request"": false, + ""is_bestie"": false + }, + ""is_verified"": false, + ""has_anonymous_profile_picture"": false, + ""follower_count"": 0, + ""byline"": ""0 followers"", + ""mutual_followers_count"": 0.0 + }, { + ""pk"": 5571319582, + ""username"": ""codzienny_sucharxddd"", + ""full_name"": ""Codzienny suchar"", + ""is_private"": false, + ""profile_pic_url"": ""https://instagram.ftxl1-1.fna.fbcdn.net/t51.2885-19/11906329_960233084022564_1448528159_a.jpg"", + ""friendship_status"": { + ""following"": false, + ""is_private"": false, + ""incoming_request"": false, + ""outgoing_request"": false, + ""is_bestie"": false + }, + ""is_verified"": false, + ""has_anonymous_profile_picture"": true, + ""follower_count"": 2, + ""byline"": ""2 followers"", + ""mutual_followers_count"": 0.0 + }, { + ""pk"": 5867597536, + ""username"": ""codzienny_suchar_elo230"", + ""full_name"": ""myszka miki"", + ""is_private"": false, + ""profile_pic_url"": ""https://instagram.ftxl1-1.fna.fbcdn.net/t51.2885-19/11906329_960233084022564_1448528159_a.jpg"", + ""friendship_status"": { + ""following"": false, + ""is_private"": false, + ""incoming_request"": false, + ""outgoing_request"": false, + ""is_bestie"": false + }, + ""is_verified"": false, + ""has_anonymous_profile_picture"": true, + ""follower_count"": 1, + ""byline"": ""1 follower"", + ""mutual_followers_count"": 0.0 + } + ], + ""has_more"": false, + ""rank_token"": ""3b0955a3-c0b1-403a-942f-8965d0e96eaf"", + ""status"": ""ok"" +}"; + + [Theory] + [InlineData("codziennysuchar")] + [InlineData("codzienny_suchar")] + [InlineData("codzienny.suchar")] + public void UserResponseConverterTest(string username) + { + var response = JsonConvert.DeserializeObject(testJson); + var user = response.Users?.FirstOrDefault(u => u.UserName == username); + var fabric = ConvertersHelper.GetDefaultFabric(); + var converter = fabric.GetUserConverter(user); + var result = converter.Convert(); + Assert.NotNull(result); + } + } +} \ No newline at end of file diff --git a/InstaSharper.Tests/Utils/TestHelpers.cs b/InstaSharper.Tests/Utils/TestHelpers.cs index 4586ac7b..3f226500 100644 --- a/InstaSharper.Tests/Utils/TestHelpers.cs +++ b/InstaSharper.Tests/Utils/TestHelpers.cs @@ -4,35 +4,18 @@ using InstaSharper.API; using InstaSharper.API.Builder; using InstaSharper.Classes; -using InstaSharper.Classes.Android.DeviceInfo; using InstaSharper.Helpers; -using InstaSharper.Logger; using Xunit.Abstractions; namespace InstaSharper.Tests.Utils { public class TestHelpers { - public static IInstaApi GetDefaultInstaApiInstance(string username) - { - var device = AndroidDeviceGenerator.GetByName(AndroidDevices.SAMSUNG_NOTE3); - var requestMessage = ApiRequestMessage.FromDevice(device); - var apiInstance = InstaApiBuilder.CreateBuilder() - .SetUserName(username) - .UseLogger(new DebugLogger(LogLevel.All)) - .SetApiRequestMessage(requestMessage) - .Build(); - return apiInstance; - } - public static IInstaApi GetDefaultInstaApiInstance(UserSessionData user) { - var device = AndroidDeviceGenerator.GetByName(AndroidDevices.SAMSUNG_NOTE3); - var requestMessage = ApiRequestMessage.FromDevice(device); var apiInstance = InstaApiBuilder.CreateBuilder() .SetUser(user) - .SetApiRequestMessage(requestMessage) - .SetRequestDelay(TimeSpan.FromSeconds(2)) + .SetRequestDelay(TimeSpan.FromSeconds(5)) .Build(); return apiInstance; } @@ -50,12 +33,9 @@ public static IInstaApi GetProxifiedInstaApiInstance(UserSessionData user, Insta public static async Task Login(IInstaApi apiInstance, ITestOutputHelper output) { var loginResult = await apiInstance.LoginAsync(); - if (!loginResult.Succeeded) - { - output.WriteLine($"Can't login: {loginResult.Info.Message}"); - return false; - } - return true; + if (loginResult.Succeeded) return true; + output.WriteLine($"Can't login: {loginResult.Info.Message}"); + return false; } } } \ No newline at end of file diff --git a/InstaSharper/API/Builder/IInstaApiBuilder.cs b/InstaSharper/API/Builder/IInstaApiBuilder.cs index d4600539..02762f2f 100644 --- a/InstaSharper/API/Builder/IInstaApiBuilder.cs +++ b/InstaSharper/API/Builder/IInstaApiBuilder.cs @@ -8,13 +8,54 @@ namespace InstaSharper.API.Builder { public interface IInstaApiBuilder { + /// + /// Create new API instance + /// + /// API instance IInstaApi Build(); + + /// + /// Use custom logger + /// + /// IInstaLogger implementation + /// API Builder IInstaApiBuilder UseLogger(IInstaLogger logger); + + /// + /// Set specific HttpClient + /// + /// HttpClient + /// API Builder IInstaApiBuilder UseHttpClient(HttpClient httpClient); + + /// + /// Set custom HttpClientHandler to be able to use certain features, e.g Proxy and so on + /// + /// HttpClientHandler + /// API Builder IInstaApiBuilder UseHttpClientHandler(HttpClientHandler handler); - IInstaApiBuilder SetUserName(string username); + + + /// + /// Specify user login, password from here + /// + /// User auth data + /// API Builder IInstaApiBuilder SetUser(UserSessionData user); + + /// + /// Set custom request message. Used to be able to customize device info. + /// + /// Custom request message object + /// Please, do not use if you don't know what you are doing + /// API Builder IInstaApiBuilder SetApiRequestMessage(ApiRequestMessage requestMessage); + + /// + /// Set delay between requests. Useful when API supposed to be used for mass-bombing. + /// + /// Timespan delay + /// API Builder IInstaApiBuilder SetRequestDelay(TimeSpan delay); } } \ No newline at end of file diff --git a/InstaSharper/API/Builder/InstaApiBuilder.cs b/InstaSharper/API/Builder/InstaApiBuilder.cs index 48497026..0f412554 100644 --- a/InstaSharper/API/Builder/InstaApiBuilder.cs +++ b/InstaSharper/API/Builder/InstaApiBuilder.cs @@ -8,10 +8,7 @@ namespace InstaSharper.API.Builder { public class InstaApiBuilder : IInstaApiBuilder { - private static readonly Lazy LazyInstance = - new Lazy(() => new InstaApiBuilder()); - - private TimeSpan _delay; + private TimeSpan _delay = TimeSpan.Zero; private AndroidDevice _device; private HttpClient _httpClient; private HttpClientHandler _httpHandler = new HttpClientHandler(); @@ -24,10 +21,17 @@ private InstaApiBuilder() { } - public static InstaApiBuilder Instance => LazyInstance.Value; - + /// + /// Create new API instance + /// + /// + /// API instance + /// + /// User auth data must be specified public IInstaApi Build() { + if (_user == null) + throw new ArgumentNullException("User auth data must be specified"); if (_httpClient == null) _httpClient = new HttpClient(_httpHandler) {BaseAddress = new Uri(InstaApiConstants.INSTAGRAM_URL)}; @@ -47,6 +51,7 @@ public IInstaApi Build() if (string.IsNullOrEmpty(_requestMessage.password)) _requestMessage.password = _user?.Password; if (string.IsNullOrEmpty(_requestMessage.username)) _requestMessage.username = _user?.UserName; + if (_device == null && !string.IsNullOrEmpty(_requestMessage.device_id)) _device = AndroidDeviceGenerator.GetById(_requestMessage.device_id); if (_device == null) AndroidDeviceGenerator.GetRandomAndroidDevice(); @@ -59,48 +64,91 @@ public IInstaApi Build() return instaApi; } + /// + /// Use custom logger + /// + /// IInstaLogger implementation + /// + /// API Builder + /// public IInstaApiBuilder UseLogger(IInstaLogger logger) { _logger = logger; return this; } + /// + /// Set specific HttpClient + /// + /// HttpClient + /// + /// API Builder + /// public IInstaApiBuilder UseHttpClient(HttpClient httpClient) { _httpClient = httpClient; return this; } + /// + /// Set custom HttpClientHandler to be able to use certain features, e.g Proxy and so on + /// + /// HttpClientHandler + /// + /// API Builder + /// public IInstaApiBuilder UseHttpClientHandler(HttpClientHandler handler) { _httpHandler = handler; return this; } - public IInstaApiBuilder SetUserName(string username) - { - _user = new UserSessionData {UserName = username}; - return this; - } - + /// + /// Specify user login, password from here + /// + /// User auth data + /// + /// API Builder + /// public IInstaApiBuilder SetUser(UserSessionData user) { _user = user; return this; } + /// + /// Set custom request message. Used to be able to customize device info. + /// + /// Custom request message object + /// + /// API Builder + /// + /// + /// Please, do not use if you don't know what you are doing + /// public IInstaApiBuilder SetApiRequestMessage(ApiRequestMessage requestMessage) { _requestMessage = requestMessage; return this; } + /// + /// Set delay between requests. Useful when API supposed to be used for mass-bombing. + /// + /// Timespan delay + /// + /// API Builder + /// public IInstaApiBuilder SetRequestDelay(TimeSpan delay) { _delay = delay; return this; } + /// + /// Creates the builder. + /// + /// public static IInstaApiBuilder CreateBuilder() { return new InstaApiBuilder(); diff --git a/InstaSharper/API/IInstaApi.cs b/InstaSharper/API/IInstaApi.cs index 7ddfdbb5..d694b895 100644 --- a/InstaSharper/API/IInstaApi.cs +++ b/InstaSharper/API/IInstaApi.cs @@ -1,4 +1,5 @@ -using System.IO; +using System; +using System.IO; using System.Threading.Tasks; using InstaSharper.Classes; using InstaSharper.Classes.Models; @@ -32,40 +33,69 @@ public interface IInstaApi /// /// Login using given credentials asynchronously /// - /// True is succeed - Task> LoginAsync(); + /// + /// Success --> is succeed + /// TwoFactorRequired --> requires 2FA login. + /// BadPassword --> Password is wrong + /// InvalidUser --> User/phone number is wrong + /// Exception --> Something wrong happened + /// + Task> LoginAsync(); + + /// + /// 2-Factor Authentication Login using a verification code + /// Before call this method, please run LoginAsync first. + /// + /// Verification Code sent to your phone number + /// + /// Success --> is succeed + /// InvalidCode --> The code is invalid + /// CodeExpired --> The code is expired, please request a new one. + /// Exception --> Something wrong happened + /// + Task> TwoFactorLoginAsync(string verificationCode); + + /// + /// Get Two Factor Authentication details + /// + /// + /// An instance of TwoFactorLoginInfo if success. + /// A null reference if not success; in this case, do LoginAsync first and check if Two Factor Authentication is + /// required, if not, don't run this method + /// + Task> GetTwoFactorInfoAsync(); /// /// Logout from instagram asynchronously /// - /// True if completed without errors + /// True if logged out without errors Task> LogoutAsync(); /// /// Get user timeline feed (feed of recent posts from users you follow) asynchronously. /// - /// Maximum count of pages to retrieve + /// Pagination parameters: next id and max amount of pages to load /// /// /// - Task> GetUserTimelineFeedAsync(int maxPages = 0); + Task> GetUserTimelineFeedAsync(PaginationParameters paginationParameters); /// /// Get user explore feed (Explore tab info) asynchronously /// - /// Maximum count of pages to retrieve + /// Pagination parameters: next id and max amount of pages to load /// > - Task> GetExploreFeedAsync(int maxPages = 0); + Task> GetExploreFeedAsync(PaginationParameters paginationParameters); /// /// Get all user media by username asynchronously /// /// Username - /// Maximum count of pages to retrieve + /// Pagination parameters: next id and max amount of pages to load /// /// /// - Task> GetUserMediaAsync(string username, int maxPages = 0); + Task> GetUserMediaAsync(string username, PaginationParameters paginationParameters); /// /// Get media by its id asynchronously @@ -97,51 +127,53 @@ public interface IInstaApi /// Get tag feed by tag value asynchronously /// /// Tag value - /// Maximum count of pages to retrieve + /// Pagination parameters: next id and max amount of pages to load /// /// /// - Task> GetTagFeedAsync(string tag, int maxPages = 0); + Task> GetTagFeedAsync(string tag, PaginationParameters paginationParameters); /// /// Get followers list by username asynchronously /// /// Username - /// Maximum count of pages to retrieve + /// Pagination parameters: next id and max amount of pages to load /// /// /// - Task> GetUserFollowersAsync(string username, int maxPages = 0); + Task> GetUserFollowersAsync(string username, + PaginationParameters paginationParameters); /// /// Get following list by username asynchronously /// /// Username - /// Maximum count of pages to retrieve + /// Pagination parameters: next id and max amount of pages to load /// /// /// - Task> GetUserFollowingAsync(string username, int maxPages = 0); + Task> GetUserFollowingAsync(string username, + PaginationParameters paginationParameters); /// /// Get followers list for currently logged in user asynchronously /// - /// Maximum count of pages to retrieve + /// Pagination parameters: next id and max amount of pages to load /// /// /// - Task> GetCurrentUserFollowersAsync(int maxPages = 0); + Task> GetCurrentUserFollowersAsync(PaginationParameters paginationParameters); /// /// Get user tags by username asynchronously /// Returns media list containing tags /// /// Username - /// Maximum count of pages to retrieve + /// Pagination parameters: next id and max amount of pages to load /// /// /// - Task> GetUserTagsAsync(string username, int maxPages = 0); + Task> GetUserTagsAsync(string username, PaginationParameters paginationParameters); /// /// Get direct inbox threads for current user asynchronously @@ -166,42 +198,42 @@ public interface IInstaApi /// Comma-separated users PK /// Message thread ids /// Message text - /// - Task> SendDirectMessage(string recipients, string threadIds, string text); + /// List of threads + Task> SendDirectMessage(string recipients, string threadIds, string text); /// /// Get recent recipients (threads and users) asynchronously /// /// - /// + /// /// - Task> GetRecentRecipientsAsync(); + Task> GetRecentRecipientsAsync(); /// /// Get ranked recipients (threads and users) asynchronously /// /// - /// + /// /// - Task> GetRankedRecipientsAsync(); + Task> GetRankedRecipientsAsync(); /// /// Get recent activity info asynchronously /// - /// Maximum count of pages to retrieve + /// Pagination parameters: next id and max amount of pages to load /// /// /// - Task> GetRecentActivityAsync(int maxPages = 0); + Task> GetRecentActivityAsync(PaginationParameters paginationParameters); /// /// Get activity of following asynchronously /// - /// Maximum count of pages to retrieve + /// Pagination parameters: next id and max amount of pages to load /// /// /// - Task> GetFollowingRecentActivityAsync(int maxPages = 0); + Task> GetFollowingRecentActivityAsync(PaginationParameters paginationParameters); /// /// Like media (photo or video) @@ -227,12 +259,26 @@ public interface IInstaApi /// User id Task> UnFollowUserAsync(long userId); + /// + /// Block user + /// + /// User id + Task> BlockUserAsync(long userId); + + /// + /// Stop block user + /// + /// User id + Task> UnBlockUserAsync(long userId); + + /// /// Get media comments /// /// Media id - /// Maximum amount of pages to load - Task> GetMediaCommentsAsync(string mediaId, int maxPages = 0); + /// Pagination parameters: next id and max amount of pages to load + Task> + GetMediaCommentsAsync(string mediaId, PaginationParameters paginationParameters); /// /// Get users (short) who liked certain media. Normaly it return around 1000 last users. @@ -271,6 +317,14 @@ public interface IInstaApi /// Caption Task> UploadPhotoAsync(InstaImage image, string caption); + /// + /// Upload photo + /// + /// Array of photos to upload + /// Caption + /// + Task> UploadPhotosAlbumAsync(InstaImage[] images, string caption); + /// /// Configure photo /// @@ -280,6 +334,15 @@ public interface IInstaApi /// Task> ConfigurePhotoAsync(InstaImage image, string uploadId, string caption); + /// + /// Configure photos for Album + /// + /// Array of upload IDs to configure + /// /// + /// Caption + /// + Task> ConfigureAlbumAsync(string[] uploadId, string caption); + /// /// Get user story feed (stories from users followed by current user). /// @@ -337,11 +400,11 @@ public interface IInstaApi /// /// Get feed of media your liked. /// - /// Maximum count of pages to retrieve + /// Pagination parameters: next id and max amount of pages to load /// /// /// - Task> GetLikeFeedAsync(int maxPages = 0); + Task> GetLikeFeedAsync(PaginationParameters paginationParameters); /// @@ -360,6 +423,78 @@ public interface IInstaApi /// Task> GetUserStoryFeedAsync(long userId); + /// + /// Get your collection for given collection id + /// + /// Collection ID + /// + /// + /// + Task> GetCollectionAsync(long collectionId); + + /// + /// Get your collections + /// + /// + /// + /// + Task> GetCollectionsAsync(); + + /// + /// Create a new collection + /// + /// The name of the new collection + /// + /// + /// + Task> CreateCollectionAsync(string collectionName); + + /// + /// Delete your collection for given collection id + /// + /// Collection ID to delete + /// true if succeed + Task> DeleteCollectionAsync(long collectionId); + + /// + /// Get media ID from an url (got from "share link") + /// + /// Uri to get media ID + /// Media ID + Task> GetMediaIdFromUrlAsync(Uri uri); + + /// + /// Get share link from media Id + /// + /// media ID + /// Share link as Uri + Task> GetShareLinkFromMediaIdAsync(string mediaId); + + /// + /// Adds items to collection asynchronous. + /// + /// Collection identifier. + /// Media id list. + /// + Task> AddItemsToCollectionAsync(long collectionId, params string[] mediaIds); + + /// + /// Searches for specific location by provided geo-data or search query. + /// + /// Latitude + /// Longitude + /// Search query + /// List of locations (short format) + Task> SearchLocation(double latitude, double longitude, string query); + + /// + /// Gets the feed of particular location. + /// + /// Location identifier + /// Pagination parameters: next id and max amount of pages to load + /// Location feed + Task> GetLocationFeed(long locationId, PaginationParameters paginationParameters); + #endregion } } \ No newline at end of file diff --git a/InstaSharper/API/InstaApi.cs b/InstaSharper/API/InstaApi.cs index 0a041f74..e5faf3b8 100644 --- a/InstaSharper/API/InstaApi.cs +++ b/InstaSharper/API/InstaApi.cs @@ -1,22 +1,19 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; using System.Net; using System.Net.Http; using System.Threading.Tasks; +using InstaSharper.API.Processors; using InstaSharper.Classes; using InstaSharper.Classes.Android.DeviceInfo; using InstaSharper.Classes.Models; using InstaSharper.Classes.ResponseWrappers; using InstaSharper.Classes.ResponseWrappers.BaseResponse; using InstaSharper.Converters; -using InstaSharper.Converters.Json; using InstaSharper.Helpers; using InstaSharper.Logger; using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using InstaRecentActivityConverter = InstaSharper.Converters.Json.InstaRecentActivityConverter; namespace InstaSharper.API { @@ -24,8 +21,20 @@ internal class InstaApi : IInstaApi { private readonly IHttpRequestProcessor _httpRequestProcessor; private readonly IInstaLogger _logger; + private ICollectionProcessor _collectionProcessor; + private ICommentProcessor _commentProcessor; private AndroidDevice _deviceInfo; + private IFeedProcessor _feedProcessor; + + private ILocationProcessor _locationProcessor; + private IMediaProcessor _mediaProcessor; + private IMessagingProcessor _messagingProcessor; + private IUserProfileProcessor _profileProcessor; + private IStoryProcessor _storyProcessor; + + private TwoFactorLoginInfo _twoFactorInfo; private UserSessionData _user; + private IUserProcessor _userProcessor; public InstaApi(UserSessionData user, IInstaLogger logger, AndroidDevice deviceInfo, IHttpRequestProcessor httpRequestProcessor) @@ -36,1492 +45,990 @@ public InstaApi(UserSessionData user, IInstaLogger logger, AndroidDevice deviceI _httpRequestProcessor = httpRequestProcessor; } - public bool IsUserAuthenticated { get; private set; } - - #region async part - - public async Task> LoginAsync() - { - ValidateUser(); - ValidateRequestMessage(); - try - { - var csrftoken = string.Empty; - var firstResponse = await _httpRequestProcessor.GetAsync(_httpRequestProcessor.Client.BaseAddress); - var cookies = - _httpRequestProcessor.HttpHandler.CookieContainer.GetCookies(_httpRequestProcessor.Client - .BaseAddress); - _logger?.LogResponse(firstResponse); - foreach (Cookie cookie in cookies) - if (cookie.Name == InstaApiConstants.CSRFTOKEN) csrftoken = cookie.Value; - _user.CsrfToken = csrftoken; - var instaUri = UriCreator.GetLoginUri(); - var signature = - $"{_httpRequestProcessor.RequestMessage.GenerateSignature()}.{_httpRequestProcessor.RequestMessage.GetMessageString()}"; - var fields = new Dictionary - { - {InstaApiConstants.HEADER_IG_SIGNATURE, signature}, - {InstaApiConstants.HEADER_IG_SIGNATURE_KEY_VERSION, InstaApiConstants.IG_SIGNATURE_KEY_VERSION} - }; - var request = HttpHelper.GetDefaultRequest(HttpMethod.Post, instaUri, _deviceInfo); - request.Content = new FormUrlEncodedContent(fields); - request.Properties.Add(InstaApiConstants.HEADER_IG_SIGNATURE, signature); - request.Properties.Add(InstaApiConstants.HEADER_IG_SIGNATURE_KEY_VERSION, - InstaApiConstants.IG_SIGNATURE_KEY_VERSION); - var response = await _httpRequestProcessor.SendAsync(request); - var json = await response.Content.ReadAsStringAsync(); - if (response.StatusCode != HttpStatusCode.OK) return Result.UnExpectedResponse(response, json); - var loginInfo = - JsonConvert.DeserializeObject(json); - IsUserAuthenticated = loginInfo.User != null && loginInfo.User.UserName == _user.UserName; - var converter = ConvertersFabric.GetUserShortConverter(loginInfo.User); - _user.LoggedInUder = converter.Convert(); - _user.RankToken = $"{_user.LoggedInUder.Pk}_{_httpRequestProcessor.RequestMessage.phone_id}"; - return Result.Success(true); - } - catch (Exception exception) - { - LogException(exception); - return Result.Fail(exception.Message, false); - } - } - - public async Task> LogoutAsync() - { - ValidateUser(); - ValidateLoggedIn(); - try - { - var instaUri = UriCreator.GetLogoutUri(); - var request = HttpHelper.GetDefaultRequest(HttpMethod.Get, instaUri, _deviceInfo); - var response = await _httpRequestProcessor.SendAsync(request); - var json = await response.Content.ReadAsStringAsync(); - if (response.StatusCode != HttpStatusCode.OK) return Result.UnExpectedResponse(response, json); - var logoutInfo = JsonConvert.DeserializeObject(json); - IsUserAuthenticated = logoutInfo.Status == "ok"; - return Result.Success(true); - } - catch (Exception exception) - { - LogException(exception); - return Result.Fail(exception.Message, false); - } - } - - public async Task> GetUserTimelineFeedAsync(int maxPages = 0) + /// + /// Get user timeline feed (feed of recent posts from users you follow) asynchronously. + /// + /// Pagination parameters: next id and max amount of pages to load + /// + /// + /// + public async Task> GetUserTimelineFeedAsync(PaginationParameters paginationParameters) { ValidateUser(); ValidateLoggedIn(); - var userFeedUri = UriCreator.GetUserFeedUri(); - var request = HttpHelper.GetDefaultRequest(HttpMethod.Get, userFeedUri, _deviceInfo); - var response = await _httpRequestProcessor.SendAsync(request); - var json = await response.Content.ReadAsStringAsync(); - if (response.StatusCode != HttpStatusCode.OK) return Result.UnExpectedResponse(response, json); - var feedResponse = JsonConvert.DeserializeObject(json, - new InstaFeedResponseDataConverter()); - var converter = ConvertersFabric.GetFeedConverter(feedResponse); - var feed = converter.Convert(); - var nextId = feedResponse.NextMaxId; - var moreAvailable = feedResponse.MoreAvailable; - while (moreAvailable && feed.Medias.Pages < maxPages) - { - if (string.IsNullOrEmpty(nextId)) break; - var nextFeed = await GetUserFeedWithMaxIdAsync(nextId); - if (!nextFeed.Succeeded) Result.Success($"Not all pages was downloaded: {nextFeed.Info.Message}", feed); - nextId = nextFeed.Value.NextMaxId; - moreAvailable = nextFeed.Value.MoreAvailable; - feed.Medias.AddRange( - nextFeed.Value.Items.Select(ConvertersFabric.GetSingleMediaConverter) - .Select(conv => conv.Convert())); - feed.Medias.Pages++; - } - return Result.Success(feed); + return await _feedProcessor.GetUserTimelineFeedAsync(paginationParameters); } + /// + /// Get user story reel feed. Contains user info last story including all story items. + /// + /// User identifier (PK) + /// public async Task> GetUserStoryFeedAsync(long userId) { ValidateUser(); ValidateLoggedIn(); - try - { - var userFeedUri = UriCreator.GetUserReelFeedUri(userId); - var request = HttpHelper.GetDefaultRequest(HttpMethod.Get, userFeedUri, _deviceInfo); - var response = await _httpRequestProcessor.SendAsync(request); - var json = await response.Content.ReadAsStringAsync(); - if (response.StatusCode != HttpStatusCode.OK) - return Result.UnExpectedResponse(response, json); - var feedResponse = JsonConvert.DeserializeObject(json); - var feed = ConvertersFabric.GetReelFeedConverter(feedResponse).Convert(); - return Result.Success(feed); - } - catch (Exception exception) - { - return Result.Fail(exception.Message, (InsteReelFeed) null); - } - } - - /// - public Stream GetStateDataAsStream() - { - var state = new StateData - { - DeviceInfo = _deviceInfo, - IsAuthenticated = IsUserAuthenticated, - UserSession = _user, - Cookies = _httpRequestProcessor.HttpHandler.CookieContainer - }; - return SerializationHelper.SerializeToStream(state); + return await _storyProcessor.GetUserStoryFeedAsync(userId); } - /// - public void LoadStateDataFromStream(Stream stream) - { - var data = SerializationHelper.DeserializeFromStream(stream); - _deviceInfo = data.DeviceInfo; - _user = data.UserSession; - IsUserAuthenticated = data.IsAuthenticated; - _httpRequestProcessor.HttpHandler.CookieContainer = data.Cookies; - } - public async Task> GetExploreFeedAsync(int maxPages = 0) + /// + /// Get user explore feed (Explore tab info) asynchronously + /// + /// Pagination parameters: next id and max amount of pages to load + /// + /// > + /// + public async Task> GetExploreFeedAsync(PaginationParameters paginationParameters) { ValidateUser(); ValidateLoggedIn(); - try - { - if (maxPages == 0) maxPages = int.MaxValue; - var exploreUri = UriCreator.GetExploreUri(); - var request = HttpHelper.GetDefaultRequest(HttpMethod.Get, exploreUri, _deviceInfo); - var response = await _httpRequestProcessor.SendAsync(request); - var json = await response.Content.ReadAsStringAsync(); - if (response.StatusCode != HttpStatusCode.OK) return Result.Fail("", (InstaExploreFeed) null); - var feedResponse = JsonConvert.DeserializeObject(json, - new InstaExploreFeedDataConverter()); - var exploreFeed = ConvertersFabric.GetExploreFeedConverter(feedResponse).Convert(); - var nextId = feedResponse.Items.Medias.LastOrDefault(media => !string.IsNullOrEmpty(media.NextMaxId)) - ?.NextMaxId; - while (!string.IsNullOrEmpty(nextId) && exploreFeed.Medias.Pages < maxPages) - { - var nextFeed = await GetExploreFeedAsync(nextId); - if (!nextFeed.Succeeded) - Result.Success($"Not all pages were downloaded: {nextFeed.Info.Message}", exploreFeed); - nextId = feedResponse.Items.Medias.LastOrDefault(media => !string.IsNullOrEmpty(media.NextMaxId)) - ?.NextMaxId; - exploreFeed.Medias.AddRange( - nextFeed.Value.Items.Medias.Select(ConvertersFabric.GetSingleMediaConverter) - .Select(conv => conv.Convert())); - exploreFeed.Medias.Pages++; - } - return Result.Success(exploreFeed); - } - catch (Exception exception) - { - LogException(exception); - return Result.Fail(exception.Message, (InstaExploreFeed) null); - } + return await _feedProcessor.GetExploreFeedAsync(paginationParameters); } - public async Task> GetUserMediaAsync(string username, int maxPages = 0) + /// + /// Get all user media by username asynchronously + /// + /// Username + /// Pagination parameters: next id and max amount of pages to load + /// + /// + /// + public async Task> GetUserMediaAsync(string username, + PaginationParameters paginationParameters) { ValidateUser(); - if (maxPages == 0) maxPages = int.MaxValue; + ValidateLoggedIn(); var user = await GetUserAsync(username); - if (!user.Succeeded) return Result.Fail("Unable to get current user"); - var instaUri = UriCreator.GetUserMediaListUri(user.Value.Pk); - var request = HttpHelper.GetDefaultRequest(HttpMethod.Get, instaUri, _deviceInfo); - var response = await _httpRequestProcessor.SendAsync(request); - var json = await response.Content.ReadAsStringAsync(); - if (response.StatusCode == HttpStatusCode.OK) - { - var mediaResponse = JsonConvert.DeserializeObject(json, - new InstaMediaListDataConverter()); - var moreAvailable = mediaResponse.MoreAvailable; - var converter = ConvertersFabric.GetMediaListConverter(mediaResponse); - var mediaList = converter.Convert(); - mediaList.Pages++; - var nextId = mediaResponse.NextMaxId; - while (moreAvailable && mediaList.Pages < maxPages) - { - instaUri = UriCreator.GetMediaListWithMaxIdUri(user.Value.Pk, nextId); - var nextMedia = await GetUserMediaListWithMaxIdAsync(instaUri); - mediaList.Pages++; - if (!nextMedia.Succeeded) - Result.Success($"Not all pages were downloaded: {nextMedia.Info.Message}", mediaList); - nextId = nextMedia.Value.NextMaxId; - moreAvailable = nextMedia.Value.MoreAvailable; - converter = ConvertersFabric.GetMediaListConverter(nextMedia.Value); - mediaList.AddRange(converter.Convert()); - } - return Result.Success(mediaList); - } - return Result.UnExpectedResponse(response, json); + if (!user.Succeeded) + return Result.Fail("Unable to get user to load media"); + return await _userProcessor.GetUserMediaAsync(user.Value.Pk, paginationParameters); } + /// + /// Get media by its id asynchronously + /// + /// Maximum count of pages to retrieve + /// + /// + /// public async Task> GetMediaByIdAsync(string mediaId) { ValidateUser(); - var mediaUri = UriCreator.GetMediaUri(mediaId); - var request = HttpHelper.GetDefaultRequest(HttpMethod.Get, mediaUri, _deviceInfo); - var response = await _httpRequestProcessor.SendAsync(request); - var json = await response.Content.ReadAsStringAsync(); - if (response.StatusCode == HttpStatusCode.OK) - { - var mediaResponse = JsonConvert.DeserializeObject(json, - new InstaMediaListDataConverter()); - if (mediaResponse.Medias?.Count != 1) - { - var errorMessage = $"Got wrong media count for request with media id={mediaId}"; - _logger?.LogInfo(errorMessage); - return Result.Fail(errorMessage); - } - var converter = ConvertersFabric.GetSingleMediaConverter(mediaResponse.Medias.FirstOrDefault()); - return Result.Success(converter.Convert()); - } - return Result.UnExpectedResponse(response, json); + return await _mediaProcessor.GetMediaByIdAsync(mediaId); } + /// + /// Get user info by its user name asynchronously + /// + /// Username + /// + /// + /// public async Task> GetUserAsync(string username) { ValidateUser(); - var userUri = UriCreator.GetUserUri(username); - var request = HttpHelper.GetDefaultRequest(HttpMethod.Get, userUri, _deviceInfo); - request.Properties.Add(new KeyValuePair(InstaApiConstants.HEADER_TIMEZONE, - InstaApiConstants.TIMEZONE_OFFSET.ToString())); - request.Properties.Add(new KeyValuePair(InstaApiConstants.HEADER_COUNT, "1")); - request.Properties.Add( - new KeyValuePair(InstaApiConstants.HEADER_RANK_TOKEN, _user.RankToken)); - var response = await _httpRequestProcessor.SendAsync(request); - var json = await response.Content.ReadAsStringAsync(); - if (response.StatusCode == HttpStatusCode.OK) - { - var userInfo = JsonConvert.DeserializeObject(json); - var user = userInfo.Users?.FirstOrDefault(u => u.UserName == username); - if (user == null) - { - var errorMessage = $"Can't find this user: {username}"; - _logger?.LogInfo(errorMessage); - return Result.Fail(errorMessage); - } - if (string.IsNullOrEmpty(user.Pk)) - Result.Fail("Pk is null"); - var converter = ConvertersFabric.GetUserConverter(user); - return Result.Success(converter.Convert()); - } - return Result.UnExpectedResponse(response, json); + ValidateLoggedIn(); + return await _userProcessor.GetUserAsync(username); } + /// + /// Get currently logged in user info asynchronously + /// + /// + /// + /// public async Task> GetCurrentUserAsync() { ValidateUser(); ValidateLoggedIn(); - var instaUri = UriCreator.GetCurrentUserUri(); - var fields = new Dictionary - { - {"_uuid", _deviceInfo.DeviceGuid.ToString()}, - {"_uid", _user.LoggedInUder.Pk}, - {"_csrftoken", _user.CsrfToken} - }; - var request = HttpHelper.GetDefaultRequest(HttpMethod.Post, instaUri, _deviceInfo); - request.Content = new FormUrlEncodedContent(fields); - var response = await _httpRequestProcessor.SendAsync(request); - var json = await response.Content.ReadAsStringAsync(); - if (response.StatusCode == HttpStatusCode.OK) - { - var user = JsonConvert.DeserializeObject(json, - new InstaCurrentUserDataConverter()); - if (string.IsNullOrEmpty(user.Pk)) - Result.Fail("Pk is null"); - var converter = ConvertersFabric.GetCurrentUserConverter(user); - var userConverted = converter.Convert(); - return Result.Success(userConverted); - } - return Result.UnExpectedResponse(response, json); + return await _userProcessor.GetCurrentUserAsync(); } - public async Task> GetTagFeedAsync(string tag, int maxPages = 0) + /// + /// Get tag feed by tag value asynchronously + /// + /// Tag value + /// Pagination parameters: next id and max amount of pages to load + /// + /// + /// + public async Task> GetTagFeedAsync(string tag, PaginationParameters paginationParameters) { ValidateUser(); ValidateLoggedIn(); - if (maxPages == 0) maxPages = int.MaxValue; - var userFeedUri = UriCreator.GetTagFeedUri(tag); - var request = HttpHelper.GetDefaultRequest(HttpMethod.Get, userFeedUri, _deviceInfo); - var response = await _httpRequestProcessor.SendAsync(request); - var json = await response.Content.ReadAsStringAsync(); - if (response.StatusCode == HttpStatusCode.OK) - { - var feedResponse = JsonConvert.DeserializeObject(json, - new InstaTagFeedDataConverter()); - var converter = ConvertersFabric.GetTagFeedConverter(feedResponse); - var tagFeed = converter.Convert(); - tagFeed.Medias.Pages++; - var nextId = feedResponse.NextMaxId; - var moreAvailable = feedResponse.MoreAvailable; - while (moreAvailable && tagFeed.Medias.Pages < maxPages) - { - var nextMedia = await GetTagFeedWithMaxIdAsync(tag, nextId); - tagFeed.Medias.Pages++; - if (!nextMedia.Succeeded) - return Result.Success($"Not all pages was downloaded: {nextMedia.Info.Message}", tagFeed); - nextId = nextMedia.Value.NextMaxId; - moreAvailable = nextMedia.Value.MoreAvailable; - tagFeed.Medias.AddRange(ConvertersFabric.GetMediaListConverter(nextMedia.Value).Convert()); - } - return Result.Success(tagFeed); - } - return Result.UnExpectedResponse(response, json); + return await _feedProcessor.GetTagFeedAsync(tag, paginationParameters); } - public async Task> GetUserFollowersAsync(string username, int maxPages = 0) + /// + /// Get followers list by username asynchronously + /// + /// Username + /// Pagination parameters: next id and max amount of pages to load + /// + /// + /// + public async Task> GetUserFollowersAsync(string username, + PaginationParameters paginationParameters) { ValidateUser(); ValidateLoggedIn(); - try - { - if (maxPages == 0) maxPages = int.MaxValue; - var user = await GetUserAsync(username); - var userFollowersUri = UriCreator.GetUserFollowersUri(user.Value.Pk, _user.RankToken); - var followers = new InstaUserShortList(); - var followersResponse = await GetUserListByURIAsync(userFollowersUri); - if (!followersResponse.Succeeded) - Result.Fail(followersResponse.Info, (InstaUserList) null); - followers.AddRange( - followersResponse.Value.Items.Select(ConvertersFabric.GetUserShortConverter) - .Select(converter => converter.Convert())); - if (!followersResponse.Value.IsBigList) return Result.Success(followers); - var pages = 1; - while (!string.IsNullOrEmpty(followersResponse.Value.NextMaxId) && pages < maxPages) - { - var nextFollowersUri = - UriCreator.GetUserFollowersUri(user.Value.Pk, _user.RankToken, - followersResponse.Value.NextMaxId); - followersResponse = await GetUserListByURIAsync(nextFollowersUri); - if (!followersResponse.Succeeded) - return Result.Success($"Not all pages was downloaded: {followersResponse.Info.Message}", - followers); - followers.AddRange( - followersResponse.Value.Items.Select(ConvertersFabric.GetUserShortConverter) - .Select(converter => converter.Convert())); - pages++; - } - return Result.Success(followers); - } - catch (Exception exception) - { - LogException(exception); - return Result.Fail(exception.Message, (InstaUserShortList) null); - } + return await _userProcessor.GetUserFollowersAsync(username, paginationParameters); } - public async Task> GetUserFollowingAsync(string username, int maxPages = 0) + /// + /// Get following list by username asynchronously + /// + /// Username + /// Pagination parameters: next id and max amount of pages to load + /// + /// + /// + public async Task> GetUserFollowingAsync(string username, + PaginationParameters paginationParameters) { ValidateUser(); ValidateLoggedIn(); - try - { - if (maxPages == 0) maxPages = int.MaxValue; - var user = await GetUserAsync(username); - var userFeedUri = UriCreator.GetUserFollowingUri(user.Value.Pk, _user.RankToken); - var following = new InstaUserShortList(); - var userListResponse = await GetUserListByURIAsync(userFeedUri); - if (!userListResponse.Succeeded) - Result.Fail(userListResponse.Info, following); - following.AddRange( - userListResponse.Value.Items.Select(ConvertersFabric.GetUserShortConverter) - .Select(converter => converter.Convert())); - if (!userListResponse.Value.IsBigList) return Result.Success(following); - var pages = 1; - while (!string.IsNullOrEmpty(userListResponse.Value.NextMaxId) && pages < maxPages) - { - var nextUri = - UriCreator.GetUserFollowingUri(user.Value.Pk, _user.RankToken, - userListResponse.Value.NextMaxId); - userListResponse = await GetUserListByURIAsync(nextUri); - if (!userListResponse.Succeeded) - return Result.Success($"Not all pages was downloaded: {userListResponse.Info.Message}", - following); - following.AddRange( - userListResponse.Value.Items.Select(ConvertersFabric.GetUserShortConverter) - .Select(converter => converter.Convert())); - pages++; - } - return Result.Success(following); - } - catch (Exception exception) - { - LogException(exception); - return Result.Fail(exception.Message, (InstaUserShortList) null); - } + return await _userProcessor.GetUserFollowingAsync(username, paginationParameters); } - public async Task> GetCurrentUserFollowersAsync(int maxPages = 0) + /// + /// Get followers list for currently logged in user asynchronously + /// + /// Pagination parameters: next id and max amount of pages to load + /// + /// + /// + public async Task> GetCurrentUserFollowersAsync( + PaginationParameters paginationParameters) { ValidateUser(); - return await GetUserFollowersAsync(_user.UserName, maxPages); + ValidateLoggedIn(); + return await _userProcessor.GetCurrentUserFollowersAsync(paginationParameters); } - public async Task> GetUserTagsAsync(string username, int maxPages = 0) + /// + /// Get user tags by username asynchronously + /// Returns media list containing tags + /// + /// Username + /// Pagination parameters: next id and max amount of pages to load + /// + /// + /// + public async Task> GetUserTagsAsync(string username, + PaginationParameters paginationParameters) { ValidateUser(); ValidateLoggedIn(); - try - { - if (maxPages == 0) maxPages = int.MaxValue; - var user = await GetUserAsync(username); - if (!user.Succeeded || string.IsNullOrEmpty(user.Value.Pk)) - return Result.Fail($"Unable to get user {username}", (InstaMediaList) null); - var uri = UriCreator.GetUserTagsUri(user.Value?.Pk, _user.RankToken); - var request = HttpHelper.GetDefaultRequest(HttpMethod.Get, uri, _deviceInfo); - var response = await _httpRequestProcessor.SendAsync(request); - var json = await response.Content.ReadAsStringAsync(); - var userTags = new InstaMediaList(); - if (response.StatusCode != HttpStatusCode.OK) return Result.Fail("", (InstaMediaList) null); - var mediaResponse = JsonConvert.DeserializeObject(json, - new InstaMediaListDataConverter()); - var nextId = mediaResponse.NextMaxId; - userTags.AddRange( - mediaResponse.Medias.Select(ConvertersFabric.GetSingleMediaConverter) - .Select(converter => converter.Convert())); - var pages = 1; - while (!string.IsNullOrEmpty(nextId) && pages < maxPages) - { - uri = UriCreator.GetUserTagsUri(user.Value?.Pk, _user.RankToken, nextId); - var nextMedia = await GetUserMediaListWithMaxIdAsync(uri); - if (!nextMedia.Succeeded) - Result.Success($"Not all pages was downloaded: {nextMedia.Info.Message}", userTags); - nextId = nextMedia.Value.NextMaxId; - userTags.AddRange( - mediaResponse.Medias.Select(ConvertersFabric.GetSingleMediaConverter) - .Select(converter => converter.Convert())); - pages++; - } - return Result.Success(userTags); - } - catch (Exception exception) - { - LogException(exception); - return Result.Fail(exception.Message, (InstaMediaList) null); - } + var user = await GetUserAsync(username); + if (!user.Succeeded) + return Result.Fail($"Unable to get user {username} to get tags", (InstaMediaList) null); + return await _userProcessor.GetUserTagsAsync(user.Value.Pk, paginationParameters); } + /// + /// Get direct inbox threads for current user asynchronously + /// + /// + /// + /// public async Task> GetDirectInboxAsync() { ValidateUser(); ValidateLoggedIn(); - try - { - var directInboxUri = UriCreator.GetDirectInboxUri(); - var request = HttpHelper.GetDefaultRequest(HttpMethod.Get, directInboxUri, _deviceInfo); - var response = await _httpRequestProcessor.SendAsync(request); - var json = await response.Content.ReadAsStringAsync(); - if (response.StatusCode != HttpStatusCode.OK) return Result.Fail("", (InstaDirectInboxContainer) null); - var inboxResponse = JsonConvert.DeserializeObject(json); - var converter = ConvertersFabric.GetDirectInboxConverter(inboxResponse); - return Result.Success(converter.Convert()); - } - catch (Exception exception) - { - LogException(exception); - return Result.Fail(exception); - } + return await _messagingProcessor.GetDirectInboxAsync(); } + /// + /// Get direct inbox thread by its id asynchronously + /// + /// Thread id + /// + /// + /// public async Task> GetDirectInboxThreadAsync(string threadId) { ValidateUser(); ValidateLoggedIn(); - try - { - var directInboxUri = UriCreator.GetDirectInboxThreadUri(threadId); - var request = HttpHelper.GetDefaultRequest(HttpMethod.Get, directInboxUri, _deviceInfo); - var response = await _httpRequestProcessor.SendAsync(request); - var json = await response.Content.ReadAsStringAsync(); - if (response.StatusCode != HttpStatusCode.OK) return Result.Fail("", (InstaDirectInboxThread) null); - var threadResponse = JsonConvert.DeserializeObject(json, - new InstaThreadDataConverter()); - var converter = ConvertersFabric.GetDirectThreadConverter(threadResponse); - return Result.Success(converter.Convert()); - } - catch (Exception exception) - { - LogException(exception); - return Result.Fail(exception); - } + return await _messagingProcessor.GetDirectInboxThreadAsync(threadId); } - public async Task> SendDirectMessage(string recipients, string threadIds, string text) + /// + /// Send direct message to provided users and threads + /// + /// Comma-separated users PK + /// Message thread ids + /// Message text + /// + /// List of threads + /// + public async Task> SendDirectMessage(string recipients, string threadIds, + string text) { ValidateUser(); ValidateLoggedIn(); - try - { - var directSendMessageUri = UriCreator.GetDirectSendMessageUri(); - - var request = HttpHelper.GetDefaultRequest(HttpMethod.Post, directSendMessageUri, _deviceInfo); - - var fields = new Dictionary {{"text", text}}; - - if (!string.IsNullOrEmpty(recipients)) - fields.Add("recipient_users", "[[" + recipients + "]]"); - else if (!string.IsNullOrEmpty(threadIds)) - fields.Add("thread_ids", "[" + threadIds + "]"); - else - return Result.Fail("Please provide at least one recipient or thread."); - - request.Content = new FormUrlEncodedContent(fields); - var response = await _httpRequestProcessor.SendAsync(request); - var json = await response.Content.ReadAsStringAsync(); - if (response.StatusCode != HttpStatusCode.OK) return Result.UnExpectedResponse(response, json); - var result = JsonConvert.DeserializeObject(json); - return result.IsOk() ? Result.Success(true) : Result.Fail(result.Status); - } - catch (Exception exception) - { - LogException(exception); - return Result.Fail(exception); - } + return await _messagingProcessor.SendDirectMessage(recipients, threadIds, text); } - public async Task> GetRecentRecipientsAsync() + /// + /// Get recent recipients (threads and users) asynchronously + /// + /// + /// + /// + public async Task> GetRecentRecipientsAsync() { ValidateUser(); ValidateLoggedIn(); - var userUri = UriCreator.GetRecentRecipientsUri(); - var request = HttpHelper.GetDefaultRequest(HttpMethod.Get, userUri, _deviceInfo); - var response = await _httpRequestProcessor.SendAsync(request); - var json = await response.Content.ReadAsStringAsync(); - - if (response.StatusCode != HttpStatusCode.OK) - return Result.UnExpectedResponse(response, json); - var responseRecipients = JsonConvert.DeserializeObject(json); - var converter = ConvertersFabric.GetRecipientsConverter(responseRecipients); - return Result.Success(converter.Convert()); + return await _messagingProcessor.GetRecentRecipientsAsync(); } - public async Task> GetRankedRecipientsAsync() + /// + /// Get ranked recipients (threads and users) asynchronously + /// + /// + /// + /// + public async Task> GetRankedRecipientsAsync() { ValidateUser(); ValidateLoggedIn(); - var userUri = UriCreator.GetRankedRecipientsUri(); - var request = HttpHelper.GetDefaultRequest(HttpMethod.Get, userUri, _deviceInfo); - var response = await _httpRequestProcessor.SendAsync(request); - var json = await response.Content.ReadAsStringAsync(); - if (response.StatusCode != HttpStatusCode.OK) - return Result.UnExpectedResponse(response, json); - var responseRecipients = JsonConvert.DeserializeObject(json); - var converter = ConvertersFabric.GetRecipientsConverter(responseRecipients); - return Result.Success(converter.Convert()); - } - - public async Task> GetRecentActivityAsync(int maxPages = 0) - { - var uri = UriCreator.GetRecentActivityUri(); - return await GetRecentActivityInternalAsync(uri, maxPages); + return await _messagingProcessor.GetRankedRecipientsAsync(); } - public async Task> GetFollowingRecentActivityAsync(int maxPages = 0) + /// + /// Get recent activity info asynchronously + /// + /// Pagination parameters: next id and max amount of pages to load + /// + /// + /// + public async Task> GetRecentActivityAsync(PaginationParameters paginationParameters) { - var uri = UriCreator.GetFollowingRecentActivityUri(); - return await GetRecentActivityInternalAsync(uri, maxPages); + return await _feedProcessor.GetRecentActivityFeedAsync(paginationParameters); } - - public async Task> CheckpointAsync(string checkPointUrl) + /// + /// Get activity of following asynchronously + /// + /// + /// + /// + /// + public async Task> GetFollowingRecentActivityAsync( + PaginationParameters paginationParameters) { - if (string.IsNullOrEmpty(checkPointUrl)) return Result.Fail("Empty checkpoint URL", false); - var instaUri = new Uri(checkPointUrl); - var request = HttpHelper.GetDefaultRequest(HttpMethod.Get, instaUri, _deviceInfo); - var response = await _httpRequestProcessor.SendAsync(request); - var json = await response.Content.ReadAsStringAsync(); - if (response.StatusCode == HttpStatusCode.OK) return Result.Success(true); - return Result.UnExpectedResponse(response, json); + return await _feedProcessor.GetFollowingRecentActivityFeedAsync(paginationParameters); } + /// + /// Like media (photo or video) + /// + /// Media id + /// public async Task> LikeMediaAsync(string mediaId) { - return await LikeUnlikeMediaInternal(mediaId, UriCreator.GetLikeMediaUri(mediaId)); + return await _mediaProcessor.LikeMediaAsync(mediaId); } + /// + /// Remove like from media (photo or video) + /// + /// Media id + /// public async Task> UnLikeMediaAsync(string mediaId) { - return await LikeUnlikeMediaInternal(mediaId, UriCreator.GetUnLikeMediaUri(mediaId)); + return await _mediaProcessor.UnLikeMediaAsync(mediaId); } - public async Task> LikeUnlikeMediaInternal(string mediaId, Uri instaUri) + /// + /// Get media comments + /// + /// Media id + /// Maximum amount of pages to load and start id + /// + public async Task> GetMediaCommentsAsync(string mediaId, + PaginationParameters paginationParameters) { ValidateUser(); ValidateLoggedIn(); - try - { - var fields = new Dictionary - { - {"_uuid", _deviceInfo.DeviceGuid.ToString()}, - {"_uid", _user.LoggedInUder.Pk}, - {"_csrftoken", _user.CsrfToken}, - {"media_id", mediaId} - }; - var request = HttpHelper.GetSignedRequest(HttpMethod.Post, instaUri, _deviceInfo, fields); - var response = await _httpRequestProcessor.SendAsync(request); - var json = await response.Content.ReadAsStringAsync(); - if (response.StatusCode == HttpStatusCode.OK) - return Result.Success(true); - return Result.UnExpectedResponse(response, json); - } - catch (Exception exception) - { - LogException(exception); - return Result.Fail(exception.Message, false); - } - } - public async Task> GetMediaCommentsAsync(string mediaId, int maxPages = 0) - { - ValidateUser(); - ValidateLoggedIn(); - try - { - if (maxPages == 0) maxPages = int.MaxValue; - var commentsUri = UriCreator.GetMediaCommentsUri(mediaId); - var request = HttpHelper.GetDefaultRequest(HttpMethod.Get, commentsUri, _deviceInfo); - var response = await _httpRequestProcessor.SendAsync(request); - var json = await response.Content.ReadAsStringAsync(); - if (response.StatusCode != HttpStatusCode.OK) - return Result.Fail($"Unexpected response status: {response.StatusCode}", (InstaCommentList) null); - var commentListResponse = JsonConvert.DeserializeObject(json); - var converter = ConvertersFabric.GetCommentListConverter(commentListResponse); - var instaComments = converter.Convert(); - instaComments.Pages++; - var nextId = commentListResponse.NextMaxId; - var moreAvailable = commentListResponse.MoreComentsAvailable; - while (moreAvailable && instaComments.Pages < maxPages) - { - if (string.IsNullOrEmpty(nextId)) break; - var nextComments = await GetCommentListWithMaxIdAsync(mediaId, nextId); - if (!nextComments.Succeeded) - Result.Success($"Not all pages was downloaded: {nextComments.Info.Message}", instaComments); - nextId = nextComments.Value.NextMaxId; - moreAvailable = nextComments.Value.MoreComentsAvailable; - converter = ConvertersFabric.GetCommentListConverter(nextComments.Value); - instaComments.Comments.AddRange(converter.Convert().Comments); - instaComments.Pages++; - } - return Result.Success(instaComments); - } - catch (Exception exception) - { - LogException(exception); - return Result.Fail(exception); - } + return await _commentProcessor.GetMediaCommentsAsync(mediaId, paginationParameters); } + /// + /// Get users (short) who liked certain media. Normaly it return around 1000 last users. + /// + /// Media id + /// public async Task> GetMediaLikersAsync(string mediaId) { ValidateUser(); ValidateLoggedIn(); - try - { - var likersUri = UriCreator.GetMediaLikersUri(mediaId); - var request = HttpHelper.GetDefaultRequest(HttpMethod.Get, likersUri, _deviceInfo); - var response = await _httpRequestProcessor.SendAsync(request); - var json = await response.Content.ReadAsStringAsync(); - if (response.StatusCode != HttpStatusCode.OK) - return Result.UnExpectedResponse(response, json); - var likers = new InstaLikersList(); - var mediaLikersResponse = JsonConvert.DeserializeObject(json); - likers.UsersCount = mediaLikersResponse.UsersCount; - if (mediaLikersResponse.UsersCount < 1) return Result.Success(likers); - likers.AddRange( - mediaLikersResponse.Users.Select(ConvertersFabric.GetUserShortConverter) - .Select(converter => converter.Convert())); - return Result.Success(likers); - } - catch (Exception exception) - { - LogException(exception); - return Result.Fail(exception); - } + return await _mediaProcessor.GetMediaLikersAsync(mediaId); } + /// + /// Follow user + /// + /// User id + /// public async Task> FollowUserAsync(long userId) { - return await FollowUnfollowUserInternal(userId, UriCreator.GetFollowUserUri(userId)); + return await _userProcessor.FollowUserAsync(userId); } + /// + /// Stop follow user + /// + /// User id + /// public async Task> UnFollowUserAsync(long userId) { - return await FollowUnfollowUserInternal(userId, UriCreator.GetUnFollowUserUri(userId)); + return await _userProcessor.UnFollowUserAsync(userId); + } + + + /// + /// Block user + /// + /// User id + /// + public async Task> BlockUserAsync(long userId) + { + return await _userProcessor.BlockUserAsync(userId); } + /// + /// Stop Block user + /// + /// User id + /// + public async Task> UnBlockUserAsync(long userId) + { + return await _userProcessor.UnBlockUserAsync(userId); + } + /// + /// Set current account private + /// + /// public async Task> SetAccountPrivateAsync() { ValidateUser(); ValidateLoggedIn(); - try - { - var instaUri = UriCreator.GetUriSetAccountPrivate(); - var fields = new Dictionary - { - {"_uuid", _deviceInfo.DeviceGuid.ToString()}, - {"_uid", _user.LoggedInUder.Pk}, - {"_csrftoken", _user.CsrfToken} - }; - var hash = CryptoHelper.CalculateHash(InstaApiConstants.IG_SIGNATURE_KEY, - JsonConvert.SerializeObject(fields)); - var payload = JsonConvert.SerializeObject(fields); - var signature = $"{hash}.{Uri.EscapeDataString(payload)}"; - var request = HttpHelper.GetDefaultRequest(HttpMethod.Post, instaUri, _deviceInfo); - request.Content = new FormUrlEncodedContent(fields); - request.Properties.Add(InstaApiConstants.HEADER_IG_SIGNATURE, signature); - request.Properties.Add(InstaApiConstants.HEADER_IG_SIGNATURE_KEY_VERSION, - InstaApiConstants.IG_SIGNATURE_KEY_VERSION); - var response = await _httpRequestProcessor.SendAsync(request); - var json = await response.Content.ReadAsStringAsync(); - if (response.StatusCode == HttpStatusCode.OK) - { - var userInfoUpdated = - JsonConvert.DeserializeObject(json, new InstaUserShortDataConverter()); - if (string.IsNullOrEmpty(userInfoUpdated.Pk)) - return Result.Fail("Pk is null or empty"); - var converter = ConvertersFabric.GetUserShortConverter(userInfoUpdated); - return Result.Success(converter.Convert()); - } - return Result.UnExpectedResponse(response, json); - } - catch (Exception exception) - { - LogException(exception); - return Result.Fail(exception.Message, (InstaUserShort) null); - } + return await _profileProcessor.SetAccountPrivateAsync(); } + /// + /// Set current account public + /// + /// public async Task> SetAccountPublicAsync() { ValidateUser(); ValidateLoggedIn(); - try - { - var instaUri = UriCreator.GetUriSetAccountPublic(); - var fields = new Dictionary - { - {"_uuid", _deviceInfo.DeviceGuid.ToString()}, - {"_uid", _user.LoggedInUder.Pk}, - {"_csrftoken", _user.CsrfToken} - }; - var hash = CryptoHelper.CalculateHash(InstaApiConstants.IG_SIGNATURE_KEY, - JsonConvert.SerializeObject(fields)); - var payload = JsonConvert.SerializeObject(fields); - var signature = $"{hash}.{Uri.EscapeDataString(payload)}"; - var request = HttpHelper.GetDefaultRequest(HttpMethod.Post, instaUri, _deviceInfo); - request.Content = new FormUrlEncodedContent(fields); - request.Properties.Add(InstaApiConstants.HEADER_IG_SIGNATURE, signature); - request.Properties.Add(InstaApiConstants.HEADER_IG_SIGNATURE_KEY_VERSION, - InstaApiConstants.IG_SIGNATURE_KEY_VERSION); - var response = await _httpRequestProcessor.SendAsync(request); - var json = await response.Content.ReadAsStringAsync(); - if (response.StatusCode == HttpStatusCode.OK) - { - var userInfoUpdated = - JsonConvert.DeserializeObject(json, new InstaUserShortDataConverter()); - if (string.IsNullOrEmpty(userInfoUpdated.Pk)) - return Result.Fail("Pk is null or empty"); - var converter = ConvertersFabric.GetUserShortConverter(userInfoUpdated); - return Result.Success(converter.Convert()); - } - return Result.UnExpectedResponse(response, json); - } - catch (Exception exception) - { - LogException(exception); - return Result.Fail(exception.Message, (InstaUserShort) null); - } + return await _profileProcessor.SetAccountPublicAsync(); } + /// + /// Comment media + /// + /// Media id + /// Comment text + /// public async Task> CommentMediaAsync(string mediaId, string text) { ValidateUser(); ValidateLoggedIn(); - try - { - var instaUri = UriCreator.GetPostCommetUri(mediaId); - var breadcrumb = CryptoHelper.GetCommentBreadCrumbEncoded(text); - var fields = new Dictionary - { - {"user_breadcrumb", breadcrumb}, - {"idempotence_token", Guid.NewGuid().ToString()}, - {"_uuid", _deviceInfo.DeviceGuid.ToString()}, - {"_uid", _user.LoggedInUder.Pk}, - {"_csrftoken", _user.CsrfToken}, - {"comment_text", text}, - {"containermodule", "comments_feed_timeline"}, - {"radio_type", "wifi-none"} - }; - var request = HttpHelper.GetSignedRequest(HttpMethod.Post, instaUri, _deviceInfo, fields); - var response = await _httpRequestProcessor.SendAsync(request); - var json = await response.Content.ReadAsStringAsync(); - if (response.StatusCode == HttpStatusCode.OK) - { - var commentResponse = JsonConvert.DeserializeObject(json, - new InstaCommentDataConverter()); - var converter = ConvertersFabric.GetCommentConverter(commentResponse); - return Result.Success(converter.Convert()); - } - return Result.UnExpectedResponse(response, json); - } - catch (Exception exception) - { - return Result.Fail(exception.Message, (InstaComment) null); - } + return await _commentProcessor.CommentMediaAsync(mediaId, text); } + /// + /// Delete comment from media + /// + /// Media id + /// Comment id + /// public async Task> DeleteCommentAsync(string mediaId, string commentId) { ValidateUser(); ValidateLoggedIn(); - try - { - var instaUri = UriCreator.GetDeleteCommetUri(mediaId, commentId); - var fields = new Dictionary - { - {"_uuid", _deviceInfo.DeviceGuid.ToString()}, - {"_uid", _user.LoggedInUder.Pk}, - {"_csrftoken", _user.CsrfToken} - }; - var request = HttpHelper.GetSignedRequest(HttpMethod.Post, instaUri, _deviceInfo, fields); - var response = await _httpRequestProcessor.SendAsync(request); - var json = await response.Content.ReadAsStringAsync(); - if (response.StatusCode == HttpStatusCode.OK) - return Result.Success(true); - return Result.UnExpectedResponse(response, json); - } - catch (Exception exception) - { - LogException(exception); - return Result.Fail(exception.Message, false); - } + return await _commentProcessor.DeleteCommentAsync(mediaId, commentId); } + /// + /// Upload photo + /// + /// Photo to upload + /// Caption + /// public async Task> UploadPhotoAsync(InstaImage image, string caption) { ValidateUser(); ValidateLoggedIn(); - try - { - var instaUri = UriCreator.GetUploadPhotoUri(); - var uploadId = ApiRequestMessage.GenerateUploadId(); - var requestContent = new MultipartFormDataContent(uploadId) - { - {new StringContent(uploadId), "\"upload_id\""}, - {new StringContent(_deviceInfo.DeviceGuid.ToString()), "\"_uuid\""}, - {new StringContent(_user.CsrfToken), "\"_csrftoken\""}, - { - new StringContent("{\"lib_name\":\"jt\",\"lib_version\":\"1.3.0\",\"quality\":\"87\"}"), - "\"image_compression\"" - } - }; - var imageContent = new ByteArrayContent(File.ReadAllBytes(image.URI)); - imageContent.Headers.Add("Content-Transfer-Encoding", "binary"); - imageContent.Headers.Add("Content-Type", "application/octet-stream"); - requestContent.Add(imageContent, "photo", $"pending_media_{ApiRequestMessage.GenerateUploadId()}.jpg"); - var request = HttpHelper.GetDefaultRequest(HttpMethod.Post, instaUri, _deviceInfo); - request.Content = requestContent; - var response = await _httpRequestProcessor.SendAsync(request); - var json = await response.Content.ReadAsStringAsync(); - if (response.IsSuccessStatusCode) - return await ConfigurePhotoAsync(image, uploadId, caption); - return Result.UnExpectedResponse(response, json); - } - catch (Exception exception) - { - return Result.Fail(exception.Message, (InstaMedia) null); - } + return await _mediaProcessor.UploadPhotoAsync(image, caption); + } + + /// + /// Upload photo + /// + /// Array of photos to upload + /// Caption + /// + public async Task> UploadPhotosAlbumAsync(InstaImage[] images, string caption) + { + ValidateUser(); + ValidateLoggedIn(); + return await _mediaProcessor.UploadPhotosAlbumAsync(images, caption); } + /// + /// Configure photo + /// + /// Photo to configure + /// Upload id + /// Caption + /// public async Task> ConfigurePhotoAsync(InstaImage image, string uploadId, string caption) { ValidateUser(); ValidateLoggedIn(); - try - { - var instaUri = UriCreator.GetMediaConfigureUri(); - var androidVersion = - AndroidVersion.FromString(_deviceInfo.FirmwareFingerprint.Split('/')[2].Split(':')[1]); - if (androidVersion == null) - return Result.Fail("Unsupported android version", (InstaMedia) null); - var data = new JObject - { - {"_uuid", _deviceInfo.DeviceGuid.ToString()}, - {"_uid", _user.LoggedInUder.Pk}, - {"_csrftoken", _user.CsrfToken}, - {"media_folder", "Camera"}, - {"source_type", "4"}, - {"caption", caption}, - {"upload_id", uploadId}, - { - "device", new JObject - { - {"manufacturer", _deviceInfo.HardwareManufacturer}, - {"model", _deviceInfo.HardwareModel}, - {"android_version", androidVersion.VersionNumber}, - {"android_release", androidVersion.APILevel} - } - }, - { - "edits", new JObject - { - {"crop_original_size", new JArray {image.Width, image.Height}}, - {"crop_center", new JArray {0.0, -0.0}}, - {"crop_zoom", 1} - } - }, - { - "extra", new JObject - { - {"source_width", image.Width}, - {"source_height", image.Height} - } - } - }; - var request = HttpHelper.GetSignedRequest(HttpMethod.Post, instaUri, _deviceInfo, data); - var response = await _httpRequestProcessor.SendAsync(request); - var json = await response.Content.ReadAsStringAsync(); - if (response.IsSuccessStatusCode) - { - var mediaResponse = JsonConvert.DeserializeObject(json); - var converter = ConvertersFabric.GetSingleMediaConverter(mediaResponse); - return Result.Success(converter.Convert()); - } - return Result.UnExpectedResponse(response, json); - } - catch (Exception exception) - { - LogException(exception); - return Result.Fail(exception.Message, (InstaMedia) null); - } + return await _mediaProcessor.ConfigurePhotoAsync(image, uploadId, caption); + } + + /// + /// Configure photos for Album + /// + /// Array of upload IDs to configure + /// /// + /// Caption + /// + public async Task> ConfigureAlbumAsync(string[] uploadIds, string caption) + { + ValidateUser(); + ValidateLoggedIn(); + return await _mediaProcessor.ConfigureAlbumAsync(uploadIds, caption); } + /// + /// Get user story feed (stories from users followed by current user). + /// + /// public async Task> GetStoryFeedAsync() { ValidateUser(); ValidateLoggedIn(); - - try - { - var storyFeedUri = UriCreator.GetStoryFeedUri(); - var request = HttpHelper.GetDefaultRequest(HttpMethod.Get, storyFeedUri, _deviceInfo); - var response = await _httpRequestProcessor.SendAsync(request); - var json = await response.Content.ReadAsStringAsync(); - if (response.StatusCode != HttpStatusCode.OK) return Result.Fail("", (InstaStoryFeed) null); - var storyFeedResponse = JsonConvert.DeserializeObject(json); - var instaStoryFeed = ConvertersFabric.GetStoryFeedConverter(storyFeedResponse).Convert(); - return Result.Success(instaStoryFeed); - } - catch (Exception exception) - { - LogException(exception); - return Result.Fail(exception.Message, (InstaStoryFeed) null); - } + return await _storyProcessor.GetStoryFeedAsync(); } + /// + /// Get the story by userId + /// + /// User Id + /// public async Task> GetUserStoryAsync(long userId) { ValidateUser(); ValidateLoggedIn(); - - try - { - var userStoryUri = UriCreator.GetUserStoryUri(userId); - var request = HttpHelper.GetDefaultRequest(HttpMethod.Get, userStoryUri, _deviceInfo); - var response = await _httpRequestProcessor.SendAsync(request); - var json = await response.Content.ReadAsStringAsync(); - - if (response.StatusCode != HttpStatusCode.OK) return Result.Fail("", (InstaStory) null); - var userStory = new InstaStory(); - var userStoryResponse = JsonConvert.DeserializeObject(json); - - userStory = ConvertersFabric.GetStoryConverter(userStoryResponse).Convert(); - - return Result.Success(userStory); - } - catch (Exception exception) - { - LogException(exception); - return Result.Fail(exception.Message, (InstaStory) null); - } + return await _storyProcessor.GetUserStoryAsync(userId); } + /// + /// Upload story photo + /// + /// Photo to upload + /// Caption + /// public async Task> UploadStoryPhotoAsync(InstaImage image, string caption) { ValidateUser(); ValidateLoggedIn(); - try - { - var instaUri = UriCreator.GetUploadPhotoUri(); - var uploadId = ApiRequestMessage.GenerateUploadId(); - var requestContent = new MultipartFormDataContent(uploadId) - { - {new StringContent(uploadId), "\"upload_id\""}, - {new StringContent(_deviceInfo.DeviceGuid.ToString()), "\"_uuid\""}, - {new StringContent(_user.CsrfToken), "\"_csrftoken\""}, - { - new StringContent("{\"lib_name\":\"jt\",\"lib_version\":\"1.3.0\",\"quality\":\"87\"}"), - "\"image_compression\"" - } - }; - var imageContent = new ByteArrayContent(File.ReadAllBytes(image.URI)); - imageContent.Headers.Add("Content-Transfer-Encoding", "binary"); - imageContent.Headers.Add("Content-Type", "application/octet-stream"); - requestContent.Add(imageContent, "photo", $"pending_media_{ApiRequestMessage.GenerateUploadId()}.jpg"); - var request = HttpHelper.GetDefaultRequest(HttpMethod.Post, instaUri, _deviceInfo); - request.Content = requestContent; - var response = await _httpRequestProcessor.SendAsync(request); - var json = await response.Content.ReadAsStringAsync(); - if (response.IsSuccessStatusCode) - return await ConfigureStoryPhotoAsync(image, uploadId, caption); - return Result.UnExpectedResponse(response, json); - } - catch (Exception exception) - { - return Result.Fail(exception.Message, (InstaStoryMedia) null); - } + return await _storyProcessor.UploadStoryPhotoAsync(image, caption); } + /// + /// Configure story photo + /// + /// Photo to configure + /// Upload id + /// Caption + /// public async Task> ConfigureStoryPhotoAsync(InstaImage image, string uploadId, string caption) { ValidateUser(); ValidateLoggedIn(); - try - { - var instaUri = UriCreator.GetStoryConfigureUri(); - var data = new JObject - { - {"_uuid", _deviceInfo.DeviceGuid.ToString()}, - {"_uid", _user.LoggedInUder.Pk}, - {"_csrftoken", _user.CsrfToken}, - {"source_type", "1"}, - {"caption", caption}, - {"upload_id", uploadId}, - {"edits", new JObject()}, - {"disable_comments", false}, - {"configure_mode", 1}, - {"camera_position", "unknown"} - }; - var request = HttpHelper.GetSignedRequest(HttpMethod.Post, instaUri, _deviceInfo, data); - var response = await _httpRequestProcessor.SendAsync(request); - var json = await response.Content.ReadAsStringAsync(); - if (response.IsSuccessStatusCode) - { - var mediaResponse = JsonConvert.DeserializeObject(json); - var converter = ConvertersFabric.GetStoryMediaConverter(mediaResponse); - return Result.Success(converter.Convert()); - } - return Result.UnExpectedResponse(response, json); - } - catch (Exception exception) - { - LogException(exception); - return Result.Fail(exception.Message, (InstaStoryMedia) null); - } + return await _storyProcessor.ConfigureStoryPhotoAsync(image, uploadId, caption); } + /// + /// Change password + /// + /// The old password + /// + /// The new password (shouldn't be the same old password, and should be a password you never used + /// here) + /// + /// + /// Return true if the password is changed + /// public async Task> ChangePasswordAsync(string oldPassword, string newPassword) { ValidateUser(); ValidateLoggedIn(); - - if (oldPassword == newPassword) - return Result.Fail("The old password should not the same of the new password", false); - - try - { - var changePasswordUri = UriCreator.GetChangePasswordUri(); - - var data = new JObject - { - {"_uuid", _deviceInfo.DeviceGuid.ToString()}, - {"_uid", _user.LoggedInUder.Pk}, - {"_csrftoken", _user.CsrfToken}, - {"old_password", oldPassword}, - {"new_password1", newPassword}, - {"new_password2", newPassword} - }; - - var request = HttpHelper.GetSignedRequest(HttpMethod.Get, changePasswordUri, _deviceInfo, data); - var response = await _httpRequestProcessor.SendAsync(request); - var json = await response.Content.ReadAsStringAsync(); - if (response.StatusCode == HttpStatusCode.OK) - return Result.Success(true); //If status code is OK, then the password is surely changed - var error = JsonConvert.DeserializeObject(json); - var errors = ""; - error.Message.Errors.ForEach(errorContent => errors += errorContent + "\n"); - return Result.Fail(errors, false); - } - catch (Exception exception) - { - return Result.Fail(exception.Message, false); - } + return await _profileProcessor.ChangePasswordAsync(oldPassword, newPassword); } + /// + /// Delete a media (photo or video) + /// + /// The media ID + /// The type of the media + /// + /// Return true if the media is deleted + /// public async Task> DeleteMediaAsync(string mediaId, InstaMediaType mediaType) { ValidateUser(); ValidateLoggedIn(); - - try - { - var deleteMediaUri = UriCreator.GetDeleteMediaUri(mediaId, mediaType); - - var data = new JObject - { - {"_uuid", _deviceInfo.DeviceGuid.ToString()}, - {"_uid", _user.LoggedInUder.Pk}, - {"_csrftoken", _user.CsrfToken}, - {"media_id", mediaId} - }; - - var request = HttpHelper.GetSignedRequest(HttpMethod.Get, deleteMediaUri, _deviceInfo, data); - var response = await _httpRequestProcessor.SendAsync(request); - var json = await response.Content.ReadAsStringAsync(); - if (response.StatusCode == HttpStatusCode.OK) - { - var deletedResponse = JsonConvert.DeserializeObject(json); - return Result.Success(deletedResponse.IsDeleted); - } - var error = JsonConvert.DeserializeObject(json); - var errors = ""; - error.Message.Errors.ForEach(errorContent => errors += errorContent + "\n"); - return Result.Fail(errors, false); - } - catch (Exception exception) - { - LogException(exception); - return Result.Fail(exception.Message, false); - } + return await _mediaProcessor.DeleteMediaAsync(mediaId, mediaType); } + /// + /// Edit the caption of the media (photo/video) + /// + /// The media ID + /// The new caption + /// + /// Return true if everything is ok + /// public async Task> EditMediaAsync(string mediaId, string caption) { ValidateUser(); ValidateLoggedIn(); - - try - { - var editMediaUri = UriCreator.GetEditMediaUri(mediaId); - - var data = new JObject - { - {"_uuid", _deviceInfo.DeviceGuid.ToString()}, - {"_uid", _user.LoggedInUder.Pk}, - {"_csrftoken", _user.CsrfToken}, - {"caption_text", caption} - }; - - var request = HttpHelper.GetSignedRequest(HttpMethod.Get, editMediaUri, _deviceInfo, data); - var response = await _httpRequestProcessor.SendAsync(request); - var json = await response.Content.ReadAsStringAsync(); - if (response.StatusCode == HttpStatusCode.OK) - return - Result.Success( - true); //Technically Instagram returns the InstaMediaItem, but it is useless in our case, at this time. - var error = JsonConvert.DeserializeObject(json); - return Result.Fail(error.Message, false); - } - catch (Exception exception) - { - return Result.Fail(exception.Message, false); - } + return await _mediaProcessor.EditMediaAsync(mediaId, caption); } - public async Task> GetLikeFeedAsync(int maxPages = 0) + /// + /// Get feed of media your liked. + /// + /// Pagination parameters: next id and max amount of pages to load + /// + /// + /// + public async Task> GetLikeFeedAsync(PaginationParameters paginationParameters) { ValidateUser(); - if (maxPages == 0) maxPages = int.MaxValue; - var instaUri = UriCreator.GetUserLikeFeedUri(); - var request = HttpHelper.GetDefaultRequest(HttpMethod.Get, instaUri, _deviceInfo); - var response = await _httpRequestProcessor.SendAsync(request); - var json = await response.Content.ReadAsStringAsync(); - if (response.StatusCode == HttpStatusCode.OK) - { - var mediaResponse = JsonConvert.DeserializeObject(json, - new InstaMediaListDataConverter()); - var moreAvailable = mediaResponse.MoreAvailable; - var converter = ConvertersFabric.GetMediaListConverter(mediaResponse); - var mediaList = converter.Convert(); - mediaList.Pages++; - var nextId = mediaResponse.NextMaxId; - while (moreAvailable && mediaList.Pages < maxPages) - { - var result = await GetLikeFeedInternal(nextId); - if (!result.Succeeded) - return Result.Fail(result.Info, mediaList); - converter = ConvertersFabric.GetMediaListConverter(result.Value); - mediaList.AddRange(converter.Convert()); - mediaList.Pages++; - nextId = mediaResponse.NextMaxId; - moreAvailable = result.Value.MoreAvailable; - } - return Result.Success(mediaList); - } - return Result.UnExpectedResponse(response, json); + return await _feedProcessor.GetLikeFeedAsync(paginationParameters); } - public async Task> GetLikeFeedInternal(string maxId = "") + /// + /// Get friendship status for given user id. + /// + /// User identifier (PK) + /// + /// + /// + public async Task> GetFriendshipStatusAsync(long userId) { - var instaUri = UriCreator.GetUserLikeFeedUri(maxId); - var request = HttpHelper.GetDefaultRequest(HttpMethod.Get, instaUri, _deviceInfo); - var response = await _httpRequestProcessor.SendAsync(request); - var json = await response.Content.ReadAsStringAsync(); - if (response.StatusCode != HttpStatusCode.OK) - return Result.UnExpectedResponse(response, json); - var mediaResponse = JsonConvert.DeserializeObject(json, - new InstaMediaListDataConverter()); - return Result.Success(mediaResponse); + ValidateUser(); + ValidateLoggedIn(); + return await _userProcessor.GetFriendshipStatusAsync(userId); } - public async Task> GetFriendshipStatusAsync(long userId) + /// + /// Get your collection for given collection id + /// + /// Collection ID + /// + /// + /// + public async Task> GetCollectionAsync(long collectionId) { ValidateUser(); - var userUri = UriCreator.GetUserFriendshipUri(userId); - var request = HttpHelper.GetDefaultRequest(HttpMethod.Get, userUri, _deviceInfo); - var response = await _httpRequestProcessor.SendAsync(request); - var json = await response.Content.ReadAsStringAsync(); - if (response.StatusCode != HttpStatusCode.OK) - return Result.UnExpectedResponse(response, json); - var friendshipStatusResponse = JsonConvert.DeserializeObject(json); - var converter = ConvertersFabric.GetFriendShipStatusConverter(friendshipStatusResponse); - return Result.Success(converter.Convert()); + ValidateLoggedIn(); + return await _collectionProcessor.GetCollectionAsync(collectionId); } - #endregion - #region private part - - private void ValidateUser() + /// + /// Get your collections + /// + /// + /// + /// + public async Task> GetCollectionsAsync() { - if (string.IsNullOrEmpty(_user.UserName) || string.IsNullOrEmpty(_user.Password)) - throw new ArgumentException("user name and password must be specified"); + ValidateUser(); + ValidateLoggedIn(); + return await _collectionProcessor.GetCollectionsAsync(); } - private void ValidateLoggedIn() + /// + /// Create a new collection + /// + /// The name of the new collection + /// + /// + /// + public async Task> CreateCollectionAsync(string collectionName) { - if (!IsUserAuthenticated) throw new ArgumentException("user must be authenticated"); + ValidateUser(); + ValidateLoggedIn(); + return await _collectionProcessor.CreateCollectionAsync(collectionName); } - private void ValidateRequestMessage() + public async Task> AddItemsToCollectionAsync(long collectionId, + params string[] mediaIds) { - if (_httpRequestProcessor.RequestMessage == null || _httpRequestProcessor.RequestMessage.IsEmpty()) - throw new ArgumentException("API request message null or empty"); + ValidateUser(); + ValidateLoggedIn(); + return await _collectionProcessor.AddItemsToCollectionAsync(collectionId, mediaIds); } - private async Task> GetUserFeedWithMaxIdAsync(string maxId) - { - if (!Uri.TryCreate(new Uri(InstaApiConstants.INSTAGRAM_URL), InstaApiConstants.TIMELINEFEED, - out var instaUri)) - throw new Exception("Cant create search user URI"); - var userUriBuilder = new UriBuilder(instaUri) {Query = $"max_id={maxId}"}; - var request = HttpHelper.GetDefaultRequest(HttpMethod.Get, userUriBuilder.Uri, _deviceInfo); - request.Properties.Add(new KeyValuePair(InstaApiConstants.HEADER_PHONE_ID, - _httpRequestProcessor.RequestMessage.phone_id)); - request.Properties.Add(new KeyValuePair(InstaApiConstants.HEADER_TIMEZONE, - InstaApiConstants.TIMEZONE_OFFSET.ToString())); - var response = await _httpRequestProcessor.SendAsync(request); - var json = await response.Content.ReadAsStringAsync(); - if (response.StatusCode == HttpStatusCode.OK) - { - var feedResponse = JsonConvert.DeserializeObject(json, - new InstaFeedResponseDataConverter()); - return Result.Success(feedResponse); - } - return Result.UnExpectedResponse(response, json); + /// + /// Delete your collection for given collection id + /// + /// Collection ID to delete + /// true if succeed + public async Task> DeleteCollectionAsync(long collectionId) + { + ValidateUser(); + ValidateLoggedIn(); + return await _collectionProcessor.DeleteCollectionAsync(collectionId); } - private async Task> GetFollowingActivityWithMaxIdAsync(string maxId) + /// + /// Get media ID from an url (got from "share link") + /// + /// Uri to get media ID + /// Media ID + public async Task> GetMediaIdFromUrlAsync(Uri uri) { - var uri = UriCreator.GetFollowingRecentActivityUri(maxId); - var request = HttpHelper.GetDefaultRequest(HttpMethod.Get, uri, _deviceInfo); - var response = await _httpRequestProcessor.SendAsync(request); - var json = await response.Content.ReadAsStringAsync(); - if (response.StatusCode == HttpStatusCode.OK) - { - var followingActivity = JsonConvert.DeserializeObject(json, - new InstaRecentActivityConverter()); - return Result.Success(followingActivity); - } - return Result.UnExpectedResponse(response, json); + ValidateLoggedIn(); + ValidateRequestMessage(); + return await _mediaProcessor.GetMediaIdFromUrlAsync(uri); } - private async Task> GetUserMediaListWithMaxIdAsync(Uri instaUri) + /// + /// Get share link from media Id + /// + /// media ID + /// Share link as Uri + public async Task> GetShareLinkFromMediaIdAsync(string mediaId) { - var request = HttpHelper.GetDefaultRequest(HttpMethod.Get, instaUri, _deviceInfo); - var response = await _httpRequestProcessor.SendAsync(request); - var json = await response.Content.ReadAsStringAsync(); - if (response.StatusCode == HttpStatusCode.OK) - { - var mediaResponse = JsonConvert.DeserializeObject(json, - new InstaMediaListDataConverter()); - return Result.Success(mediaResponse); - } - return Result.Fail("", (InstaMediaListResponse) null); + return await _mediaProcessor.GetShareLinkFromMediaIdAsync(mediaId); } - private async Task> GetUserListByURIAsync(Uri uri) + /// + /// Searches for specific location by provided geo-data or search query. + /// + /// Latitude + /// Longitude + /// Search query + /// + /// List of locations (short format) + /// + public async Task> SearchLocation(double latitude, double longitude, + string query) { ValidateUser(); - try - { - if (!IsUserAuthenticated) throw new ArgumentException("user must be authenticated"); - var request = HttpHelper.GetDefaultRequest(HttpMethod.Get, uri, _deviceInfo); - var response = await _httpRequestProcessor.SendAsync(request); - var json = await response.Content.ReadAsStringAsync(); - if (response.StatusCode == HttpStatusCode.OK) - { - var instaUserListResponse = JsonConvert.DeserializeObject(json); - if (!instaUserListResponse.IsOk()) Result.Fail("", (InstaUserListShortResponse) null); - return Result.Success(instaUserListResponse); - } - return Result.UnExpectedResponse(response, json); - } - catch (Exception exception) - { - LogException(exception); - return Result.Fail(exception.Message, (InstaUserListShortResponse) null); - } + ValidateLoggedIn(); + return await _locationProcessor.Search(latitude, longitude, query); } - private async Task> GetRecentActivityInternalAsync(Uri uri, int maxPages = 0) + /// + /// Gets the feed of particular location. + /// + /// Location identifier + /// Pagination parameters: next id and max amount of pages to load + /// + /// Location feed + /// + public async Task> GetLocationFeed(long locationId, + PaginationParameters paginationParameters) { + ValidateUser(); ValidateLoggedIn(); - if (maxPages == 0) maxPages = int.MaxValue; - - var request = HttpHelper.GetDefaultRequest(HttpMethod.Get, uri, _deviceInfo); - var response = await _httpRequestProcessor.SendAsync(request, HttpCompletionOption.ResponseContentRead); - var activityFeed = new InstaActivityFeed(); - var json = await response.Content.ReadAsStringAsync(); - if (response.StatusCode != HttpStatusCode.OK) - return Result.UnExpectedResponse(response, json); - var feedPage = JsonConvert.DeserializeObject(json, - new InstaRecentActivityConverter()); - activityFeed.IsOwnActivity = feedPage.IsOwnActivity; - var nextId = feedPage.NextMaxId; - activityFeed.Items.AddRange( - feedPage.Stories.Select(ConvertersFabric.GetSingleRecentActivityConverter) - .Select(converter => converter.Convert())); - var pages = 1; - while (!string.IsNullOrEmpty(nextId) && pages < maxPages) - { - var nextFollowingFeed = await GetFollowingActivityWithMaxIdAsync(nextId); - if (!nextFollowingFeed.Succeeded) - return Result.Success($"Not all pages was downloaded: {nextFollowingFeed.Info.Message}", - activityFeed); - nextId = nextFollowingFeed.Value.NextMaxId; - activityFeed.Items.AddRange( - feedPage.Stories.Select(ConvertersFabric.GetSingleRecentActivityConverter) - .Select(converter => converter.Convert())); - pages++; - } - return Result.Success(activityFeed); + return await _locationProcessor.GetFeed(locationId, paginationParameters); } - private async Task> GetTagFeedWithMaxIdAsync(string tag, string nextId) + + #region Authentication/State data + + /// + /// Indicates whether user authenticated or not + /// + public bool IsUserAuthenticated { get; private set; } + + /// + /// Login using given credentials asynchronously + /// + /// + /// Success --> is succeed + /// TwoFactorRequired --> requires 2FA login. + /// BadPassword --> Password is wrong + /// InvalidUser --> User/phone number is wrong + /// Exception --> Something wrong happened + /// + public async Task> LoginAsync() { ValidateUser(); - ValidateLoggedIn(); + ValidateRequestMessage(); try { - var instaUri = UriCreator.GetTagFeedUri(tag); - instaUri = new UriBuilder(instaUri) {Query = $"max_id={nextId}"}.Uri; - var request = HttpHelper.GetDefaultRequest(HttpMethod.Get, instaUri, _deviceInfo); + var csrftoken = string.Empty; + var firstResponse = await _httpRequestProcessor.GetAsync(_httpRequestProcessor.Client.BaseAddress); + var cookies = + _httpRequestProcessor.HttpHandler.CookieContainer.GetCookies(_httpRequestProcessor.Client + .BaseAddress); + _logger?.LogResponse(firstResponse); + foreach (Cookie cookie in cookies) + if (cookie.Name == InstaApiConstants.CSRFTOKEN) csrftoken = cookie.Value; + _user.CsrfToken = csrftoken; + var instaUri = UriCreator.GetLoginUri(); + var signature = + $"{_httpRequestProcessor.RequestMessage.GenerateSignature(InstaApiConstants.IG_SIGNATURE_KEY)}.{_httpRequestProcessor.RequestMessage.GetMessageString()}"; + var fields = new Dictionary + { + {InstaApiConstants.HEADER_IG_SIGNATURE, signature}, + {InstaApiConstants.HEADER_IG_SIGNATURE_KEY_VERSION, InstaApiConstants.IG_SIGNATURE_KEY_VERSION} + }; + var request = HttpHelper.GetDefaultRequest(HttpMethod.Post, instaUri, _deviceInfo); + request.Content = new FormUrlEncodedContent(fields); + request.Properties.Add(InstaApiConstants.HEADER_IG_SIGNATURE, signature); + request.Properties.Add(InstaApiConstants.HEADER_IG_SIGNATURE_KEY_VERSION, + InstaApiConstants.IG_SIGNATURE_KEY_VERSION); var response = await _httpRequestProcessor.SendAsync(request); var json = await response.Content.ReadAsStringAsync(); - if (response.StatusCode == HttpStatusCode.OK) + if (response.StatusCode != HttpStatusCode.OK + ) //If the password is correct BUT 2-Factor Authentication is enabled, it will still get a 400 error (bad request) { - var feedResponse = JsonConvert.DeserializeObject(json, - new InstaMediaListDataConverter()); - return Result.Success(feedResponse); + //Then check it + var loginFailReason = JsonConvert.DeserializeObject(json); + + if (loginFailReason.InvalidCredentials) + return Result.Fail("Invalid Credentials", + loginFailReason.ErrorType == "bad_password" + ? InstaLoginResult.BadPassword + : InstaLoginResult.InvalidUser); + if (loginFailReason.TwoFactorRequired) + { + _twoFactorInfo = loginFailReason.TwoFactorLoginInfo; + //2FA is required! + return Result.Fail("Two Factor Authentication is required", InstaLoginResult.TwoFactorRequired); + } + + return Result.UnExpectedResponse(response, json); } - return Result.UnExpectedResponse(response, json); + var loginInfo = + JsonConvert.DeserializeObject(json); + IsUserAuthenticated = loginInfo.User != null && + loginInfo.User.UserName.ToLower() == _user.UserName.ToLower(); + var converter = ConvertersFabric.Instance.GetUserShortConverter(loginInfo.User); + _user.LoggedInUder = converter.Convert(); + _user.RankToken = $"{_user.LoggedInUder.Pk}_{_httpRequestProcessor.RequestMessage.phone_id}"; + return Result.Success(InstaLoginResult.Success); } catch (Exception exception) { - return Result.Fail(exception.Message, (InstaMediaListResponse) null); + LogException(exception); + return Result.Fail(exception, InstaLoginResult.Exception); } - } - - private async Task> GetCommentListWithMaxIdAsync(string mediaId, - string nextId) - { - var commentsUri = UriCreator.GetMediaCommentsUri(mediaId); - var commentsUriMaxId = new UriBuilder(commentsUri) {Query = $"max_id={nextId}"}.Uri; - var request = HttpHelper.GetDefaultRequest(HttpMethod.Get, commentsUriMaxId, _deviceInfo); - var response = await _httpRequestProcessor.SendAsync(request); - var json = await response.Content.ReadAsStringAsync(); - if (response.StatusCode == HttpStatusCode.OK) + finally { - var comments = JsonConvert.DeserializeObject(json); - return Result.Success(comments); + InvalidateProcessors(); } - return Result.Fail("", (InstaCommentListResponse) null); } - private async Task> FollowUnfollowUserInternal(long userId, Uri instaUri) + /// + /// 2-Factor Authentication Login using a verification code + /// Before call this method, please run LoginAsync first. + /// + /// Verification Code sent to your phone number + /// + /// Success --> is succeed + /// InvalidCode --> The code is invalid + /// CodeExpired --> The code is expired, please request a new one. + /// Exception --> Something wrong happened + /// + public async Task> TwoFactorLoginAsync(string verificationCode) { - ValidateUser(); - ValidateLoggedIn(); + if (_twoFactorInfo == null) + return Result.Fail("Run LoginAsync first"); + try { + var twoFactorRequestMessage = new ApiTwoFactorRequestMessage(verificationCode, + _httpRequestProcessor.RequestMessage.username, + _httpRequestProcessor.RequestMessage.device_id, + _twoFactorInfo.TwoFactorIdentifier); + + var instaUri = UriCreator.GetTwoFactorLoginUri(); + var signature = + $"{twoFactorRequestMessage.GenerateSignature(InstaApiConstants.IG_SIGNATURE_KEY)}.{twoFactorRequestMessage.GetMessageString()}"; var fields = new Dictionary { - {"_uuid", _deviceInfo.DeviceGuid.ToString()}, - {"_uid", _user.LoggedInUder.Pk}, - {"_csrftoken", _user.CsrfToken}, - {"user_id", userId.ToString()}, - {"radio_type", "wifi-none"} + {InstaApiConstants.HEADER_IG_SIGNATURE, signature}, + { + InstaApiConstants.HEADER_IG_SIGNATURE_KEY_VERSION, + InstaApiConstants.IG_SIGNATURE_KEY_VERSION + } }; - var request = HttpHelper.GetSignedRequest(HttpMethod.Post, instaUri, _deviceInfo, fields); + var request = HttpHelper.GetDefaultRequest(HttpMethod.Post, instaUri, _deviceInfo); + request.Content = new FormUrlEncodedContent(fields); + request.Properties.Add(InstaApiConstants.HEADER_IG_SIGNATURE, signature); + request.Properties.Add(InstaApiConstants.HEADER_IG_SIGNATURE_KEY_VERSION, + InstaApiConstants.IG_SIGNATURE_KEY_VERSION); var response = await _httpRequestProcessor.SendAsync(request); var json = await response.Content.ReadAsStringAsync(); - if (response.StatusCode == HttpStatusCode.OK && !string.IsNullOrEmpty(json)) + + if (response.StatusCode == HttpStatusCode.OK) { - var friendshipStatus = JsonConvert.DeserializeObject(json, - new InstaFriendShipDataConverter()); - var converter = ConvertersFabric.GetFriendShipStatusConverter(friendshipStatus); - return Result.Success(converter.Convert()); + var loginInfo = + JsonConvert.DeserializeObject(json); + IsUserAuthenticated = IsUserAuthenticated = + loginInfo.User != null && loginInfo.User.UserName.ToLower() == _user.UserName.ToLower(); + var converter = ConvertersFabric.Instance.GetUserShortConverter(loginInfo.User); + _user.LoggedInUder = converter.Convert(); + _user.RankToken = $"{_user.LoggedInUder.Pk}_{_httpRequestProcessor.RequestMessage.phone_id}"; + + return Result.Success(InstaLoginTwoFactorResult.Success); } - return Result.UnExpectedResponse(response, json); + var loginFailReason = JsonConvert.DeserializeObject(json); + + if (loginFailReason.ErrorType == "sms_code_validation_code_invalid") + return Result.Fail("Please check the security code.", InstaLoginTwoFactorResult.InvalidCode); + return Result.Fail("This code is no longer valid, please, call LoginAsync again to request a new one", + InstaLoginTwoFactorResult.CodeExpired); } catch (Exception exception) { LogException(exception); - return Result.Fail(exception.Message, (InstaFriendshipStatus) null); + return Result.Fail(exception, InstaLoginTwoFactorResult.Exception); } } - private async Task> GetExploreFeedAsync(string maxId) + /// + /// Get Two Factor Authentication details + /// + /// + /// An instance of TwoFactorInfo if success. + /// A null reference if not success; in this case, do LoginAsync first and check if Two Factor Authentication is + /// required, if not, don't run this method + /// + public async Task> GetTwoFactorInfoAsync() + { + return await Task.Run(() => + _twoFactorInfo != null + ? Result.Success(_twoFactorInfo) + : Result.Fail("No Two Factor info available.")); + } + + /// + /// Logout from instagram asynchronously + /// + /// + /// True if logged out without errors + /// + public async Task> LogoutAsync() { + ValidateUser(); + ValidateLoggedIn(); try { - var exploreUri = UriCreator.GetExploreUri(maxId); - var request = HttpHelper.GetDefaultRequest(HttpMethod.Get, exploreUri, _deviceInfo); + var instaUri = UriCreator.GetLogoutUri(); + var request = HttpHelper.GetDefaultRequest(HttpMethod.Get, instaUri, _deviceInfo); var response = await _httpRequestProcessor.SendAsync(request); var json = await response.Content.ReadAsStringAsync(); - if (response.StatusCode != HttpStatusCode.OK) return Result.Fail("", (InstaExploreFeedResponse) null); - return Result.Success( - JsonConvert.DeserializeObject(json, new InstaExploreFeedDataConverter())); + if (response.StatusCode != HttpStatusCode.OK) return Result.UnExpectedResponse(response, json); + var logoutInfo = JsonConvert.DeserializeObject(json); + IsUserAuthenticated = logoutInfo.Status == "ok"; + return Result.Success(true); } catch (Exception exception) { LogException(exception); - return Result.Fail(exception.Message, (InstaExploreFeedResponse) null); + return Result.Fail(exception, false); } } + /// + /// Get current state info as Memory stream + /// + /// + /// State data + /// + public Stream GetStateDataAsStream() + { + var state = new StateData + { + DeviceInfo = _deviceInfo, + IsAuthenticated = IsUserAuthenticated, + UserSession = _user, + Cookies = _httpRequestProcessor.HttpHandler.CookieContainer + }; + return SerializationHelper.SerializeToStream(state); + } + + /// + /// Loads the state data from stream. + /// + /// The stream. + public void LoadStateDataFromStream(Stream stream) + { + var data = SerializationHelper.DeserializeFromStream(stream); + _deviceInfo = data.DeviceInfo; + _user = data.UserSession; + _httpRequestProcessor.HttpHandler.CookieContainer = data.Cookies; + IsUserAuthenticated = data.IsAuthenticated; + InvalidateProcessors(); + } + + #endregion + + + #region private part + + private void InvalidateProcessors() + { + _locationProcessor = new LocationProcessor(_deviceInfo, _user, _httpRequestProcessor, _logger); + _collectionProcessor = new CollectionProcessor(_deviceInfo, _user, _httpRequestProcessor, _logger); + _mediaProcessor = new MediaProcessor(_deviceInfo, _user, _httpRequestProcessor, _logger); + _userProcessor = new UserProcessor(_deviceInfo, _user, _httpRequestProcessor, _logger); + _storyProcessor = new StoryProcessor(_deviceInfo, _user, _httpRequestProcessor, _logger); + _commentProcessor = new CommentProcessor(_deviceInfo, _user, _httpRequestProcessor, _logger); + _profileProcessor = new UserProfileProcessor(_deviceInfo, _user, _httpRequestProcessor, _logger); + _messagingProcessor = new MessagingProcessor(_deviceInfo, _user, _httpRequestProcessor, _logger); + _feedProcessor = new FeedProcessor(_deviceInfo, _user, _httpRequestProcessor, _logger); + } + + private void ValidateUser() + { + if (string.IsNullOrEmpty(_user.UserName) || string.IsNullOrEmpty(_user.Password)) + throw new ArgumentException("user name and password must be specified"); + } + + private void ValidateLoggedIn() + { + if (!IsUserAuthenticated) throw new ArgumentException("user must be authenticated"); + } + + private void ValidateRequestMessage() + { + if (_httpRequestProcessor.RequestMessage == null || _httpRequestProcessor.RequestMessage.IsEmpty()) + throw new ArgumentException("API request message null or empty"); + } + private void LogException(Exception exception) { _logger?.LogException(exception); diff --git a/InstaSharper/API/InstaApiConstants.cs b/InstaSharper/API/InstaApiConstants.cs index 5613b3a3..55870e04 100644 --- a/InstaSharper/API/InstaApiConstants.cs +++ b/InstaSharper/API/InstaApiConstants.cs @@ -1,4 +1,6 @@ -namespace InstaSharper.API +using System; + +namespace InstaSharper.API { internal static class InstaApiConstants { @@ -20,9 +22,12 @@ internal static class InstaApiConstants public const string HEADER_QUERY = "q"; public const string HEADER_RANK_TOKEN = "rank_token"; public const string HEADER_COUNT = "count"; - public const string IG_SIGNATURE_KEY = "b4946d296abf005163e72346a6d33dd083cadde638e6ad9c5eb92e381b35784a"; + + public const string IG_SIGNATURE_KEY = "b4946d296abf005163e72346a6d33dd083cadde638e6ad9c5eb92e381b35784a" + ; //4749bda4fc1f49372dae3d79db339ce4959cfbbe + public const string HEADER_IG_SIGNATURE = "signed_body"; - public const string IG_SIGNATURE_KEY_VERSION = "4"; + public const string IG_SIGNATURE_KEY_VERSION = "4"; //5 public const string HEADER_IG_SIGNATURE_KEY_VERSION = "ig_sig_key_version"; public const string IG_CAPABILITIES = "3boBAA=="; public const string HEADER_IG_CAPABILITIES = "X-IG-Capabilities"; @@ -43,10 +48,13 @@ internal static class InstaApiConstants public const string API = "/api"; public const string API_SUFFIX = API + API_VERSION; public const string API_VERSION = "/v1"; + public const string BASE_INSTAGRAM_API_URL = INSTAGRAM_URL + API_SUFFIX + "/"; + public const string CURRENTUSER = API_SUFFIX + "/accounts/current_user?edit=true"; public const string SEARCH_USERS = API_SUFFIX + "/users/search"; public const string ACCOUNTS_LOGIN = API_SUFFIX + "/accounts/login/"; + public const string ACCOUNTS_2FA_LOGIN = API_SUFFIX + "/accounts/two_factor_login/"; public const string CHANGE_PASSWORD = API_SUFFIX + "/accounts/change_password/"; public const string ACCOUNTS_LOGOUT = API_SUFFIX + "/accounts/logout/"; public const string EXPLORE = API_SUFFIX + "/discover/explore/"; @@ -58,6 +66,18 @@ internal static class InstaApiConstants public const string GET_USER_FOLLOWING = API_SUFFIX + "/friendships/{0}/following/?rank_token={1}"; public const string GET_TAG_FEED = API_SUFFIX + "/feed/tag/{0}"; public const string GET_RANKED_RECIPIENTS = API_SUFFIX + "/direct_v2/ranked_recipients"; + + public const string GET_LIST_COLLECTIONS = API_SUFFIX + "/collections/list/"; + public const string GET_COLLECTION = API_SUFFIX + "/feed/collection/{0}/"; + public const string CREATE_COLLECTION = API_SUFFIX + "/collections/create/"; + public const string EDIT_COLLECTION = API_SUFFIX + "/collections/{0}/edit/"; + public const string DELETE_COLLECTION = API_SUFFIX + "/collections/{0}/delete/"; + public const string COLLECTION_CREATE_MODULE = API_SUFFIX + "collection_create"; + public const string FEED_SAVED_ADD_TO_COLLECTION_MODULE = "feed_saved_add_to_collection"; + + public const string GET_MEDIAID = API_SUFFIX + "/oembed/?url={0}"; + public const string GET_SHARE_LINK = API_SUFFIX + "/media/{0}/permalink/"; + public const string GET_RECENT_RECIPIENTS = API_SUFFIX + "/direct_share/recent_recipients/"; public const string GET_DIRECT_THREAD = API_SUFFIX + "/direct_v2/threads/{0}"; public const string GET_DIRECT_INBOX = API_SUFFIX + "/direct_v2/inbox/"; @@ -70,6 +90,8 @@ internal static class InstaApiConstants public const string MEDIA_LIKERS = API_SUFFIX + "/media/{0}/likers/"; public const string FOLLOW_USER = API_SUFFIX + "/friendships/create/{0}/"; public const string UNFOLLOW_USER = API_SUFFIX + "/friendships/destroy/{0}/"; + public const string BLOCK_USER = API_SUFFIX + "/friendships/block/{0}/"; + public const string UNBLOCK_USER = API_SUFFIX + "/friendships/unblock/{0}/"; public const string SET_ACCOUNT_PRIVATE = API_SUFFIX + "/accounts/set_private/"; public const string SET_ACCOUNT_PUBLIC = API_SUFFIX + "/accounts/set_public/"; public const string POST_COMMENT = API_SUFFIX + "/media/{0}/comment/"; @@ -78,6 +100,7 @@ internal static class InstaApiConstants public const string DELETE_COMMENT = API_SUFFIX + "/media/{0}/comment/{1}/delete/"; public const string UPLOAD_PHOTO = API_SUFFIX + "/upload/photo/"; public const string MEDIA_CONFIGURE = API_SUFFIX + "/media/configure/"; + public const string MEDIA_ALBUM_CONFIGURE = API_SUFFIX + "/media/configure_sidecar/"; public const string DELETE_MEDIA = API_SUFFIX + "/media/{0}/delete/?media_type={1}"; public const string EDIT_MEDIA = API_SUFFIX + "/media/{0}/edit_media/"; public const string GET_STORY_TRAY = API_SUFFIX + "/feed/reels_tray/"; @@ -88,5 +111,6 @@ internal static class InstaApiConstants public const string HEADER_USER_AGENT = "User-Agent"; public const string LIKE_FEED = API_SUFFIX + "/feed/liked/"; public const string USER_REEL_FEED = API_SUFFIX + "/feed/user/{0}/reel_media/"; + public static readonly Uri BaseInstagramUri = new Uri(BASE_INSTAGRAM_API_URL); } } \ No newline at end of file diff --git a/InstaSharper/API/Processors/CollectionProcessor.cs b/InstaSharper/API/Processors/CollectionProcessor.cs new file mode 100644 index 00000000..1a79a32a --- /dev/null +++ b/InstaSharper/API/Processors/CollectionProcessor.cs @@ -0,0 +1,183 @@ +using System; +using System.Net; +using System.Net.Http; +using System.Threading.Tasks; +using InstaSharper.Classes; +using InstaSharper.Classes.Android.DeviceInfo; +using InstaSharper.Classes.Models; +using InstaSharper.Classes.ResponseWrappers; +using InstaSharper.Converters; +using InstaSharper.Converters.Json; +using InstaSharper.Helpers; +using InstaSharper.Logger; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace InstaSharper.API.Processors +{ + public class CollectionProcessor : ICollectionProcessor + { + private readonly AndroidDevice _deviceInfo; + private readonly IHttpRequestProcessor _httpRequestProcessor; + private readonly IInstaLogger _logger; + private readonly UserSessionData _user; + + public CollectionProcessor(AndroidDevice deviceInfo, UserSessionData user, + IHttpRequestProcessor httpRequestProcessor, IInstaLogger logger) + { + _deviceInfo = deviceInfo; + _user = user; + _httpRequestProcessor = httpRequestProcessor; + _logger = logger; + } + + public async Task> GetCollectionAsync(long collectionId) + { + try + { + var collectionUri = UriCreator.GetCollectionUri(collectionId); + var request = HttpHelper.GetDefaultRequest(HttpMethod.Get, collectionUri, _deviceInfo); + var response = await _httpRequestProcessor.SendAsync(request); + var json = await response.Content.ReadAsStringAsync(); + if (response.StatusCode != HttpStatusCode.OK) + return Result.UnExpectedResponse(response, json); + + var collectionsListResponse = + JsonConvert.DeserializeObject(json, + new InstaCollectionDataConverter()); + var converter = ConvertersFabric.Instance.GetCollectionConverter(collectionsListResponse); + return Result.Success(converter.Convert()); + } + catch (Exception exception) + { + _logger?.LogException(exception); + return Result.Fail(exception.Message); + } + } + + public async Task> GetCollectionsAsync() + { + try + { + var collectionUri = UriCreator.GetCollectionsUri(); + var request = HttpHelper.GetDefaultRequest(HttpMethod.Get, collectionUri, _deviceInfo); + var response = await _httpRequestProcessor.SendAsync(request); + var json = await response.Content.ReadAsStringAsync(); + + if (response.StatusCode != HttpStatusCode.OK) + return Result.UnExpectedResponse(response, json); + + var collectionsResponse = JsonConvert.DeserializeObject(json); + var converter = ConvertersFabric.Instance.GetCollectionsConverter(collectionsResponse); + + return Result.Success(converter.Convert()); + } + catch (Exception exception) + { + _logger?.LogException(exception); + return Result.Fail(exception.Message); + } + } + + public async Task> CreateCollectionAsync(string collectionName) + { + try + { + var createCollectionUri = UriCreator.GetCreateCollectionUri(); + + var data = new JObject + { + {"_uuid", _deviceInfo.DeviceGuid.ToString()}, + {"_uid", _user.LoggedInUder.Pk}, + {"_csrftoken", _user.CsrfToken}, + {"name", collectionName}, + {"module_name", InstaApiConstants.COLLECTION_CREATE_MODULE} + }; + + var request = + HttpHelper.GetSignedRequest(HttpMethod.Get, createCollectionUri, _deviceInfo, data); + var response = await _httpRequestProcessor.SendAsync(request); + var json = await response.Content.ReadAsStringAsync(); + + var newCollectionResponse = JsonConvert.DeserializeObject(json); + var converter = ConvertersFabric.Instance.GetCollectionConverter(newCollectionResponse); + + return response.StatusCode != HttpStatusCode.OK + ? Result.UnExpectedResponse(response, json) + : Result.Success(converter.Convert()); + } + catch (Exception exception) + { + _logger?.LogException(exception); + return Result.Fail(exception.Message); + } + } + + public async Task> DeleteCollectionAsync(long collectionId) + { + try + { + var createCollectionUri = UriCreator.GetDeleteCollectionUri(collectionId); + + var data = new JObject + { + {"_uuid", _deviceInfo.DeviceGuid.ToString()}, + {"_uid", _user.LoggedInUder.Pk}, + {"_csrftoken", _user.CsrfToken}, + {"module_name", "collection_editor"} + }; + + var request = + HttpHelper.GetSignedRequest(HttpMethod.Get, createCollectionUri, _deviceInfo, data); + var response = await _httpRequestProcessor.SendAsync(request); + var json = await response.Content.ReadAsStringAsync(); + if (response.StatusCode == HttpStatusCode.OK) + return Result.Success(true); + + var error = JsonConvert.DeserializeObject(json); + return Result.Fail(error.Message, false); + } + catch (Exception exception) + { + _logger?.LogException(exception); + return Result.Fail(exception.Message, false); + } + } + + public async Task> AddItemsToCollectionAsync(long collectionId, + params string[] mediaIds) + { + try + { + if (mediaIds?.Length < 1) + return Result.Fail("Provide at least one media id to add to collection"); + var editCollectionUri = UriCreator.GetEditCollectionUri(collectionId); + + var data = new JObject + { + {"module_name", InstaApiConstants.FEED_SAVED_ADD_TO_COLLECTION_MODULE}, + {"added_media_ids", JsonConvert.SerializeObject(mediaIds)}, + {"radio_type", "wifi-none"}, + {"_uuid", _deviceInfo.DeviceGuid.ToString()}, + {"_uid", _user.LoggedInUder.Pk}, + {"_csrftoken", _user.CsrfToken} + }; + + var request = + HttpHelper.GetSignedRequest(HttpMethod.Get, editCollectionUri, _deviceInfo, data); + var response = await _httpRequestProcessor.SendAsync(request); + var json = await response.Content.ReadAsStringAsync(); + if (response.StatusCode != HttpStatusCode.OK) + return Result.UnExpectedResponse(response, json); + var newCollectionResponse = JsonConvert.DeserializeObject(json); + var converter = ConvertersFabric.Instance.GetCollectionConverter(newCollectionResponse); + return Result.Success(converter.Convert()); + } + catch (Exception exception) + { + _logger?.LogException(exception); + return Result.Fail(exception.Message); + } + } + } +} \ No newline at end of file diff --git a/InstaSharper/API/Processors/CommentProcessor.cs b/InstaSharper/API/Processors/CommentProcessor.cs new file mode 100644 index 00000000..e316e6d7 --- /dev/null +++ b/InstaSharper/API/Processors/CommentProcessor.cs @@ -0,0 +1,150 @@ +using System; +using System.Collections.Generic; +using System.Net; +using System.Net.Http; +using System.Threading.Tasks; +using InstaSharper.Classes; +using InstaSharper.Classes.Android.DeviceInfo; +using InstaSharper.Classes.Models; +using InstaSharper.Classes.ResponseWrappers; +using InstaSharper.Converters; +using InstaSharper.Converters.Json; +using InstaSharper.Helpers; +using InstaSharper.Logger; +using Newtonsoft.Json; + +namespace InstaSharper.API.Processors +{ + public class CommentProcessor : ICommentProcessor + { + private readonly AndroidDevice _deviceInfo; + private readonly IHttpRequestProcessor _httpRequestProcessor; + private readonly IInstaLogger _logger; + private readonly UserSessionData _user; + + public CommentProcessor(AndroidDevice deviceInfo, UserSessionData user, + IHttpRequestProcessor httpRequestProcessor, IInstaLogger logger) + { + _deviceInfo = deviceInfo; + _user = user; + _httpRequestProcessor = httpRequestProcessor; + _logger = logger; + } + + public async Task> GetMediaCommentsAsync(string mediaId, + PaginationParameters paginationParameters) + { + try + { + var commentsUri = UriCreator.GetMediaCommentsUri(mediaId, paginationParameters.NextId); + var request = HttpHelper.GetDefaultRequest(HttpMethod.Get, commentsUri, _deviceInfo); + var response = await _httpRequestProcessor.SendAsync(request); + var json = await response.Content.ReadAsStringAsync(); + if (response.StatusCode != HttpStatusCode.OK) + return Result.UnExpectedResponse(response, json); + var commentListResponse = JsonConvert.DeserializeObject(json); + var pagesLoaded = 1; + + InstaCommentList Convert(InstaCommentListResponse commentsResponse) + { + return ConvertersFabric.Instance.GetCommentListConverter(commentsResponse).Convert(); + } + + while (commentListResponse.MoreComentsAvailable + && !string.IsNullOrEmpty(commentListResponse.NextMaxId) + && pagesLoaded < paginationParameters.MaximumPagesToLoad) + { + var nextComments = await GetCommentListWithMaxIdAsync(mediaId, commentListResponse.NextMaxId); + if (!nextComments.Succeeded) + Result.Success($"Not all pages was downloaded: {nextComments.Info.Message}", + Convert(commentListResponse)); + commentListResponse.NextMaxId = nextComments.Value.NextMaxId; + commentListResponse.MoreComentsAvailable = nextComments.Value.MoreComentsAvailable; + commentListResponse.Comments.AddRange(nextComments.Value.Comments); + pagesLoaded++; + } + var converter = ConvertersFabric.Instance.GetCommentListConverter(commentListResponse); + return Result.Success(converter.Convert()); + } + catch (Exception exception) + { + _logger?.LogException(exception); + return Result.Fail(exception); + } + } + + public async Task> CommentMediaAsync(string mediaId, string text) + { + try + { + var instaUri = UriCreator.GetPostCommetUri(mediaId); + var breadcrumb = CryptoHelper.GetCommentBreadCrumbEncoded(text); + var fields = new Dictionary + { + {"user_breadcrumb", breadcrumb}, + {"idempotence_token", Guid.NewGuid().ToString()}, + {"_uuid", _deviceInfo.DeviceGuid.ToString()}, + {"_uid", _user.LoggedInUder.Pk.ToString()}, + {"_csrftoken", _user.CsrfToken}, + {"comment_text", text}, + {"containermodule", "comments_feed_timeline"}, + {"radio_type", "wifi-none"} + }; + var request = + HttpHelper.GetSignedRequest(HttpMethod.Post, instaUri, _deviceInfo, fields); + var response = await _httpRequestProcessor.SendAsync(request); + var json = await response.Content.ReadAsStringAsync(); + if (response.StatusCode != HttpStatusCode.OK) + return Result.UnExpectedResponse(response, json); + var commentResponse = JsonConvert.DeserializeObject(json, + new InstaCommentDataConverter()); + var converter = ConvertersFabric.Instance.GetCommentConverter(commentResponse); + return Result.Success(converter.Convert()); + } + catch (Exception exception) + { + _logger?.LogException(exception); + return Result.Fail(exception.Message, (InstaComment) null); + } + } + + public async Task> DeleteCommentAsync(string mediaId, string commentId) + { + try + { + var instaUri = UriCreator.GetDeleteCommetUri(mediaId, commentId); + var fields = new Dictionary + { + {"_uuid", _deviceInfo.DeviceGuid.ToString()}, + {"_uid", _user.LoggedInUder.Pk.ToString()}, + {"_csrftoken", _user.CsrfToken} + }; + var request = + HttpHelper.GetSignedRequest(HttpMethod.Post, instaUri, _deviceInfo, fields); + var response = await _httpRequestProcessor.SendAsync(request); + var json = await response.Content.ReadAsStringAsync(); + return response.StatusCode == HttpStatusCode.OK + ? Result.Success(true) + : Result.UnExpectedResponse(response, json); + } + catch (Exception exception) + { + _logger?.LogException(exception); + return Result.Fail(exception.Message, false); + } + } + + private async Task> GetCommentListWithMaxIdAsync(string mediaId, + string nextId) + { + var commentsUri = UriCreator.GetMediaCommentsUri(mediaId, nextId); + var request = HttpHelper.GetDefaultRequest(HttpMethod.Get, commentsUri, _deviceInfo); + var response = await _httpRequestProcessor.SendAsync(request); + var json = await response.Content.ReadAsStringAsync(); + if (response.StatusCode != HttpStatusCode.OK) + return Result.Fail("Unable to get next portion of comments", (InstaCommentListResponse) null); + var comments = JsonConvert.DeserializeObject(json); + return Result.Success(comments); + } + } +} \ No newline at end of file diff --git a/InstaSharper/API/Processors/FeedProcessor.cs b/InstaSharper/API/Processors/FeedProcessor.cs new file mode 100644 index 00000000..78d50f26 --- /dev/null +++ b/InstaSharper/API/Processors/FeedProcessor.cs @@ -0,0 +1,247 @@ +using System; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Threading.Tasks; +using InstaSharper.Classes; +using InstaSharper.Classes.Android.DeviceInfo; +using InstaSharper.Classes.Models; +using InstaSharper.Classes.ResponseWrappers; +using InstaSharper.Converters; +using InstaSharper.Converters.Json; +using InstaSharper.Helpers; +using InstaSharper.Logger; +using Newtonsoft.Json; +using InstaRecentActivityConverter = InstaSharper.Converters.Json.InstaRecentActivityConverter; + +namespace InstaSharper.API.Processors +{ + public class FeedProcessor : IFeedProcessor + { + private readonly AndroidDevice _deviceInfo; + private readonly IHttpRequestProcessor _httpRequestProcessor; + private readonly IInstaLogger _logger; + private readonly UserSessionData _user; + + public FeedProcessor(AndroidDevice deviceInfo, UserSessionData user, IHttpRequestProcessor httpRequestProcessor, + IInstaLogger logger) + { + _deviceInfo = deviceInfo; + _user = user; + _httpRequestProcessor = httpRequestProcessor; + _logger = logger; + } + + public async Task> GetTagFeedAsync(string tag, PaginationParameters paginationParameters) + { + var tagFeed = new InstaTagFeed(); + try + { + var userFeedUri = UriCreator.GetTagFeedUri(tag, paginationParameters.NextId); + var request = HttpHelper.GetDefaultRequest(HttpMethod.Get, userFeedUri, _deviceInfo); + var response = await _httpRequestProcessor.SendAsync(request); + var json = await response.Content.ReadAsStringAsync(); + if (response.StatusCode != HttpStatusCode.OK) + return Result.UnExpectedResponse(response, json); + var feedResponse = JsonConvert.DeserializeObject(json, + new InstaTagFeedDataConverter()); + tagFeed = ConvertersFabric.Instance.GetTagFeedConverter(feedResponse).Convert(); + + paginationParameters.NextId = feedResponse.NextMaxId; + paginationParameters.PagesLoaded++; + + while (feedResponse.MoreAvailable + && !string.IsNullOrEmpty(paginationParameters.NextId) + && paginationParameters.PagesLoaded < paginationParameters.MaximumPagesToLoad) + { + var nextFeed = await GetTagFeedAsync(tag, paginationParameters); + if (!nextFeed.Succeeded) + return Result.Fail(nextFeed.Info, tagFeed); + tagFeed.NextId = paginationParameters.NextId = nextFeed.Value.NextId; + tagFeed.Medias.AddRange(nextFeed.Value.Medias); + tagFeed.Stories.AddRange(nextFeed.Value.Stories); + } + return Result.Success(tagFeed); + } + catch (Exception exception) + { + _logger?.LogException(exception); + return Result.Fail(exception, tagFeed); + } + } + + public async Task> GetUserTimelineFeedAsync(PaginationParameters paginationParameters) + { + var feed = new InstaFeed(); + try + { + var userFeedUri = UriCreator.GetUserFeedUri(paginationParameters.NextId); + var request = HttpHelper.GetDefaultRequest(HttpMethod.Get, userFeedUri, _deviceInfo); + var response = await _httpRequestProcessor.SendAsync(request); + var json = await response.Content.ReadAsStringAsync(); + if (response.StatusCode != HttpStatusCode.OK) + return Result.UnExpectedResponse(response, json); + + var feedResponse = JsonConvert.DeserializeObject(json, + new InstaFeedResponseDataConverter()); + feed = ConvertersFabric.Instance.GetFeedConverter(feedResponse).Convert(); + paginationParameters.NextId = feed.NextId; + paginationParameters.PagesLoaded++; + + while (feedResponse.MoreAvailable + && !string.IsNullOrEmpty(paginationParameters.NextId) + && paginationParameters.PagesLoaded < paginationParameters.MaximumPagesToLoad) + { + var nextFeed = await GetUserTimelineFeedAsync(paginationParameters); + if (!nextFeed.Succeeded) + return Result.Fail(nextFeed.Info, feed); + + feed.Medias.AddRange(nextFeed.Value.Medias); + feed.Stories.AddRange(nextFeed.Value.Stories); + + paginationParameters.NextId = nextFeed.Value.NextId; + paginationParameters.PagesLoaded++; + } + return Result.Success(feed); + } + catch (Exception exception) + { + _logger?.LogException(exception); + return Result.Fail(exception, feed); + } + } + + public async Task> GetExploreFeedAsync(PaginationParameters paginationParameters) + { + var exploreFeed = new InstaExploreFeed(); + try + { + var exploreUri = UriCreator.GetExploreUri(paginationParameters.NextId); + var request = HttpHelper.GetDefaultRequest(HttpMethod.Get, exploreUri, _deviceInfo); + var response = await _httpRequestProcessor.SendAsync(request); + var json = await response.Content.ReadAsStringAsync(); + if (response.StatusCode != HttpStatusCode.OK) + return Result.UnExpectedResponse(response, json); + var feedResponse = JsonConvert.DeserializeObject(json, + new InstaExploreFeedDataConverter()); + exploreFeed = ConvertersFabric.Instance.GetExploreFeedConverter(feedResponse).Convert(); + var nextId = feedResponse.Items.Medias.LastOrDefault(media => !string.IsNullOrEmpty(media.NextMaxId)) + ?.NextMaxId; + exploreFeed.Medias.PageSize = feedResponse.ResultsCount; + paginationParameters.NextId = nextId; + exploreFeed.NextId = nextId; + while (feedResponse.MoreAvailable + && !string.IsNullOrEmpty(paginationParameters.NextId) + && paginationParameters.PagesLoaded < paginationParameters.MaximumPagesToLoad) + { + var nextFeed = await GetExploreFeedAsync(paginationParameters); + if (!nextFeed.Succeeded) + return Result.Fail(nextFeed.Info, exploreFeed); + nextId = feedResponse.Items.Medias.LastOrDefault(media => !string.IsNullOrEmpty(media.NextMaxId)) + ?.NextMaxId; + exploreFeed.NextId = paginationParameters.NextId = nextId; + paginationParameters.PagesLoaded++; + exploreFeed.Medias.AddRange(nextFeed.Value.Medias); + } + exploreFeed.Medias.Pages = paginationParameters.PagesLoaded; + return Result.Success(exploreFeed); + } + catch (Exception exception) + { + _logger?.LogException(exception); + return Result.Fail(exception, exploreFeed); + } + } + + public async Task> GetFollowingRecentActivityFeedAsync( + PaginationParameters paginationParameters) + { + var uri = UriCreator.GetFollowingRecentActivityUri(); + return await GetRecentActivityInternalAsync(uri, paginationParameters); + } + + public async Task> GetRecentActivityFeedAsync( + PaginationParameters paginationParameters) + { + var uri = UriCreator.GetRecentActivityUri(); + return await GetRecentActivityInternalAsync(uri, paginationParameters); + } + + public async Task> GetLikeFeedAsync(PaginationParameters paginationParameters) + { + var instaUri = UriCreator.GetUserLikeFeedUri(paginationParameters.NextId); + var request = HttpHelper.GetDefaultRequest(HttpMethod.Get, instaUri, _deviceInfo); + var response = await _httpRequestProcessor.SendAsync(request); + var json = await response.Content.ReadAsStringAsync(); + if (response.StatusCode != HttpStatusCode.OK) + return Result.UnExpectedResponse(response, json); + var mediaResponse = JsonConvert.DeserializeObject(json, + new InstaMediaListDataConverter()); + + var mediaList = ConvertersFabric.Instance.GetMediaListConverter(mediaResponse).Convert(); + mediaList.NextId = paginationParameters.NextId = mediaResponse.NextMaxId; + while (mediaResponse.MoreAvailable + && !string.IsNullOrEmpty(paginationParameters.NextId) + && paginationParameters.PagesLoaded < paginationParameters.MaximumPagesToLoad) + { + var result = await GetLikeFeedAsync(paginationParameters); + if (!result.Succeeded) + return Result.Fail(result.Info, mediaList); + + paginationParameters.PagesLoaded++; + mediaList.NextId = paginationParameters.NextId = result.Value.NextId; + mediaList.AddRange(result.Value); + } + mediaList.PageSize = mediaResponse.ResultsCount; + mediaList.Pages = paginationParameters.PagesLoaded; + return Result.Success(mediaList); + } + + private async Task> GetFollowingActivityWithMaxIdAsync(string maxId) + { + var uri = UriCreator.GetFollowingRecentActivityUri(maxId); + var request = HttpHelper.GetDefaultRequest(HttpMethod.Get, uri, _deviceInfo); + var response = await _httpRequestProcessor.SendAsync(request); + var json = await response.Content.ReadAsStringAsync(); + if (response.StatusCode != HttpStatusCode.OK) + return Result.UnExpectedResponse(response, json); + var followingActivity = JsonConvert.DeserializeObject(json, + new InstaRecentActivityConverter()); + return Result.Success(followingActivity); + } + + private async Task> GetRecentActivityInternalAsync(Uri uri, + PaginationParameters paginationParameters) + { + var request = HttpHelper.GetDefaultRequest(HttpMethod.Get, uri, _deviceInfo); + var response = await _httpRequestProcessor.SendAsync(request, HttpCompletionOption.ResponseContentRead); + var activityFeed = new InstaActivityFeed(); + var json = await response.Content.ReadAsStringAsync(); + if (response.StatusCode != HttpStatusCode.OK) + return Result.UnExpectedResponse(response, json); + var feedPage = JsonConvert.DeserializeObject(json, + new InstaRecentActivityConverter()); + activityFeed.IsOwnActivity = feedPage.IsOwnActivity; + var nextId = feedPage.NextMaxId; + activityFeed.Items.AddRange( + feedPage.Stories.Select(ConvertersFabric.Instance.GetSingleRecentActivityConverter) + .Select(converter => converter.Convert())); + paginationParameters.PagesLoaded++; + activityFeed.NextId = paginationParameters.NextId = feedPage.NextMaxId; + while (!string.IsNullOrEmpty(nextId) + && paginationParameters.PagesLoaded < paginationParameters.MaximumPagesToLoad) + { + var nextFollowingFeed = await GetFollowingActivityWithMaxIdAsync(nextId); + if (!nextFollowingFeed.Succeeded) + return Result.Fail(nextFollowingFeed.Info, activityFeed); + nextId = nextFollowingFeed.Value.NextMaxId; + activityFeed.Items.AddRange( + feedPage.Stories.Select(ConvertersFabric.Instance.GetSingleRecentActivityConverter) + .Select(converter => converter.Convert())); + paginationParameters.PagesLoaded++; + activityFeed.NextId = paginationParameters.NextId = nextId; + } + return Result.Success(activityFeed); + } + } +} \ No newline at end of file diff --git a/InstaSharper/API/Processors/ICollectionProcessor.cs b/InstaSharper/API/Processors/ICollectionProcessor.cs new file mode 100644 index 00000000..0098fe15 --- /dev/null +++ b/InstaSharper/API/Processors/ICollectionProcessor.cs @@ -0,0 +1,19 @@ +using System.Threading.Tasks; +using InstaSharper.Classes; +using InstaSharper.Classes.Models; + +namespace InstaSharper.API.Processors +{ + public interface ICollectionProcessor + { + Task> GetCollectionAsync(long collectionId); + + Task> GetCollectionsAsync(); + + Task> CreateCollectionAsync(string collectionName); + + Task> DeleteCollectionAsync(long collectionId); + + Task> AddItemsToCollectionAsync(long collectionId, params string[] mediaIds); + } +} \ No newline at end of file diff --git a/InstaSharper/API/Processors/ICommentProcessor.cs b/InstaSharper/API/Processors/ICommentProcessor.cs new file mode 100644 index 00000000..bee85b05 --- /dev/null +++ b/InstaSharper/API/Processors/ICommentProcessor.cs @@ -0,0 +1,15 @@ +using System.Threading.Tasks; +using InstaSharper.Classes; +using InstaSharper.Classes.Models; + +namespace InstaSharper.API.Processors +{ + public interface ICommentProcessor + { + Task> + GetMediaCommentsAsync(string mediaId, PaginationParameters paginationParameters); + + Task> CommentMediaAsync(string mediaId, string text); + Task> DeleteCommentAsync(string mediaId, string commentId); + } +} \ No newline at end of file diff --git a/InstaSharper/API/Processors/IFeedProcessor.cs b/InstaSharper/API/Processors/IFeedProcessor.cs new file mode 100644 index 00000000..6bbd780a --- /dev/null +++ b/InstaSharper/API/Processors/IFeedProcessor.cs @@ -0,0 +1,16 @@ +using System.Threading.Tasks; +using InstaSharper.Classes; +using InstaSharper.Classes.Models; + +namespace InstaSharper.API.Processors +{ + public interface IFeedProcessor + { + Task> GetTagFeedAsync(string tag, PaginationParameters paginationParameters); + Task> GetUserTimelineFeedAsync(PaginationParameters paginationParameters); + Task> GetExploreFeedAsync(PaginationParameters paginationParameters); + Task> GetFollowingRecentActivityFeedAsync(PaginationParameters paginationParameters); + Task> GetRecentActivityFeedAsync(PaginationParameters paginationParameters); + Task> GetLikeFeedAsync(PaginationParameters paginationParameters); + } +} \ No newline at end of file diff --git a/InstaSharper/API/Processors/ILocationProcessor.cs b/InstaSharper/API/Processors/ILocationProcessor.cs new file mode 100644 index 00000000..36bc713e --- /dev/null +++ b/InstaSharper/API/Processors/ILocationProcessor.cs @@ -0,0 +1,13 @@ +using System.Threading.Tasks; +using InstaSharper.Classes; +using InstaSharper.Classes.Models; + +namespace InstaSharper.API.Processors +{ + public interface ILocationProcessor + { + Task> Search(double latitude, double longitude, string query); + + Task> GetFeed(long locationId, PaginationParameters paginationParameters); + } +} \ No newline at end of file diff --git a/InstaSharper/API/Processors/IMediaProcessor.cs b/InstaSharper/API/Processors/IMediaProcessor.cs new file mode 100644 index 00000000..068cd88e --- /dev/null +++ b/InstaSharper/API/Processors/IMediaProcessor.cs @@ -0,0 +1,34 @@ +using System; +using System.Threading.Tasks; +using InstaSharper.Classes; +using InstaSharper.Classes.Models; + +namespace InstaSharper.API.Processors +{ + public interface IMediaProcessor + { + Task> GetMediaIdFromUrlAsync(Uri uri); + + Task> DeleteMediaAsync(string mediaId, InstaMediaType mediaType); + + Task> EditMediaAsync(string mediaId, string caption); + + Task> UploadPhotoAsync(InstaImage image, string caption); + + Task> UploadPhotosAlbumAsync(InstaImage[] images, string caption); + + Task> ConfigurePhotoAsync(InstaImage image, string uploadId, string caption); + + Task> ConfigureAlbumAsync(string[] uploadId, string caption); + + Task> GetMediaLikersAsync(string mediaId); + + Task> LikeMediaAsync(string mediaId); + + Task> UnLikeMediaAsync(string mediaId); + + Task> GetMediaByIdAsync(string mediaId); + + Task> GetShareLinkFromMediaIdAsync(string mediaId); + } +} \ No newline at end of file diff --git a/InstaSharper/API/Processors/IMessagingProcessor.cs b/InstaSharper/API/Processors/IMessagingProcessor.cs new file mode 100644 index 00000000..9940ef4d --- /dev/null +++ b/InstaSharper/API/Processors/IMessagingProcessor.cs @@ -0,0 +1,18 @@ +using System.Threading.Tasks; +using InstaSharper.Classes; +using InstaSharper.Classes.Models; + +namespace InstaSharper.API.Processors +{ + public interface IMessagingProcessor + { + Task> GetDirectInboxAsync(); + Task> GetDirectInboxThreadAsync(string threadId); + + Task> SendDirectMessage(string recipients, string threadIds, + string text); + + Task> GetRecentRecipientsAsync(); + Task> GetRankedRecipientsAsync(); + } +} \ No newline at end of file diff --git a/InstaSharper/API/Processors/IStoryProcessor.cs b/InstaSharper/API/Processors/IStoryProcessor.cs new file mode 100644 index 00000000..cdb70156 --- /dev/null +++ b/InstaSharper/API/Processors/IStoryProcessor.cs @@ -0,0 +1,15 @@ +using System.Threading.Tasks; +using InstaSharper.Classes; +using InstaSharper.Classes.Models; + +namespace InstaSharper.API.Processors +{ + public interface IStoryProcessor + { + Task> GetStoryFeedAsync(); + Task> GetUserStoryAsync(long userId); + Task> UploadStoryPhotoAsync(InstaImage image, string caption); + Task> ConfigureStoryPhotoAsync(InstaImage image, string uploadId, string caption); + Task> GetUserStoryFeedAsync(long userId); + } +} \ No newline at end of file diff --git a/InstaSharper/API/Processors/IUserProcessor.cs b/InstaSharper/API/Processors/IUserProcessor.cs new file mode 100644 index 00000000..55d2717d --- /dev/null +++ b/InstaSharper/API/Processors/IUserProcessor.cs @@ -0,0 +1,27 @@ +using System.Threading.Tasks; +using InstaSharper.Classes; +using InstaSharper.Classes.Models; + +namespace InstaSharper.API.Processors +{ + public interface IUserProcessor + { + Task> GetUserMediaAsync(long username, PaginationParameters paginationParameters); + Task> GetUserAsync(string username); + Task> GetCurrentUserAsync(); + + Task> GetUserFollowersAsync(string username, + PaginationParameters paginationParameters); + + Task> GetUserFollowingAsync(string username, + PaginationParameters paginationParameters); + + Task> GetCurrentUserFollowersAsync(PaginationParameters paginationParameters); + Task> GetUserTagsAsync(long username, PaginationParameters paginationParameters); + Task> FollowUserAsync(long userId); + Task> UnFollowUserAsync(long userId); + Task> BlockUserAsync(long userId); + Task> UnBlockUserAsync(long userId); + Task> GetFriendshipStatusAsync(long userId); + } +} \ No newline at end of file diff --git a/InstaSharper/API/Processors/IUserProfileProcessor.cs b/InstaSharper/API/Processors/IUserProfileProcessor.cs new file mode 100644 index 00000000..e703f214 --- /dev/null +++ b/InstaSharper/API/Processors/IUserProfileProcessor.cs @@ -0,0 +1,15 @@ +using System.Threading.Tasks; +using InstaSharper.Classes; +using InstaSharper.Classes.Models; + +namespace InstaSharper.API.Processors +{ + public interface IUserProfileProcessor + { + Task> SetAccountPrivateAsync(); + + Task> SetAccountPublicAsync(); + + Task> ChangePasswordAsync(string oldPassword, string newPassword); + } +} \ No newline at end of file diff --git a/InstaSharper/API/Processors/LocationProcessor.cs b/InstaSharper/API/Processors/LocationProcessor.cs new file mode 100644 index 00000000..847cfba0 --- /dev/null +++ b/InstaSharper/API/Processors/LocationProcessor.cs @@ -0,0 +1,115 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Net; +using System.Net.Http; +using System.Threading.Tasks; +using InstaSharper.API.UriCreators; +using InstaSharper.Classes; +using InstaSharper.Classes.Android.DeviceInfo; +using InstaSharper.Classes.Models; +using InstaSharper.Classes.ResponseWrappers; +using InstaSharper.Converters; +using InstaSharper.Helpers; +using InstaSharper.Logger; +using Newtonsoft.Json; + +namespace InstaSharper.API.Processors +{ + internal class LocationProcessor : ILocationProcessor + { + private readonly AndroidDevice _deviceInfo; + private readonly IUriCreatorNextId _getFeedUriCreator = new GetLocationFeedUriCreator(); + private readonly IHttpRequestProcessor _httpRequestProcessor; + private readonly IInstaLogger _logger; + private readonly IUriCreator _searchLocationUriCreator = new SearchLocationUriCreator(); + private readonly UserSessionData _user; + + public LocationProcessor(AndroidDevice deviceInfo, UserSessionData user, + IHttpRequestProcessor httpRequestProcessor, IInstaLogger logger) + { + _deviceInfo = deviceInfo; + _user = user; + _httpRequestProcessor = httpRequestProcessor; + _logger = logger; + } + + public async Task> Search(double latitude, double longitude, string query) + { + try + { + var uri = _searchLocationUriCreator.GetUri(); + + var fields = new Dictionary + { + {"_uuid", _deviceInfo.DeviceGuid.ToString()}, + {"_uid", _user.LoggedInUder.Pk.ToString()}, + {"_csrftoken", _user.CsrfToken}, + {"latitude", latitude.ToString(CultureInfo.InvariantCulture)}, + {"longitude", longitude.ToString(CultureInfo.InvariantCulture)}, + {"rank_token", _user.RankToken} + }; + + if (!string.IsNullOrEmpty(query)) + fields.Add("search_query", query); + else + fields.Add("timestamp", DateTimeHelper.GetUnixTimestampSeconds().ToString()); + if (!Uri.TryCreate(uri, fields.AsQueryString(), out var newuri)) + return Result.Fail("Unable to create uri for location search"); + + var request = HttpHelper.GetDefaultRequest(HttpMethod.Get, newuri, _deviceInfo); + var response = await _httpRequestProcessor.SendAsync(request); + var json = await response.Content.ReadAsStringAsync(); + if (response.StatusCode != HttpStatusCode.OK) + return Result.UnExpectedResponse(response, json); + var locations = JsonConvert.DeserializeObject(json); + var converter = ConvertersFabric.Instance.GetLocationsSearchConverter(locations); + return Result.Success(converter.Convert()); + } + catch (Exception exception) + { + _logger?.LogException(exception); + return Result.Fail(exception); + } + } + + public async Task> GetFeed(long locationId, + PaginationParameters paginationParameters) + { + try + { + var uri = _getFeedUriCreator.GetUri(locationId, paginationParameters.NextId); + var request = HttpHelper.GetDefaultRequest(HttpMethod.Get, uri, _deviceInfo); + var response = await _httpRequestProcessor.SendAsync(request); + var json = await response.Content.ReadAsStringAsync(); + if (response.StatusCode != HttpStatusCode.OK) + return Result.UnExpectedResponse(response, json); + + var feedResponse = JsonConvert.DeserializeObject(json); + var feed = ConvertersFabric.Instance.GetLocationFeedConverter(feedResponse).Convert(); + paginationParameters.PagesLoaded++; + paginationParameters.NextId = feed.NextId; + + while (feedResponse.MoreAvailable + && !string.IsNullOrEmpty(paginationParameters.NextId) + && paginationParameters.PagesLoaded < paginationParameters.MaximumPagesToLoad) + { + var nextFeed = await GetFeed(locationId, paginationParameters); + if (!nextFeed.Succeeded) + return nextFeed; + paginationParameters.StartFromId(nextFeed.Value.NextId); + paginationParameters.PagesLoaded++; + feed.NextId = nextFeed.Value.NextId; + feed.Medias.AddRange(nextFeed.Value.Medias); + feed.RankedMedias.AddRange(nextFeed.Value.RankedMedias); + } + return Result.Success(feed); + } + catch (Exception exception) + { + _logger?.LogException(exception); + return Result.Fail(exception); + } + } + } +} \ No newline at end of file diff --git a/InstaSharper/API/Processors/MediaProcessor.cs b/InstaSharper/API/Processors/MediaProcessor.cs new file mode 100644 index 00000000..d6057155 --- /dev/null +++ b/InstaSharper/API/Processors/MediaProcessor.cs @@ -0,0 +1,431 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Threading.Tasks; +using InstaSharper.Classes; +using InstaSharper.Classes.Android.DeviceInfo; +using InstaSharper.Classes.Models; +using InstaSharper.Classes.ResponseWrappers; +using InstaSharper.Converters; +using InstaSharper.Converters.Json; +using InstaSharper.Helpers; +using InstaSharper.Logger; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace InstaSharper.API.Processors +{ + internal class MediaProcessor : IMediaProcessor + { + private readonly AndroidDevice _deviceInfo; + private readonly IHttpRequestProcessor _httpRequestProcessor; + private readonly IInstaLogger _logger; + private readonly UserSessionData _user; + + public MediaProcessor(AndroidDevice deviceInfo, UserSessionData user, + IHttpRequestProcessor httpRequestProcessor, IInstaLogger logger) + { + _deviceInfo = deviceInfo; + _user = user; + _httpRequestProcessor = httpRequestProcessor; + _logger = logger; + } + + public async Task> GetMediaIdFromUrlAsync(Uri uri) + { + try + { + var collectionUri = UriCreator.GetMediaIdFromUrlUri(uri); + var request = HttpHelper.GetDefaultRequest(HttpMethod.Get, collectionUri, _deviceInfo); + var response = await _httpRequestProcessor.SendAsync(request); + var json = await response.Content.ReadAsStringAsync(); + + if (response.StatusCode != HttpStatusCode.OK) + return Result.UnExpectedResponse(response, json); + + var data = JsonConvert.DeserializeObject(json); + return Result.Success(data.MediaId); + } + catch (Exception exception) + { + _logger?.LogException(exception); + return Result.Fail(exception); + } + } + + public async Task> DeleteMediaAsync(string mediaId, InstaMediaType mediaType) + { + try + { + var deleteMediaUri = UriCreator.GetDeleteMediaUri(mediaId, mediaType); + + var data = new JObject + { + {"_uuid", _deviceInfo.DeviceGuid.ToString()}, + {"_uid", _user.LoggedInUder.Pk}, + {"_csrftoken", _user.CsrfToken}, + {"media_id", mediaId} + }; + + var request = + HttpHelper.GetSignedRequest(HttpMethod.Get, deleteMediaUri, _deviceInfo, data); + var response = await _httpRequestProcessor.SendAsync(request); + var json = await response.Content.ReadAsStringAsync(); + + if (response.StatusCode != HttpStatusCode.OK) + return Result.UnExpectedResponse(response, json); + + var deletedResponse = JsonConvert.DeserializeObject(json); + return Result.Success(deletedResponse.IsDeleted); + } + catch (Exception exception) + { + _logger?.LogException(exception); + return Result.Fail(exception); + } + } + + public async Task> EditMediaAsync(string mediaId, string caption) + { + try + { + var editMediaUri = UriCreator.GetEditMediaUri(mediaId); + + var data = new JObject + { + {"_uuid", _deviceInfo.DeviceGuid.ToString()}, + {"_uid", _user.LoggedInUder.Pk}, + {"_csrftoken", _user.CsrfToken}, + {"caption_text", caption} + }; + + var request = HttpHelper.GetSignedRequest(HttpMethod.Get, editMediaUri, _deviceInfo, data); + var response = await _httpRequestProcessor.SendAsync(request); + var json = await response.Content.ReadAsStringAsync(); + if (response.StatusCode == HttpStatusCode.OK) + return Result.Success(true); + var error = JsonConvert.DeserializeObject(json); + return Result.Fail(error.Message, false); + } + catch (Exception exception) + { + _logger?.LogException(exception); + return Result.Fail(exception); + } + } + + public async Task> UploadPhotoAsync(InstaImage image, string caption) + { + try + { + var instaUri = UriCreator.GetUploadPhotoUri(); + var uploadId = ApiRequestMessage.GenerateUploadId(); + var requestContent = new MultipartFormDataContent(uploadId) + { + {new StringContent(uploadId), "\"upload_id\""}, + {new StringContent(_deviceInfo.DeviceGuid.ToString()), "\"_uuid\""}, + {new StringContent(_user.CsrfToken), "\"_csrftoken\""}, + { + new StringContent("{\"lib_name\":\"jt\",\"lib_version\":\"1.3.0\",\"quality\":\"87\"}"), + "\"image_compression\"" + } + }; + var imageContent = new ByteArrayContent(File.ReadAllBytes(image.URI)); + imageContent.Headers.Add("Content-Transfer-Encoding", "binary"); + imageContent.Headers.Add("Content-Type", "application/octet-stream"); + requestContent.Add(imageContent, "photo", $"pending_media_{ApiRequestMessage.GenerateUploadId()}.jpg"); + var request = HttpHelper.GetDefaultRequest(HttpMethod.Post, instaUri, _deviceInfo); + request.Content = requestContent; + var response = await _httpRequestProcessor.SendAsync(request); + var json = await response.Content.ReadAsStringAsync(); + if (response.IsSuccessStatusCode) + return await ConfigurePhotoAsync(image, uploadId, caption); + return Result.UnExpectedResponse(response, json); + } + catch (Exception exception) + { + _logger?.LogException(exception); + return Result.Fail(exception); + } + } + + public async Task> UploadPhotosAlbumAsync(InstaImage[] images, string caption) + { + try + { + var uploadIds = new string[images.Length]; + var index = 0; + + foreach (var image in images) + { + var instaUri = UriCreator.GetUploadPhotoUri(); + var uploadId = ApiRequestMessage.GenerateUploadId(); + var requestContent = new MultipartFormDataContent(uploadId) + { + {new StringContent(uploadId), "\"upload_id\""}, + {new StringContent(_deviceInfo.DeviceGuid.ToString()), "\"_uuid\""}, + {new StringContent(_user.CsrfToken), "\"_csrftoken\""}, + { + new StringContent("{\"lib_name\":\"jt\",\"lib_version\":\"1.3.0\",\"quality\":\"87\"}"), + "\"image_compression\"" + }, + {new StringContent("1"), "\"is_sidecar\""} + }; + var imageContent = new ByteArrayContent(File.ReadAllBytes(image.URI)); + imageContent.Headers.Add("Content-Transfer-Encoding", "binary"); + imageContent.Headers.Add("Content-Type", "application/octet-stream"); + requestContent.Add(imageContent, "photo", + $"pending_media_{ApiRequestMessage.GenerateUploadId()}.jpg"); + var request = HttpHelper.GetDefaultRequest(HttpMethod.Post, instaUri, _deviceInfo); + request.Content = requestContent; + var response = await _httpRequestProcessor.SendAsync(request); + var json = await response.Content.ReadAsStringAsync(); + if (response.IsSuccessStatusCode) + uploadIds[index++] = uploadId; + else + return Result.UnExpectedResponse(response, json); + } + + return await ConfigureAlbumAsync(uploadIds, caption); + } + catch (Exception exception) + { + _logger?.LogException(exception); + return Result.Fail(exception); + } + } + + public async Task> ConfigurePhotoAsync(InstaImage image, string uploadId, string caption) + { + try + { + var instaUri = UriCreator.GetMediaConfigureUri(); + var androidVersion = + AndroidVersion.FromString(_deviceInfo.FirmwareFingerprint.Split('/')[2].Split(':')[1]); + if (androidVersion == null) + return Result.Fail("Unsupported android version", (InstaMedia) null); + var data = new JObject + { + {"_uuid", _deviceInfo.DeviceGuid.ToString()}, + {"_uid", _user.LoggedInUder.Pk}, + {"_csrftoken", _user.CsrfToken}, + {"media_folder", "Camera"}, + {"source_type", "4"}, + {"caption", caption}, + {"upload_id", uploadId}, + { + "device", new JObject + { + {"manufacturer", _deviceInfo.HardwareManufacturer}, + {"model", _deviceInfo.HardwareModel}, + {"android_version", androidVersion.VersionNumber}, + {"android_release", androidVersion.APILevel} + } + }, + { + "edits", new JObject + { + {"crop_original_size", new JArray {image.Width, image.Height}}, + {"crop_center", new JArray {0.0, -0.0}}, + {"crop_zoom", 1} + } + }, + { + "extra", new JObject + { + {"source_width", image.Width}, + {"source_height", image.Height} + } + } + }; + var request = HttpHelper.GetSignedRequest(HttpMethod.Post, instaUri, _deviceInfo, data); + var response = await _httpRequestProcessor.SendAsync(request); + var json = await response.Content.ReadAsStringAsync(); + if (!response.IsSuccessStatusCode) + return Result.UnExpectedResponse(response, json); + var mediaResponse = + JsonConvert.DeserializeObject(json, new InstaMediaDataConverter()); + var converter = ConvertersFabric.Instance.GetSingleMediaConverter(mediaResponse); + return Result.Success(converter.Convert()); + } + catch (Exception exception) + { + _logger?.LogException(exception); + return Result.Fail(exception); + } + } + + public async Task> ConfigureAlbumAsync(string[] uploadId, string caption) + { + try + { + var instaUri = UriCreator.GetMediaAlbumConfigureUri(); + var clientSidecarId = ApiRequestMessage.GenerateUploadId(); + + var childrenArray = new JArray(); + + foreach (var id in uploadId) + childrenArray.Add(new JObject + { + {"scene_capture_type", "standard"}, + {"mas_opt_in", "NOT_PROMPTED"}, + {"camera_position", "unknown"}, + {"allow_multi_configures", false}, + {"geotag_enabled", false}, + {"disable_comments", false}, + {"source_type", 0}, + {"upload_id", id} + }); + + var data = new JObject + { + {"_uuid", _deviceInfo.DeviceGuid.ToString()}, + {"_uid", _user.LoggedInUder.Pk}, + {"_csrftoken", _user.CsrfToken}, + {"caption", caption}, + {"client_sidecar_id", clientSidecarId}, + {"geotag_enabled", false}, + {"disable_comments", false}, + {"children_metadata", childrenArray} + }; + var request = HttpHelper.GetSignedRequest(HttpMethod.Post, instaUri, _deviceInfo, data); + var response = await _httpRequestProcessor.SendAsync(request); + var json = await response.Content.ReadAsStringAsync(); + if (!response.IsSuccessStatusCode) + return Result.UnExpectedResponse(response, json); + var mediaResponse = JsonConvert.DeserializeObject(json); + var converter = ConvertersFabric.Instance.GetSingleMediaConverter(mediaResponse); + return Result.Success(converter.Convert()); + } + catch (Exception exception) + { + _logger?.LogException(exception); + return Result.Fail(exception); + } + } + + public async Task> GetMediaLikersAsync(string mediaId) + { + try + { + var likers = new InstaLikersList(); + var likersUri = UriCreator.GetMediaLikersUri(mediaId); + var request = HttpHelper.GetDefaultRequest(HttpMethod.Get, likersUri, _deviceInfo); + var response = await _httpRequestProcessor.SendAsync(request); + var json = await response.Content.ReadAsStringAsync(); + if (response.StatusCode != HttpStatusCode.OK) + return Result.UnExpectedResponse(response, json); + var mediaLikersResponse = JsonConvert.DeserializeObject(json); + likers.UsersCount = mediaLikersResponse.UsersCount; + if (mediaLikersResponse.UsersCount < 1) return Result.Success(likers); + likers.AddRange( + mediaLikersResponse.Users.Select(ConvertersFabric.Instance.GetUserShortConverter) + .Select(converter => converter.Convert())); + return Result.Success(likers); + } + catch (Exception exception) + { + _logger?.LogException(exception); + return Result.Fail(exception); + } + } + + public async Task> LikeMediaAsync(string mediaId) + { + try + { + return await LikeUnlikeMediaInternal(mediaId, UriCreator.GetLikeMediaUri(mediaId)); + } + catch (Exception exception) + { + _logger?.LogException(exception); + return Result.Fail(exception); + } + } + + public async Task> UnLikeMediaAsync(string mediaId) + { + try + { + return await LikeUnlikeMediaInternal(mediaId, UriCreator.GetUnLikeMediaUri(mediaId)); + } + catch (Exception exception) + { + _logger?.LogException(exception); + return Result.Fail(exception); + } + } + + public async Task> GetMediaByIdAsync(string mediaId) + { + try + { + var mediaUri = UriCreator.GetMediaUri(mediaId); + var request = HttpHelper.GetDefaultRequest(HttpMethod.Get, mediaUri, _deviceInfo); + var response = await _httpRequestProcessor.SendAsync(request); + var json = await response.Content.ReadAsStringAsync(); + if (response.StatusCode != HttpStatusCode.OK) + return Result.UnExpectedResponse(response, json); + var mediaResponse = JsonConvert.DeserializeObject(json, + new InstaMediaListDataConverter()); + if (mediaResponse.Medias?.Count > 1) + { + var errorMessage = $"Got wrong media count for request with media id={mediaId}"; + _logger?.LogInfo(errorMessage); + return Result.Fail(errorMessage); + } + var converter = + ConvertersFabric.Instance.GetSingleMediaConverter(mediaResponse.Medias.FirstOrDefault()); + return Result.Success(converter.Convert()); + } + catch (Exception exception) + { + _logger?.LogException(exception); + return Result.Fail(exception); + } + } + + public async Task> GetShareLinkFromMediaIdAsync(string mediaId) + { + try + { + var collectionUri = UriCreator.GetShareLinkFromMediaId(mediaId); + var request = HttpHelper.GetDefaultRequest(HttpMethod.Get, collectionUri, _deviceInfo); + var response = await _httpRequestProcessor.SendAsync(request); + var json = await response.Content.ReadAsStringAsync(); + + if (response.StatusCode != HttpStatusCode.OK) + return Result.UnExpectedResponse(response, json); + + var data = JsonConvert.DeserializeObject(json); + return Result.Success(new Uri(data.Permalink)); + } + catch (Exception exception) + { + _logger?.LogException(exception); + return Result.Fail(exception.Message); + } + } + + private async Task> LikeUnlikeMediaInternal(string mediaId, Uri instaUri) + { + var fields = new Dictionary + { + {"_uuid", _deviceInfo.DeviceGuid.ToString()}, + {"_uid", _user.LoggedInUder.Pk.ToString()}, + {"_csrftoken", _user.CsrfToken}, + {"media_id", mediaId} + }; + var request = + HttpHelper.GetSignedRequest(HttpMethod.Post, instaUri, _deviceInfo, fields); + var response = await _httpRequestProcessor.SendAsync(request); + var json = await response.Content.ReadAsStringAsync(); + return response.StatusCode == HttpStatusCode.OK + ? Result.Success(true) + : Result.UnExpectedResponse(response, json); + } + } +} \ No newline at end of file diff --git a/InstaSharper/API/Processors/MessagingProcessor.cs b/InstaSharper/API/Processors/MessagingProcessor.cs new file mode 100644 index 00000000..c2c3e4c6 --- /dev/null +++ b/InstaSharper/API/Processors/MessagingProcessor.cs @@ -0,0 +1,156 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Threading.Tasks; +using InstaSharper.Classes; +using InstaSharper.Classes.Android.DeviceInfo; +using InstaSharper.Classes.Models; +using InstaSharper.Classes.ResponseWrappers; +using InstaSharper.Converters; +using InstaSharper.Converters.Json; +using InstaSharper.Helpers; +using InstaSharper.Logger; +using Newtonsoft.Json; + +namespace InstaSharper.API.Processors +{ + public class MessagingProcessor : IMessagingProcessor + { + private readonly AndroidDevice _deviceInfo; + private readonly IHttpRequestProcessor _httpRequestProcessor; + private readonly IInstaLogger _logger; + private readonly UserSessionData _user; + + public MessagingProcessor(AndroidDevice deviceInfo, UserSessionData user, + IHttpRequestProcessor httpRequestProcessor, + IInstaLogger logger) + { + _deviceInfo = deviceInfo; + _user = user; + _httpRequestProcessor = httpRequestProcessor; + _logger = logger; + } + + public async Task> GetDirectInboxAsync() + { + try + { + var directInboxUri = UriCreator.GetDirectInboxUri(); + var request = HttpHelper.GetDefaultRequest(HttpMethod.Get, directInboxUri, _deviceInfo); + var response = await _httpRequestProcessor.SendAsync(request); + var json = await response.Content.ReadAsStringAsync(); + if (response.StatusCode != HttpStatusCode.OK) + return Result.UnExpectedResponse(response, json); + var inboxResponse = JsonConvert.DeserializeObject(json); + var converter = ConvertersFabric.Instance.GetDirectInboxConverter(inboxResponse); + return Result.Success(converter.Convert()); + } + catch (Exception exception) + { + _logger?.LogException(exception); + return Result.Fail(exception.Message); + } + } + + public async Task> GetDirectInboxThreadAsync(string threadId) + { + try + { + var directInboxUri = UriCreator.GetDirectInboxThreadUri(threadId); + var request = HttpHelper.GetDefaultRequest(HttpMethod.Get, directInboxUri, _deviceInfo); + var response = await _httpRequestProcessor.SendAsync(request); + var json = await response.Content.ReadAsStringAsync(); + if (response.StatusCode != HttpStatusCode.OK) + return Result.UnExpectedResponse(response, json); + var threadResponse = JsonConvert.DeserializeObject(json, + new InstaThreadDataConverter()); + var converter = ConvertersFabric.Instance.GetDirectThreadConverter(threadResponse); + return Result.Success(converter.Convert()); + } + catch (Exception exception) + { + _logger?.LogException(exception); + return Result.Fail(exception.Message); + } + } + + public async Task> SendDirectMessage(string recipients, string threadIds, + string text) + { + var threads = new InstaDirectInboxThreadList(); + try + { + var directSendMessageUri = UriCreator.GetDirectSendMessageUri(); + var request = HttpHelper.GetDefaultRequest(HttpMethod.Post, directSendMessageUri, _deviceInfo); + var fields = new Dictionary {{"text", text}}; + if (!string.IsNullOrEmpty(recipients)) + fields.Add("recipient_users", "[[" + recipients + "]]"); + else + return Result.Fail("Please provide at least one recipient."); + if (!string.IsNullOrEmpty(threadIds)) + fields.Add("thread_ids", "[" + threadIds + "]"); + + request.Content = new FormUrlEncodedContent(fields); + var response = await _httpRequestProcessor.SendAsync(request); + var json = await response.Content.ReadAsStringAsync(); + if (response.StatusCode != HttpStatusCode.OK) + return Result.UnExpectedResponse(response, json); + var result = JsonConvert.DeserializeObject(json); + if (!result.IsOk()) return Result.Fail(result.Status); + threads.AddRange(result.Threads.Select(thread => + ConvertersFabric.Instance.GetDirectThreadConverter(thread).Convert())); + return Result.Success(threads); + } + catch (Exception exception) + { + _logger?.LogException(exception); + return Result.Fail(exception); + } + } + + public async Task> GetRecentRecipientsAsync() + { + try + { + var userUri = UriCreator.GetRecentRecipientsUri(); + var request = HttpHelper.GetDefaultRequest(HttpMethod.Get, userUri, _deviceInfo); + var response = await _httpRequestProcessor.SendAsync(request); + var json = await response.Content.ReadAsStringAsync(); + + if (response.StatusCode != HttpStatusCode.OK) + return Result.UnExpectedResponse(response, json); + var responseRecipients = JsonConvert.DeserializeObject(json); + var converter = ConvertersFabric.Instance.GetRecipientsConverter(responseRecipients); + return Result.Success(converter.Convert()); + } + catch (Exception exception) + { + _logger?.LogException(exception); + return Result.Fail(exception.Message); + } + } + + public async Task> GetRankedRecipientsAsync() + { + try + { + var userUri = UriCreator.GetRankedRecipientsUri(); + var request = HttpHelper.GetDefaultRequest(HttpMethod.Get, userUri, _deviceInfo); + var response = await _httpRequestProcessor.SendAsync(request); + var json = await response.Content.ReadAsStringAsync(); + if (response.StatusCode != HttpStatusCode.OK) + return Result.UnExpectedResponse(response, json); + var responseRecipients = JsonConvert.DeserializeObject(json); + var converter = ConvertersFabric.Instance.GetRecipientsConverter(responseRecipients); + return Result.Success(converter.Convert()); + } + catch (Exception exception) + { + _logger?.LogException(exception); + return Result.Fail(exception.Message); + } + } + } +} \ No newline at end of file diff --git a/InstaSharper/API/Processors/StoryProcessor.cs b/InstaSharper/API/Processors/StoryProcessor.cs new file mode 100644 index 00000000..1a80dcf3 --- /dev/null +++ b/InstaSharper/API/Processors/StoryProcessor.cs @@ -0,0 +1,169 @@ +using System; +using System.IO; +using System.Net; +using System.Net.Http; +using System.Threading.Tasks; +using InstaSharper.Classes; +using InstaSharper.Classes.Android.DeviceInfo; +using InstaSharper.Classes.Models; +using InstaSharper.Classes.ResponseWrappers; +using InstaSharper.Converters; +using InstaSharper.Helpers; +using InstaSharper.Logger; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace InstaSharper.API.Processors +{ + public class StoryProcessor : IStoryProcessor + { + private readonly AndroidDevice _deviceInfo; + private readonly IHttpRequestProcessor _httpRequestProcessor; + private readonly IInstaLogger _logger; + private readonly UserSessionData _user; + + public StoryProcessor(AndroidDevice deviceInfo, UserSessionData user, + IHttpRequestProcessor httpRequestProcessor, IInstaLogger logger) + { + _deviceInfo = deviceInfo; + _user = user; + _httpRequestProcessor = httpRequestProcessor; + _logger = logger; + } + + public async Task> GetStoryFeedAsync() + { + try + { + var storyFeedUri = UriCreator.GetStoryFeedUri(); + var request = HttpHelper.GetDefaultRequest(HttpMethod.Get, storyFeedUri, _deviceInfo); + var response = await _httpRequestProcessor.SendAsync(request); + var json = await response.Content.ReadAsStringAsync(); + if (response.StatusCode != HttpStatusCode.OK) return Result.Fail("", (InstaStoryFeed) null); + var storyFeedResponse = JsonConvert.DeserializeObject(json); + var instaStoryFeed = ConvertersFabric.Instance.GetStoryFeedConverter(storyFeedResponse).Convert(); + return Result.Success(instaStoryFeed); + } + catch (Exception exception) + { + _logger?.LogException(exception); + return Result.Fail(exception.Message); + } + } + + public async Task> GetUserStoryAsync(long userId) + { + try + { + var userStoryUri = UriCreator.GetUserStoryUri(userId); + var request = HttpHelper.GetDefaultRequest(HttpMethod.Get, userStoryUri, _deviceInfo); + var response = await _httpRequestProcessor.SendAsync(request); + var json = await response.Content.ReadAsStringAsync(); + + if (response.StatusCode != HttpStatusCode.OK) Result.UnExpectedResponse(response, json); + var userStoryResponse = JsonConvert.DeserializeObject(json); + var userStory = ConvertersFabric.Instance.GetStoryConverter(userStoryResponse).Convert(); + return Result.Success(userStory); + } + catch (Exception exception) + { + _logger?.LogException(exception); + return Result.Fail(exception.Message); + } + } + + public async Task> UploadStoryPhotoAsync(InstaImage image, string caption) + { + try + { + var instaUri = UriCreator.GetUploadPhotoUri(); + var uploadId = ApiRequestMessage.GenerateUploadId(); + var requestContent = new MultipartFormDataContent(uploadId) + { + {new StringContent(uploadId), "\"upload_id\""}, + {new StringContent(_deviceInfo.DeviceGuid.ToString()), "\"_uuid\""}, + {new StringContent(_user.CsrfToken), "\"_csrftoken\""}, + { + new StringContent("{\"lib_name\":\"jt\",\"lib_version\":\"1.3.0\",\"quality\":\"87\"}"), + "\"image_compression\"" + } + }; + var imageContent = new ByteArrayContent(File.ReadAllBytes(image.URI)); + imageContent.Headers.Add("Content-Transfer-Encoding", "binary"); + imageContent.Headers.Add("Content-Type", "application/octet-stream"); + requestContent.Add(imageContent, "photo", $"pending_media_{ApiRequestMessage.GenerateUploadId()}.jpg"); + var request = HttpHelper.GetDefaultRequest(HttpMethod.Post, instaUri, _deviceInfo); + request.Content = requestContent; + var response = await _httpRequestProcessor.SendAsync(request); + var json = await response.Content.ReadAsStringAsync(); + if (response.IsSuccessStatusCode) + return await ConfigureStoryPhotoAsync(image, uploadId, caption); + return Result.UnExpectedResponse(response, json); + } + catch (Exception exception) + { + _logger?.LogException(exception); + return Result.Fail(exception.Message); + } + } + + public async Task> ConfigureStoryPhotoAsync(InstaImage image, string uploadId, + string caption) + { + try + { + var instaUri = UriCreator.GetStoryConfigureUri(); + var data = new JObject + { + {"_uuid", _deviceInfo.DeviceGuid.ToString()}, + {"_uid", _user.LoggedInUder.Pk}, + {"_csrftoken", _user.CsrfToken}, + {"source_type", "1"}, + {"caption", caption}, + {"upload_id", uploadId}, + {"edits", new JObject()}, + {"disable_comments", false}, + {"configure_mode", 1}, + {"camera_position", "unknown"} + }; + var request = HttpHelper.GetSignedRequest(HttpMethod.Post, instaUri, _deviceInfo, data); + var response = await _httpRequestProcessor.SendAsync(request); + var json = await response.Content.ReadAsStringAsync(); + if (response.IsSuccessStatusCode) + { + var mediaResponse = JsonConvert.DeserializeObject(json); + var converter = ConvertersFabric.Instance.GetStoryMediaConverter(mediaResponse); + return Result.Success(converter.Convert()); + } + return Result.UnExpectedResponse(response, json); + } + catch (Exception exception) + { + _logger?.LogException(exception); + return Result.Fail(exception.Message); + } + } + + public async Task> GetUserStoryFeedAsync(long userId) + { + var feed = new InsteReelFeed(); + try + { + var userFeedUri = UriCreator.GetUserReelFeedUri(userId); + var request = HttpHelper.GetDefaultRequest(HttpMethod.Get, userFeedUri, _deviceInfo); + var response = await _httpRequestProcessor.SendAsync(request); + var json = await response.Content.ReadAsStringAsync(); + if (response.StatusCode != HttpStatusCode.OK) + return Result.UnExpectedResponse(response, json); + var feedResponse = JsonConvert.DeserializeObject(json); + feed = ConvertersFabric.Instance.GetReelFeedConverter(feedResponse).Convert(); + return Result.Success(feed); + } + catch (Exception exception) + { + _logger?.LogException(exception); + return Result.Fail(exception.Message, feed); + } + } + } +} \ No newline at end of file diff --git a/InstaSharper/API/Processors/UserProcessor.cs b/InstaSharper/API/Processors/UserProcessor.cs new file mode 100644 index 00000000..4213d3f2 --- /dev/null +++ b/InstaSharper/API/Processors/UserProcessor.cs @@ -0,0 +1,386 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Threading.Tasks; +using InstaSharper.Classes; +using InstaSharper.Classes.Android.DeviceInfo; +using InstaSharper.Classes.Models; +using InstaSharper.Classes.ResponseWrappers; +using InstaSharper.Converters; +using InstaSharper.Converters.Json; +using InstaSharper.Helpers; +using InstaSharper.Logger; +using Newtonsoft.Json; + +namespace InstaSharper.API.Processors +{ + public class UserProcessor : IUserProcessor + { + private readonly AndroidDevice _deviceInfo; + private readonly IHttpRequestProcessor _httpRequestProcessor; + private readonly IInstaLogger _logger; + private readonly UserSessionData _user; + + public UserProcessor(AndroidDevice deviceInfo, UserSessionData user, IHttpRequestProcessor httpRequestProcessor, + IInstaLogger logger) + { + _deviceInfo = deviceInfo; + _user = user; + _httpRequestProcessor = httpRequestProcessor; + _logger = logger; + } + + public async Task> GetUserMediaAsync(long userId, + PaginationParameters paginationParameters) + { + var mediaList = new InstaMediaList(); + try + { + var instaUri = UriCreator.GetUserMediaListUri(userId, paginationParameters.NextId); + var request = HttpHelper.GetDefaultRequest(HttpMethod.Get, instaUri, _deviceInfo); + var response = await _httpRequestProcessor.SendAsync(request); + var json = await response.Content.ReadAsStringAsync(); + if (response.StatusCode != HttpStatusCode.OK) + return Result.UnExpectedResponse(response, json); + var mediaResponse = JsonConvert.DeserializeObject(json, + new InstaMediaListDataConverter()); + + mediaList = ConvertersFabric.Instance.GetMediaListConverter(mediaResponse).Convert(); + mediaList.NextId = paginationParameters.NextId = mediaResponse.NextMaxId; + paginationParameters.PagesLoaded++; + + while (mediaResponse.MoreAvailable + && !string.IsNullOrEmpty(paginationParameters.NextId) + && paginationParameters.PagesLoaded < paginationParameters.MaximumPagesToLoad) + { + var nextMedia = await GetUserMediaAsync(userId, paginationParameters); + if (!nextMedia.Succeeded) + return Result.Fail(nextMedia.Info, mediaList); + mediaList.NextId = paginationParameters.NextId = nextMedia.Value.NextId; + mediaList.AddRange(nextMedia.Value); + } + mediaList.Pages = paginationParameters.PagesLoaded; + mediaList.PageSize = mediaResponse.ResultsCount; + return Result.Success(mediaList); + } + catch (Exception exception) + { + _logger?.LogException(exception); + return Result.Fail(exception, mediaList); + } + } + + public async Task> GetUserAsync(string username) + { + try + { + var userUri = UriCreator.GetUserUri(username); + var request = HttpHelper.GetDefaultRequest(HttpMethod.Get, userUri, _deviceInfo); + request.Properties.Add(new KeyValuePair(InstaApiConstants.HEADER_TIMEZONE, + InstaApiConstants.TIMEZONE_OFFSET.ToString())); + request.Properties.Add(new KeyValuePair(InstaApiConstants.HEADER_COUNT, "1")); + request.Properties.Add( + new KeyValuePair(InstaApiConstants.HEADER_RANK_TOKEN, _user.RankToken)); + var response = await _httpRequestProcessor.SendAsync(request); + var json = await response.Content.ReadAsStringAsync(); + if (response.StatusCode != HttpStatusCode.OK) + return Result.UnExpectedResponse(response, json); + var userInfo = JsonConvert.DeserializeObject(json); + var user = userInfo.Users?.FirstOrDefault(u => u.UserName == username); + if (user == null) + { + var errorMessage = $"Can't find this user: {username}"; + _logger?.LogInfo(errorMessage); + return Result.Fail(errorMessage); + } + if (user.Pk < 1) + Result.Fail("Pk is incorrect"); + var converter = ConvertersFabric.Instance.GetUserConverter(user); + return Result.Success(converter.Convert()); + } + catch (Exception exception) + { + _logger?.LogException(exception); + return Result.Fail(exception.Message); + } + } + + public async Task> GetCurrentUserAsync() + { + try + { + var instaUri = UriCreator.GetCurrentUserUri(); + var fields = new Dictionary + { + {"_uuid", _deviceInfo.DeviceGuid.ToString()}, + {"_uid", _user.LoggedInUder.Pk.ToString()}, + {"_csrftoken", _user.CsrfToken} + }; + var request = HttpHelper.GetDefaultRequest(HttpMethod.Post, instaUri, _deviceInfo); + request.Content = new FormUrlEncodedContent(fields); + var response = await _httpRequestProcessor.SendAsync(request); + var json = await response.Content.ReadAsStringAsync(); + if (response.StatusCode != HttpStatusCode.OK) + return Result.UnExpectedResponse(response, json); + var user = JsonConvert.DeserializeObject(json, + new InstaCurrentUserDataConverter()); + if (user.Pk < 1) + Result.Fail("Pk is incorrect"); + var converter = ConvertersFabric.Instance.GetCurrentUserConverter(user); + var userConverted = converter.Convert(); + return Result.Success(userConverted); + } + catch (Exception exception) + { + _logger?.LogException(exception); + return Result.Fail(exception.Message); + } + } + + public async Task> GetUserFollowersAsync(string username, + PaginationParameters paginationParameters) + { + var followers = new InstaUserShortList(); + try + { + var user = await GetUserAsync(username); + var userFollowersUri = + UriCreator.GetUserFollowersUri(user.Value.Pk, _user.RankToken, paginationParameters.NextId); + var followersResponse = await GetUserListByUriAsync(userFollowersUri); + if (!followersResponse.Succeeded) + Result.Fail(followersResponse.Info, (InstaUserList) null); + followers.AddRange( + followersResponse.Value.Items.Select(ConvertersFabric.Instance.GetUserShortConverter) + .Select(converter => converter.Convert())); + followers.NextId = followersResponse.Value.NextMaxId; + var pagesLoaded = 1; + while (!string.IsNullOrEmpty(followersResponse.Value.NextMaxId) + && pagesLoaded < paginationParameters.MaximumPagesToLoad) + { + var nextFollowersUri = + UriCreator.GetUserFollowersUri(user.Value.Pk, _user.RankToken, + followersResponse.Value.NextMaxId); + followersResponse = await GetUserListByUriAsync(nextFollowersUri); + if (!followersResponse.Succeeded) + return Result.Success($"Not all pages were downloaded: {followersResponse.Info.Message}", + followers); + followers.AddRange( + followersResponse.Value.Items.Select(ConvertersFabric.Instance.GetUserShortConverter) + .Select(converter => converter.Convert())); + pagesLoaded++; + followers.NextId = followersResponse.Value.NextMaxId; + } + return Result.Success(followers); + } + catch (Exception exception) + { + _logger?.LogException(exception); + return Result.Fail(exception, followers); + } + } + + public async Task> GetUserFollowingAsync(string username, + PaginationParameters paginationParameters) + { + var following = new InstaUserShortList(); + try + { + var user = await GetUserAsync(username); + var uri = UriCreator.GetUserFollowingUri(user.Value.Pk, _user.RankToken, paginationParameters.NextId); + var userListResponse = await GetUserListByUriAsync(uri); + if (!userListResponse.Succeeded) + Result.Fail(userListResponse.Info, following); + following.AddRange( + userListResponse.Value.Items.Select(ConvertersFabric.Instance.GetUserShortConverter) + .Select(converter => converter.Convert())); + following.NextId = userListResponse.Value.NextMaxId; + var pages = 1; + while (!string.IsNullOrEmpty(following.NextId) + && pages < paginationParameters.MaximumPagesToLoad) + { + var nextUri = + UriCreator.GetUserFollowingUri(user.Value.Pk, _user.RankToken, + userListResponse.Value.NextMaxId); + userListResponse = await GetUserListByUriAsync(nextUri); + if (!userListResponse.Succeeded) + return Result.Success($"Not all pages were downloaded: {userListResponse.Info.Message}", + following); + following.AddRange( + userListResponse.Value.Items.Select(ConvertersFabric.Instance.GetUserShortConverter) + .Select(converter => converter.Convert())); + pages++; + following.NextId = userListResponse.Value.NextMaxId; + } + return Result.Success(following); + } + catch (Exception exception) + { + _logger?.LogException(exception); + return Result.Fail(exception, following); + } + } + + public async Task> GetCurrentUserFollowersAsync( + PaginationParameters paginationParameters) + { + return await GetUserFollowersAsync(_user.UserName, paginationParameters); + } + + public async Task> GetUserTagsAsync(long userId, + PaginationParameters paginationParameters) + { + var userTags = new InstaMediaList(); + try + { + var uri = UriCreator.GetUserTagsUri(userId, _user.RankToken, paginationParameters.NextId); + var request = HttpHelper.GetDefaultRequest(HttpMethod.Get, uri, _deviceInfo); + var response = await _httpRequestProcessor.SendAsync(request); + var json = await response.Content.ReadAsStringAsync(); + if (response.StatusCode != HttpStatusCode.OK) return Result.Fail("", (InstaMediaList) null); + var mediaResponse = JsonConvert.DeserializeObject(json, + new InstaMediaListDataConverter()); + userTags.AddRange( + mediaResponse.Medias.Select(ConvertersFabric.Instance.GetSingleMediaConverter) + .Select(converter => converter.Convert())); + userTags.NextId = paginationParameters.NextId = mediaResponse.NextMaxId; + paginationParameters.PagesLoaded++; + + while (mediaResponse.MoreAvailable + && !string.IsNullOrEmpty(paginationParameters.NextId) + && paginationParameters.PagesLoaded < paginationParameters.MaximumPagesToLoad) + { + var nextMedia = await GetUserTagsAsync(userId, paginationParameters); + if (!nextMedia.Succeeded) + return nextMedia; + + userTags.AddRange(nextMedia.Value); + userTags.NextId = paginationParameters.NextId = nextMedia.Value.NextId; + } + return Result.Success(userTags); + } + catch (Exception exception) + { + _logger?.LogException(exception); + return Result.Fail(exception, userTags); + } + } + + public async Task> FollowUserAsync(long userId) + { + return await FollowUnfollowUserInternal(userId, UriCreator.GetFollowUserUri(userId)); + } + + public async Task> UnFollowUserAsync(long userId) + { + return await FollowUnfollowUserInternal(userId, UriCreator.GetUnFollowUserUri(userId)); + } + + public async Task> BlockUserAsync(long userId) + { + return await BlockUnblockUserInternal(userId, UriCreator.GetBlockUserUri(userId)); + } + + public async Task> UnBlockUserAsync(long userId) + { + return await BlockUnblockUserInternal(userId, UriCreator.GetUnBlockUserUri(userId)); + } + + public async Task> GetFriendshipStatusAsync(long userId) + { + try + { + var userUri = UriCreator.GetUserFriendshipUri(userId); + var request = HttpHelper.GetDefaultRequest(HttpMethod.Get, userUri, _deviceInfo); + var response = await _httpRequestProcessor.SendAsync(request); + var json = await response.Content.ReadAsStringAsync(); + if (response.StatusCode != HttpStatusCode.OK) + return Result.UnExpectedResponse(response, json); + var friendshipStatusResponse = JsonConvert.DeserializeObject(json); + var converter = ConvertersFabric.Instance.GetFriendShipStatusConverter(friendshipStatusResponse); + return Result.Success(converter.Convert()); + } + catch (Exception exception) + { + _logger?.LogException(exception); + return Result.Fail(exception.Message); + } + } + + private async Task> FollowUnfollowUserInternal(long userId, Uri instaUri) + { + try + { + var fields = new Dictionary + { + {"_uuid", _deviceInfo.DeviceGuid.ToString()}, + {"_uid", _user.LoggedInUder.Pk.ToString()}, + {"_csrftoken", _user.CsrfToken}, + {"user_id", userId.ToString()}, + {"radio_type", "wifi-none"} + }; + var request = + HttpHelper.GetSignedRequest(HttpMethod.Post, instaUri, _deviceInfo, fields); + var response = await _httpRequestProcessor.SendAsync(request); + var json = await response.Content.ReadAsStringAsync(); + if (response.StatusCode != HttpStatusCode.OK || string.IsNullOrEmpty(json)) + return Result.UnExpectedResponse(response, json); + var friendshipStatus = JsonConvert.DeserializeObject(json, + new InstaFriendShipDataConverter()); + var converter = ConvertersFabric.Instance.GetFriendShipStatusConverter(friendshipStatus); + return Result.Success(converter.Convert()); + } + catch (Exception exception) + { + _logger?.LogException(exception); + return Result.Fail(exception.Message, (InstaFriendshipStatus) null); + } + } + + private async Task> BlockUnblockUserInternal(long userId, Uri instaUri) + { + try + { + var fields = new Dictionary + { + {"_uuid", _deviceInfo.DeviceGuid.ToString()}, + {"_uid", _user.LoggedInUder.Pk.ToString()}, + {"_csrftoken", _user.CsrfToken}, + {"user_id", userId.ToString()}, + {"radio_type", "wifi-none"} + }; + var request = + HttpHelper.GetSignedRequest(HttpMethod.Post, instaUri, _deviceInfo, fields); + var response = await _httpRequestProcessor.SendAsync(request); + var json = await response.Content.ReadAsStringAsync(); + if (response.StatusCode != HttpStatusCode.OK || string.IsNullOrEmpty(json)) + return Result.UnExpectedResponse(response, json); + var friendshipStatus = JsonConvert.DeserializeObject(json, + new InstaFriendShipDataConverter()); + var converter = ConvertersFabric.Instance.GetFriendShipStatusConverter(friendshipStatus); + return Result.Success(converter.Convert()); + } + catch (Exception exception) + { + _logger?.LogException(exception); + return Result.Fail(exception.Message, (InstaFriendshipStatus) null); + } + } + + private async Task> GetUserListByUriAsync(Uri uri) + { + var request = HttpHelper.GetDefaultRequest(HttpMethod.Get, uri, _deviceInfo); + var response = await _httpRequestProcessor.SendAsync(request); + var json = await response.Content.ReadAsStringAsync(); + if (response.StatusCode != HttpStatusCode.OK) + return Result.UnExpectedResponse(response, json); + var instaUserListResponse = JsonConvert.DeserializeObject(json); + if (instaUserListResponse.IsOk()) + return Result.Success(instaUserListResponse); + var status = ErrorHandlingHelper.GetBadStatusFromJsonString(json); + Result.Fail(new ResultInfo(status.Message), (InstaUserListShortResponse) null); + return Result.Success(instaUserListResponse); + } + } +} \ No newline at end of file diff --git a/InstaSharper/API/Processors/UserProfileProcessor.cs b/InstaSharper/API/Processors/UserProfileProcessor.cs new file mode 100644 index 00000000..c4c7b155 --- /dev/null +++ b/InstaSharper/API/Processors/UserProfileProcessor.cs @@ -0,0 +1,148 @@ +using System; +using System.Collections.Generic; +using System.Net; +using System.Net.Http; +using System.Threading.Tasks; +using InstaSharper.Classes; +using InstaSharper.Classes.Android.DeviceInfo; +using InstaSharper.Classes.Models; +using InstaSharper.Classes.ResponseWrappers; +using InstaSharper.Converters; +using InstaSharper.Converters.Json; +using InstaSharper.Helpers; +using InstaSharper.Logger; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace InstaSharper.API.Processors +{ + public class UserProfileProcessor : IUserProfileProcessor + { + private readonly AndroidDevice _deviceInfo; + private readonly IHttpRequestProcessor _httpRequestProcessor; + private readonly IInstaLogger _logger; + private readonly UserSessionData _user; + + public UserProfileProcessor(AndroidDevice deviceInfo, UserSessionData user, + IHttpRequestProcessor httpRequestProcessor, IInstaLogger logger) + { + _deviceInfo = deviceInfo; + _user = user; + _httpRequestProcessor = httpRequestProcessor; + _logger = logger; + } + + public async Task> SetAccountPrivateAsync() + { + try + { + var instaUri = UriCreator.GetUriSetAccountPrivate(); + var fields = new Dictionary + { + {"_uuid", _deviceInfo.DeviceGuid.ToString()}, + {"_uid", _user.LoggedInUder.Pk.ToString()}, + {"_csrftoken", _user.CsrfToken} + }; + var hash = CryptoHelper.CalculateHash(InstaApiConstants.IG_SIGNATURE_KEY, + JsonConvert.SerializeObject(fields)); + var payload = JsonConvert.SerializeObject(fields); + var signature = $"{hash}.{Uri.EscapeDataString(payload)}"; + var request = HttpHelper.GetDefaultRequest(HttpMethod.Post, instaUri, _deviceInfo); + request.Content = new FormUrlEncodedContent(fields); + request.Properties.Add(InstaApiConstants.HEADER_IG_SIGNATURE, signature); + request.Properties.Add(InstaApiConstants.HEADER_IG_SIGNATURE_KEY_VERSION, + InstaApiConstants.IG_SIGNATURE_KEY_VERSION); + var response = await _httpRequestProcessor.SendAsync(request); + var json = await response.Content.ReadAsStringAsync(); + if (response.StatusCode != HttpStatusCode.OK) + return Result.UnExpectedResponse(response, json); + var userInfoUpdated = + JsonConvert.DeserializeObject(json, new InstaUserShortDataConverter()); + if (userInfoUpdated.Pk < 1) + return Result.Fail("Pk is null or empty"); + var converter = ConvertersFabric.Instance.GetUserShortConverter(userInfoUpdated); + return Result.Success(converter.Convert()); + } + catch (Exception exception) + { + _logger?.LogException(exception); + return Result.Fail(exception.Message, (InstaUserShort) null); + } + } + + public async Task> SetAccountPublicAsync() + { + try + { + var instaUri = UriCreator.GetUriSetAccountPublic(); + var fields = new Dictionary + { + {"_uuid", _deviceInfo.DeviceGuid.ToString()}, + {"_uid", _user.LoggedInUder.Pk.ToString()}, + {"_csrftoken", _user.CsrfToken} + }; + var hash = CryptoHelper.CalculateHash(InstaApiConstants.IG_SIGNATURE_KEY, + JsonConvert.SerializeObject(fields)); + var payload = JsonConvert.SerializeObject(fields); + var signature = $"{hash}.{Uri.EscapeDataString(payload)}"; + var request = HttpHelper.GetDefaultRequest(HttpMethod.Post, instaUri, _deviceInfo); + request.Content = new FormUrlEncodedContent(fields); + request.Properties.Add(InstaApiConstants.HEADER_IG_SIGNATURE, signature); + request.Properties.Add(InstaApiConstants.HEADER_IG_SIGNATURE_KEY_VERSION, + InstaApiConstants.IG_SIGNATURE_KEY_VERSION); + var response = await _httpRequestProcessor.SendAsync(request); + var json = await response.Content.ReadAsStringAsync(); + if (response.StatusCode == HttpStatusCode.OK) + { + var userInfoUpdated = + JsonConvert.DeserializeObject(json, new InstaUserShortDataConverter()); + if (userInfoUpdated.Pk < 1) + return Result.Fail("Pk is incorrect"); + var converter = ConvertersFabric.Instance.GetUserShortConverter(userInfoUpdated); + return Result.Success(converter.Convert()); + } + return Result.UnExpectedResponse(response, json); + } + catch (Exception exception) + { + _logger?.LogException(exception); + return Result.Fail(exception.Message, (InstaUserShort) null); + } + } + + public async Task> ChangePasswordAsync(string oldPassword, string newPassword) + { + if (oldPassword == newPassword) + return Result.Fail("The old password should not the same of the new password", false); + + try + { + var changePasswordUri = UriCreator.GetChangePasswordUri(); + + var data = new JObject + { + {"_uuid", _deviceInfo.DeviceGuid.ToString()}, + {"_uid", _user.LoggedInUder.Pk}, + {"_csrftoken", _user.CsrfToken}, + {"old_password", oldPassword}, + {"new_password1", newPassword}, + {"new_password2", newPassword} + }; + + var request = HttpHelper.GetSignedRequest(HttpMethod.Get, changePasswordUri, _deviceInfo, data); + var response = await _httpRequestProcessor.SendAsync(request); + var json = await response.Content.ReadAsStringAsync(); + if (response.StatusCode == HttpStatusCode.OK) + return Result.Success(true); + var error = JsonConvert.DeserializeObject(json); + var errors = ""; + error.Message.Errors.ForEach(errorContent => errors += errorContent + "\n"); + return Result.Fail(errors, false); + } + catch (Exception exception) + { + return Result.Fail(exception.Message, false); + } + } + } +} \ No newline at end of file diff --git a/InstaSharper/API/UriCreators/GetLocationFeedUriCreator.cs b/InstaSharper/API/UriCreators/GetLocationFeedUriCreator.cs new file mode 100644 index 00000000..d9dc1b8a --- /dev/null +++ b/InstaSharper/API/UriCreators/GetLocationFeedUriCreator.cs @@ -0,0 +1,19 @@ +using System; + +namespace InstaSharper.API.UriCreators +{ + internal class GetLocationFeedUriCreator : IUriCreatorNextId + { + private const string LocationFeed = "feed/location/{0}/"; + + public Uri GetUri(long id, string nextId) + { + if (!Uri.TryCreate(InstaApiConstants.BaseInstagramUri, string.Format(LocationFeed, id), out var instaUri)) + throw new Exception("Can't create URI for getting location feed"); + var query = string.Empty; + if (!string.IsNullOrEmpty(nextId)) query += $"max_id={nextId}"; + var uriBuilder = new UriBuilder(instaUri) {Query = query}; + return uriBuilder.Uri; + } + } +} \ No newline at end of file diff --git a/InstaSharper/API/UriCreators/IUriCreator.cs b/InstaSharper/API/UriCreators/IUriCreator.cs new file mode 100644 index 00000000..0a3314f4 --- /dev/null +++ b/InstaSharper/API/UriCreators/IUriCreator.cs @@ -0,0 +1,9 @@ +using System; + +namespace InstaSharper.API.UriCreators +{ + public interface IUriCreator + { + Uri GetUri(); + } +} \ No newline at end of file diff --git a/InstaSharper/API/UriCreators/IUriCreatorNextId.cs b/InstaSharper/API/UriCreators/IUriCreatorNextId.cs new file mode 100644 index 00000000..e50f96e5 --- /dev/null +++ b/InstaSharper/API/UriCreators/IUriCreatorNextId.cs @@ -0,0 +1,9 @@ +using System; + +namespace InstaSharper.API.UriCreators +{ + public interface IUriCreatorNextId + { + Uri GetUri(long id, string nextId); + } +} \ No newline at end of file diff --git a/InstaSharper/API/UriCreators/SearchLocationUriCreator.cs b/InstaSharper/API/UriCreators/SearchLocationUriCreator.cs new file mode 100644 index 00000000..c85f5a6e --- /dev/null +++ b/InstaSharper/API/UriCreators/SearchLocationUriCreator.cs @@ -0,0 +1,16 @@ +using System; + +namespace InstaSharper.API.UriCreators +{ + internal class SearchLocationUriCreator : IUriCreator + { + private const string SearchLocation = "location_search"; + + public Uri GetUri() + { + if (!Uri.TryCreate(InstaApiConstants.BaseInstagramUri, SearchLocation, out var instaUri)) + throw new Exception("Can't create URI for searchiing location"); + return instaUri; + } + } +} \ No newline at end of file diff --git a/InstaSharper/Classes/Android/DeviceInfo/ApiRequestMessage.cs b/InstaSharper/Classes/Android/DeviceInfo/ApiRequestMessage.cs index ebd2afb6..4f32aa87 100644 --- a/InstaSharper/Classes/Android/DeviceInfo/ApiRequestMessage.cs +++ b/InstaSharper/Classes/Android/DeviceInfo/ApiRequestMessage.cs @@ -19,9 +19,11 @@ internal string GetMessageString() return JsonConvert.SerializeObject(this); } - internal string GenerateSignature() + internal string GenerateSignature(string signatureKey) { - return CryptoHelper.CalculateHash(InstaApiConstants.IG_SIGNATURE_KEY, + if (string.IsNullOrEmpty(signatureKey)) + signatureKey = InstaApiConstants.IG_SIGNATURE_KEY; + return CryptoHelper.CalculateHash(signatureKey, JsonConvert.SerializeObject(this)); } diff --git a/InstaSharper/Classes/Android/DeviceInfo/ApiTwoFactorRequestMessage.cs b/InstaSharper/Classes/Android/DeviceInfo/ApiTwoFactorRequestMessage.cs new file mode 100644 index 00000000..38ec1058 --- /dev/null +++ b/InstaSharper/Classes/Android/DeviceInfo/ApiTwoFactorRequestMessage.cs @@ -0,0 +1,37 @@ +using InstaSharper.API; +using InstaSharper.Helpers; +using Newtonsoft.Json; + +namespace InstaSharper.Classes.Android.DeviceInfo +{ + internal class ApiTwoFactorRequestMessage + { + internal ApiTwoFactorRequestMessage(string verificationCode, string username, string deviceId, + string twoFactorIdentifier) + { + verification_code = verificationCode; + this.username = username; + device_id = deviceId; + two_factor_identifier = twoFactorIdentifier; + } + + public string verification_code { get; set; } + public string username { get; set; } + public string device_id { get; set; } + public string two_factor_identifier { get; set; } + + + internal string GenerateSignature(string signatureKey) + { + if (string.IsNullOrEmpty(signatureKey)) + signatureKey = InstaApiConstants.IG_SIGNATURE_KEY; + return CryptoHelper.CalculateHash(signatureKey, + JsonConvert.SerializeObject(this)); + } + + internal string GetMessageString() + { + return JsonConvert.SerializeObject(this); + } + } +} \ No newline at end of file diff --git a/InstaSharper/Classes/InstaLoginBaseResponse.cs b/InstaSharper/Classes/InstaLoginBaseResponse.cs new file mode 100644 index 00000000..762200ac --- /dev/null +++ b/InstaSharper/Classes/InstaLoginBaseResponse.cs @@ -0,0 +1,27 @@ +using Newtonsoft.Json; + +namespace InstaSharper.Classes +{ + internal class InstaLoginBaseResponse + { + #region InvalidCredentials + + [JsonProperty("invalid_credentials")] + public bool InvalidCredentials { get; set; } + + [JsonProperty("error_type")] + public string ErrorType { get; set; } + + #endregion + + #region 2 Factor Authentication + + [JsonProperty("two_factor_required")] + public bool TwoFactorRequired { get; set; } + + [JsonProperty("two_factor_info")] + public TwoFactorLoginInfo TwoFactorLoginInfo { get; set; } + + #endregion + } +} \ No newline at end of file diff --git a/InstaSharper/Classes/InstaLoginResult.cs b/InstaSharper/Classes/InstaLoginResult.cs new file mode 100644 index 00000000..4ff133eb --- /dev/null +++ b/InstaSharper/Classes/InstaLoginResult.cs @@ -0,0 +1,11 @@ +namespace InstaSharper.Classes +{ + public enum InstaLoginResult + { + Success, + BadPassword, + InvalidUser, + TwoFactorRequired, + Exception + } +} \ No newline at end of file diff --git a/InstaSharper/Classes/InstaLoginTwoFactorBaseResponse.cs b/InstaSharper/Classes/InstaLoginTwoFactorBaseResponse.cs new file mode 100644 index 00000000..f19d9c27 --- /dev/null +++ b/InstaSharper/Classes/InstaLoginTwoFactorBaseResponse.cs @@ -0,0 +1,13 @@ +using Newtonsoft.Json; + +namespace InstaSharper.Classes +{ + internal class InstaLoginTwoFactorBaseResponse + { + [JsonProperty("message")] + public string Message { get; set; } + + [JsonProperty("error_type")] + public string ErrorType { get; set; } + } +} \ No newline at end of file diff --git a/InstaSharper/Classes/InstaLoginTwoFactorResult.cs b/InstaSharper/Classes/InstaLoginTwoFactorResult.cs new file mode 100644 index 00000000..e67c3ccd --- /dev/null +++ b/InstaSharper/Classes/InstaLoginTwoFactorResult.cs @@ -0,0 +1,10 @@ +namespace InstaSharper.Classes +{ + public enum InstaLoginTwoFactorResult + { + Success, //Ok + InvalidCode, //sms_code_validation_code_invalid + CodeExpired, //invalid_nonce + Exception + } +} \ No newline at end of file diff --git a/InstaSharper/Classes/Models/InstaActivityFeed.cs b/InstaSharper/Classes/Models/InstaActivityFeed.cs index e0f0768d..2f712939 100644 --- a/InstaSharper/Classes/Models/InstaActivityFeed.cs +++ b/InstaSharper/Classes/Models/InstaActivityFeed.cs @@ -2,9 +2,10 @@ namespace InstaSharper.Classes.Models { - public class InstaActivityFeed + public class InstaActivityFeed : IInstaBaseList { public bool IsOwnActivity { get; set; } = false; public List Items { get; set; } = new List(); + public string NextId { get; set; } } } \ No newline at end of file diff --git a/InstaSharper/Classes/Models/InstaBaseFeed.cs b/InstaSharper/Classes/Models/InstaBaseFeed.cs new file mode 100644 index 00000000..0ad17f9d --- /dev/null +++ b/InstaSharper/Classes/Models/InstaBaseFeed.cs @@ -0,0 +1,8 @@ +namespace InstaSharper.Classes.Models +{ + public class InstaBaseFeed + { + public InstaMediaList Medias { get; set; } = new InstaMediaList(); + public string NextId { get; set; } + } +} \ No newline at end of file diff --git a/InstaSharper/Classes/Models/InstaBaseList.cs b/InstaSharper/Classes/Models/InstaBaseList.cs new file mode 100644 index 00000000..4a9ea312 --- /dev/null +++ b/InstaSharper/Classes/Models/InstaBaseList.cs @@ -0,0 +1,7 @@ +namespace InstaSharper.Classes.Models +{ + public interface IInstaBaseList + { + string NextId { get; set; } + } +} \ No newline at end of file diff --git a/InstaSharper/Classes/Models/InstaCollectionItem.cs b/InstaSharper/Classes/Models/InstaCollectionItem.cs new file mode 100644 index 00000000..a67da08b --- /dev/null +++ b/InstaSharper/Classes/Models/InstaCollectionItem.cs @@ -0,0 +1,15 @@ +namespace InstaSharper.Classes.Models +{ + public class InstaCollectionItem + { + public long CollectionId { get; set; } + + public string CollectionName { get; set; } + + public bool HasRelatedMedia { get; set; } + + public InstaMediaList Media { get; set; } + + public InstaCoverMedia CoverMedia { get; set; } + } +} \ No newline at end of file diff --git a/InstaSharper/Classes/Models/InstaCollections.cs b/InstaSharper/Classes/Models/InstaCollections.cs new file mode 100644 index 00000000..a6b02f76 --- /dev/null +++ b/InstaSharper/Classes/Models/InstaCollections.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; + +namespace InstaSharper.Classes.Models +{ + public class InstaCollections + { + public bool MoreCollectionsAvailable { get; set; } + + public int Pages { get; set; } = 0; + + public List Items { get; set; } + } +} \ No newline at end of file diff --git a/InstaSharper/Classes/Models/InstaCommentList.cs b/InstaSharper/Classes/Models/InstaCommentList.cs index b2ff0f77..1f2d8592 100644 --- a/InstaSharper/Classes/Models/InstaCommentList.cs +++ b/InstaSharper/Classes/Models/InstaCommentList.cs @@ -2,10 +2,8 @@ namespace InstaSharper.Classes.Models { - public class InstaCommentList + public class InstaCommentList : IInstaBaseList { - public int CommentsCount { get; set; } - public bool LikesEnabled { get; set; } public bool CaptionIsEdited { get; set; } @@ -17,6 +15,7 @@ public class InstaCommentList public bool MoreComentsAvailable { get; set; } public List Comments { get; set; } = new List(); - public int Pages { get; set; } = 0; + + public string NextId { get; set; } } } \ No newline at end of file diff --git a/InstaSharper/Classes/Models/InstaCoverMedia.cs b/InstaSharper/Classes/Models/InstaCoverMedia.cs new file mode 100644 index 00000000..f3753aaa --- /dev/null +++ b/InstaSharper/Classes/Models/InstaCoverMedia.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; + +namespace InstaSharper.Classes.Models +{ + public class InstaCoverMedia + { + public long Id { get; set; } + + public List ImageVersions { get; set; } + + public int MediaType { get; set; } + + public int OriginalHeight { get; set; } + + public int OriginalWidth { get; set; } + } +} \ No newline at end of file diff --git a/InstaSharper/Classes/Models/InstaDirectInboxItem.cs b/InstaSharper/Classes/Models/InstaDirectInboxItem.cs index d74ef3a6..085fc805 100644 --- a/InstaSharper/Classes/Models/InstaDirectInboxItem.cs +++ b/InstaSharper/Classes/Models/InstaDirectInboxItem.cs @@ -15,8 +15,9 @@ public class InstaDirectInboxItem public string ItemId { get; set; } - public InstaDirectThreadItemType ItemType { get; set; } + public InstaDirectThreadItemType ItemType { get; set; } = InstaDirectThreadItemType.Text; + public InstaInboxMedia Media { get; set; } public InstaMedia MediaShare { get; set; } diff --git a/InstaSharper/Classes/Models/InstaDirectInboxThreadList.cs b/InstaSharper/Classes/Models/InstaDirectInboxThreadList.cs new file mode 100644 index 00000000..37e569ef --- /dev/null +++ b/InstaSharper/Classes/Models/InstaDirectInboxThreadList.cs @@ -0,0 +1,8 @@ +using System.Collections.Generic; + +namespace InstaSharper.Classes.Models +{ + public class InstaDirectInboxThreadList : List + { + } +} \ No newline at end of file diff --git a/InstaSharper/Classes/Models/InstaDirectThreadItemType.cs b/InstaSharper/Classes/Models/InstaDirectThreadItemType.cs index 83610890..6a9a128e 100644 --- a/InstaSharper/Classes/Models/InstaDirectThreadItemType.cs +++ b/InstaSharper/Classes/Models/InstaDirectThreadItemType.cs @@ -3,6 +3,9 @@ public enum InstaDirectThreadItemType { Text = 0, - MediaShare = 1 + MediaShare = 1, + Like = 2, + Link = 3, + Media = 4 } } \ No newline at end of file diff --git a/InstaSharper/Classes/Models/InstaExploreFeed.cs b/InstaSharper/Classes/Models/InstaExploreFeed.cs index 559c407c..ed121b3a 100644 --- a/InstaSharper/Classes/Models/InstaExploreFeed.cs +++ b/InstaSharper/Classes/Models/InstaExploreFeed.cs @@ -1,8 +1,7 @@ namespace InstaSharper.Classes.Models { - public class InstaExploreFeed + public class InstaExploreFeed : InstaBaseFeed { - public InstaMediaList Medias { get; set; } = new InstaMediaList(); public InstaStoryTray StoryTray { get; set; } = new InstaStoryTray(); public InstaChannel Channel { get; set; } = new InstaChannel(); } diff --git a/InstaSharper/Classes/Models/InstaFeed.cs b/InstaSharper/Classes/Models/InstaFeed.cs index f7f90549..b3ef644f 100644 --- a/InstaSharper/Classes/Models/InstaFeed.cs +++ b/InstaSharper/Classes/Models/InstaFeed.cs @@ -2,12 +2,13 @@ namespace InstaSharper.Classes.Models { - public class InstaFeed + public class InstaFeed : IInstaBaseList { public int MediaItemsCount => Medias.Count; public int StoriesItemsCount => Stories.Count; - public InstaMediaList Medias { get; set; } = new InstaMediaList(); + public List Medias { get; set; } = new List(); public List Stories { get; set; } = new List(); + public string NextId { get; set; } } } \ No newline at end of file diff --git a/InstaSharper/Classes/Models/InstaInboxMedia.cs b/InstaSharper/Classes/Models/InstaInboxMedia.cs new file mode 100644 index 00000000..6bf5ba6e --- /dev/null +++ b/InstaSharper/Classes/Models/InstaInboxMedia.cs @@ -0,0 +1,12 @@ +using System.Collections.Generic; + +namespace InstaSharper.Classes.Models +{ + public class InstaInboxMedia + { + public List Images { get; set; } = new List(); + public long OriginalWidth { get; set; } + public long OriginalHeight { get; set; } + public InstaMediaType MediaType { get; set; } + } +} \ No newline at end of file diff --git a/InstaSharper/Classes/Models/InstaLocation.cs b/InstaSharper/Classes/Models/InstaLocation.cs index 3fea0672..190bbecb 100644 --- a/InstaSharper/Classes/Models/InstaLocation.cs +++ b/InstaSharper/Classes/Models/InstaLocation.cs @@ -1,23 +1,13 @@ namespace InstaSharper.Classes.Models { - public class InstaLocation + public class InstaLocation : InstaLocationShort { public long FacebookPlacesId { get; set; } public string City { get; set; } - public string Address { get; set; } - - public string ExternalSource { get; set; } - - public double Lng { get; set; } - public long Pk { get; set; } - public double Lat { get; set; } - - public string Name { get; set; } - public string ShortName { get; set; } } } \ No newline at end of file diff --git a/InstaSharper/Classes/Models/InstaLocationFeed.cs b/InstaSharper/Classes/Models/InstaLocationFeed.cs new file mode 100644 index 00000000..b57b1a46 --- /dev/null +++ b/InstaSharper/Classes/Models/InstaLocationFeed.cs @@ -0,0 +1,11 @@ +namespace InstaSharper.Classes.Models +{ + public class InstaLocationFeed : InstaBaseFeed + { + public InstaMediaList RankedMedias { get; set; } = new InstaMediaList(); + public InstaStory Story { get; set; } + public InstaLocation Location { get; set; } + + public long MediaCount { get; set; } + } +} \ No newline at end of file diff --git a/InstaSharper/Classes/Models/InstaLocationList.cs b/InstaSharper/Classes/Models/InstaLocationList.cs new file mode 100644 index 00000000..7a8b46ac --- /dev/null +++ b/InstaSharper/Classes/Models/InstaLocationList.cs @@ -0,0 +1,8 @@ +using System.Collections.Generic; + +namespace InstaSharper.Classes.Models +{ + public class InstaLocationList : List + { + } +} \ No newline at end of file diff --git a/InstaSharper/Classes/Models/InstaLocationShort.cs b/InstaSharper/Classes/Models/InstaLocationShort.cs new file mode 100644 index 00000000..6d63a125 --- /dev/null +++ b/InstaSharper/Classes/Models/InstaLocationShort.cs @@ -0,0 +1,17 @@ +namespace InstaSharper.Classes.Models +{ + public class InstaLocationShort + { + public string ExternalSource { get; set; } + + public string ExternalId { get; set; } + + public string Address { get; set; } + + public double Lng { get; set; } + + public double Lat { get; set; } + + public string Name { get; set; } + } +} \ No newline at end of file diff --git a/InstaSharper/Classes/Models/InstaLocationShortList.cs b/InstaSharper/Classes/Models/InstaLocationShortList.cs new file mode 100644 index 00000000..92764a4f --- /dev/null +++ b/InstaSharper/Classes/Models/InstaLocationShortList.cs @@ -0,0 +1,8 @@ +using System.Collections.Generic; + +namespace InstaSharper.Classes.Models +{ + public class InstaLocationShortList : List + { + } +} \ No newline at end of file diff --git a/InstaSharper/Classes/Models/InstaMedia.cs b/InstaSharper/Classes/Models/InstaMedia.cs index ca722eb5..ddc40b72 100644 --- a/InstaSharper/Classes/Models/InstaMedia.cs +++ b/InstaSharper/Classes/Models/InstaMedia.cs @@ -57,5 +57,7 @@ public class InstaMedia public bool HasAudio { get; set; } public bool IsMultiPost => Carousel != null; + public List PreviewComments { get; set; } = new List(); + public InstaLocation Location { get; set; } } } \ No newline at end of file diff --git a/InstaSharper/Classes/Models/InstaMediaList.cs b/InstaSharper/Classes/Models/InstaMediaList.cs index 248887df..fb7a4f06 100644 --- a/InstaSharper/Classes/Models/InstaMediaList.cs +++ b/InstaSharper/Classes/Models/InstaMediaList.cs @@ -2,9 +2,10 @@ namespace InstaSharper.Classes.Models { - public class InstaMediaList : List + public class InstaMediaList : List, IInstaBaseList { public int Pages { get; set; } = 0; public int PageSize { get; set; } = 0; + public string NextId { get; set; } } } \ No newline at end of file diff --git a/InstaSharper/Classes/Models/InstaRecipientThreads.cs b/InstaSharper/Classes/Models/InstaRecipients.cs similarity index 53% rename from InstaSharper/Classes/Models/InstaRecipientThreads.cs rename to InstaSharper/Classes/Models/InstaRecipients.cs index 8d3aa158..5f04aa69 100644 --- a/InstaSharper/Classes/Models/InstaRecipientThreads.cs +++ b/InstaSharper/Classes/Models/InstaRecipients.cs @@ -2,9 +2,12 @@ namespace InstaSharper.Classes.Models { - public class InstaRecipientThreads + public class InstaRecipients { - public List Items = new List(); + public List Threads { get; set; } = new List(); + + public List Users { get; set; } = new List(); + public long ExpiresIn { get; set; } public bool Filtered { get; set; } diff --git a/InstaSharper/Classes/Models/InstaStory.cs b/InstaSharper/Classes/Models/InstaStory.cs index 9521037d..73a1a648 100644 --- a/InstaSharper/Classes/Models/InstaStory.cs +++ b/InstaSharper/Classes/Models/InstaStory.cs @@ -7,11 +7,11 @@ public class InstaStory { public bool CanReply { get; set; } - public long ExpiringAt { get; set; } + public DateTime ExpiringAt { get; set; } public InstaUserShort User { get; set; } - public InstaOwner Owner { get; set; } + public InstaUserShort Owner { get; set; } public string SourceToken { get; set; } @@ -27,7 +27,7 @@ public class InstaStory public int SeenRankedPosition { get; set; } - public List Items { get; set; } + public List Items { get; set; } = new InstaMediaList(); public int PrefetchCount { get; set; } diff --git a/InstaSharper/Classes/Models/InstaUserShort.cs b/InstaSharper/Classes/Models/InstaUserShort.cs index 6a51de42..2a73758c 100644 --- a/InstaSharper/Classes/Models/InstaUserShort.cs +++ b/InstaSharper/Classes/Models/InstaUserShort.cs @@ -7,7 +7,7 @@ public class InstaUserShort { public bool IsVerified { get; set; } public bool IsPrivate { get; set; } - public string Pk { get; set; } + public long Pk { get; set; } public string ProfilePicture { get; set; } public string ProfilePictureId { get; set; } = "unknown"; public string UserName { get; set; } diff --git a/InstaSharper/Classes/Models/InstaUserShortList.cs b/InstaSharper/Classes/Models/InstaUserShortList.cs index 3ce0e1f5..0c8adced 100644 --- a/InstaSharper/Classes/Models/InstaUserShortList.cs +++ b/InstaSharper/Classes/Models/InstaUserShortList.cs @@ -2,7 +2,8 @@ namespace InstaSharper.Classes.Models { - public class InstaUserShortList : List + public class InstaUserShortList : List, IInstaBaseList { + public string NextId { get; set; } } } \ No newline at end of file diff --git a/InstaSharper/Classes/PaginationParameters.cs b/InstaSharper/Classes/PaginationParameters.cs new file mode 100644 index 00000000..39e47d93 --- /dev/null +++ b/InstaSharper/Classes/PaginationParameters.cs @@ -0,0 +1,27 @@ +namespace InstaSharper.Classes +{ + public class PaginationParameters + { + private PaginationParameters() + { + } + + public string NextId { get; set; } = string.Empty; + public int MaximumPagesToLoad { get; private set; } + public int PagesLoaded { get; set; } + + public static PaginationParameters Empty => MaxPagesToLoad(int.MaxValue); + + public static PaginationParameters MaxPagesToLoad(int maxPagesToLoad) + { + return new PaginationParameters {MaximumPagesToLoad = maxPagesToLoad}; + } + + + public PaginationParameters StartFromId(string nextId) + { + NextId = nextId; + return this; + } + } +} \ No newline at end of file diff --git a/InstaSharper/Classes/PhoneVerificationSettings.cs b/InstaSharper/Classes/PhoneVerificationSettings.cs new file mode 100644 index 00000000..02a0dc75 --- /dev/null +++ b/InstaSharper/Classes/PhoneVerificationSettings.cs @@ -0,0 +1,19 @@ +using Newtonsoft.Json; + +namespace InstaSharper.Classes +{ + public class PhoneVerificationSettings + { + [JsonProperty("max_sms_count")] + public short MaxSmsCount { get; set; } + + [JsonProperty("resend_sms_delay_sec")] + public int ResendSmsDelaySeconds { get; set; } + + [JsonProperty("robocall_after_max_sms")] + public bool RobocallAfterMaxSms { get; set; } + + [JsonProperty("robocall_count_down_time")] + public int RobocallCountDownTime { get; set; } + } +} \ No newline at end of file diff --git a/InstaSharper/Classes/ResponseType.cs b/InstaSharper/Classes/ResponseType.cs index 038b3140..1f8e6ea3 100644 --- a/InstaSharper/Classes/ResponseType.cs +++ b/InstaSharper/Classes/ResponseType.cs @@ -10,6 +10,7 @@ public enum ResponseType OK = 5, WrongRequest = 6, SomePagesSkipped = 7, - UnExpectedResponse = 8 + UnExpectedResponse = 8, + InternalException = 9 } } \ No newline at end of file diff --git a/InstaSharper/Classes/ResponseWrappers/BadStatusResponse.cs b/InstaSharper/Classes/ResponseWrappers/BadStatusResponse.cs index a37c8d75..6ed2d481 100644 --- a/InstaSharper/Classes/ResponseWrappers/BadStatusResponse.cs +++ b/InstaSharper/Classes/ResponseWrappers/BadStatusResponse.cs @@ -3,7 +3,7 @@ namespace InstaSharper.Classes.ResponseWrappers { - internal class BadStatusResponse : BaseStatusResponse + public class BadStatusResponse : BaseStatusResponse { [JsonProperty("message")] public string Message { get; set; } diff --git a/InstaSharper/Classes/ResponseWrappers/BaseResponse/BaseLoadableResponse.cs b/InstaSharper/Classes/ResponseWrappers/BaseResponse/BaseLoadableResponse.cs index 36377921..1041e5e9 100644 --- a/InstaSharper/Classes/ResponseWrappers/BaseResponse/BaseLoadableResponse.cs +++ b/InstaSharper/Classes/ResponseWrappers/BaseResponse/BaseLoadableResponse.cs @@ -2,7 +2,7 @@ namespace InstaSharper.Classes.ResponseWrappers.BaseResponse { - internal class BaseLoadableResponse : BaseStatusResponse + public class BaseLoadableResponse : BaseStatusResponse { [JsonProperty("more_available")] public bool MoreAvailable { get; set; } diff --git a/InstaSharper/Classes/ResponseWrappers/DeleteResponse.cs b/InstaSharper/Classes/ResponseWrappers/DeleteResponse.cs index 05ed9e0a..0956d177 100644 --- a/InstaSharper/Classes/ResponseWrappers/DeleteResponse.cs +++ b/InstaSharper/Classes/ResponseWrappers/DeleteResponse.cs @@ -3,7 +3,7 @@ namespace InstaSharper.Classes.ResponseWrappers { - internal class DeleteResponse : BaseStatusResponse + public class DeleteResponse : BaseStatusResponse { [JsonProperty("did_delete")] public bool IsDeleted { get; set; } diff --git a/InstaSharper/Classes/ResponseWrappers/FollowedByResponse.cs b/InstaSharper/Classes/ResponseWrappers/FollowedByResponse.cs index 3b07d147..59b89014 100644 --- a/InstaSharper/Classes/ResponseWrappers/FollowedByResponse.cs +++ b/InstaSharper/Classes/ResponseWrappers/FollowedByResponse.cs @@ -2,7 +2,7 @@ namespace InstaSharper.Classes.ResponseWrappers { - internal class FollowedByResponse + public class FollowedByResponse { [JsonProperty("count")] public int Count { get; set; } diff --git a/InstaSharper/Classes/ResponseWrappers/IInstaRecipientsResponse.cs b/InstaSharper/Classes/ResponseWrappers/IInstaRecipientsResponse.cs index 16d48ebf..752e3b0c 100644 --- a/InstaSharper/Classes/ResponseWrappers/IInstaRecipientsResponse.cs +++ b/InstaSharper/Classes/ResponseWrappers/IInstaRecipientsResponse.cs @@ -1,6 +1,6 @@ namespace InstaSharper.Classes.ResponseWrappers { - internal interface IInstaRecipientsResponse + public interface IInstaRecipientsResponse { long Expires { get; set; } diff --git a/InstaSharper/Classes/ResponseWrappers/ImageResponse.cs b/InstaSharper/Classes/ResponseWrappers/ImageResponse.cs index c9a2a388..d824b36e 100644 --- a/InstaSharper/Classes/ResponseWrappers/ImageResponse.cs +++ b/InstaSharper/Classes/ResponseWrappers/ImageResponse.cs @@ -2,7 +2,7 @@ namespace InstaSharper.Classes.ResponseWrappers { - internal class ImageResponse + public class ImageResponse { [JsonProperty("url")] public string Url { get; set; } diff --git a/InstaSharper/Classes/ResponseWrappers/ImagesResponse.cs b/InstaSharper/Classes/ResponseWrappers/ImagesResponse.cs index a091cebf..c9ef487e 100644 --- a/InstaSharper/Classes/ResponseWrappers/ImagesResponse.cs +++ b/InstaSharper/Classes/ResponseWrappers/ImagesResponse.cs @@ -2,7 +2,7 @@ namespace InstaSharper.Classes.ResponseWrappers { - internal class ImagesResponse + public class ImagesResponse { [JsonProperty("low_resolution")] public ImageResponse LowResolution { get; set; } diff --git a/InstaSharper/Classes/ResponseWrappers/InstaCaptionResponse.cs b/InstaSharper/Classes/ResponseWrappers/InstaCaptionResponse.cs index 234348af..7771bf35 100644 --- a/InstaSharper/Classes/ResponseWrappers/InstaCaptionResponse.cs +++ b/InstaSharper/Classes/ResponseWrappers/InstaCaptionResponse.cs @@ -3,7 +3,7 @@ namespace InstaSharper.Classes.ResponseWrappers { - internal class InstaCaptionResponse : BaseStatusResponse + public class InstaCaptionResponse : BaseStatusResponse { [JsonProperty("user_id")] public long UserId { get; set; } diff --git a/InstaSharper/Classes/ResponseWrappers/InstaCarouselItemResponse.cs b/InstaSharper/Classes/ResponseWrappers/InstaCarouselItemResponse.cs index f9c4fac4..16684c41 100644 --- a/InstaSharper/Classes/ResponseWrappers/InstaCarouselItemResponse.cs +++ b/InstaSharper/Classes/ResponseWrappers/InstaCarouselItemResponse.cs @@ -3,7 +3,7 @@ namespace InstaSharper.Classes.ResponseWrappers { - internal class InstaCarouselItemResponse + public class InstaCarouselItemResponse { [JsonProperty("id")] public string InstaIdentifier { get; set; } diff --git a/InstaSharper/Classes/ResponseWrappers/InstaCarouselResponse.cs b/InstaSharper/Classes/ResponseWrappers/InstaCarouselResponse.cs index 70803cd4..767eb1dd 100644 --- a/InstaSharper/Classes/ResponseWrappers/InstaCarouselResponse.cs +++ b/InstaSharper/Classes/ResponseWrappers/InstaCarouselResponse.cs @@ -2,7 +2,7 @@ namespace InstaSharper.Classes.ResponseWrappers { - internal class InstaCarouselResponse : List + public class InstaCarouselResponse : List { } } \ No newline at end of file diff --git a/InstaSharper/Classes/ResponseWrappers/InstaChannelResponse.cs b/InstaSharper/Classes/ResponseWrappers/InstaChannelResponse.cs index 2c4b3784..8b7dd54f 100644 --- a/InstaSharper/Classes/ResponseWrappers/InstaChannelResponse.cs +++ b/InstaSharper/Classes/ResponseWrappers/InstaChannelResponse.cs @@ -2,7 +2,7 @@ namespace InstaSharper.Classes.ResponseWrappers { - internal class InstaChannelResponse + public class InstaChannelResponse { [JsonProperty("title")] public string Title { get; set; } diff --git a/InstaSharper/Classes/ResponseWrappers/InstaCollectionItemResponse.cs b/InstaSharper/Classes/ResponseWrappers/InstaCollectionItemResponse.cs new file mode 100644 index 00000000..a99c7290 --- /dev/null +++ b/InstaSharper/Classes/ResponseWrappers/InstaCollectionItemResponse.cs @@ -0,0 +1,23 @@ +using InstaSharper.Classes.ResponseWrappers.BaseResponse; +using Newtonsoft.Json; + +namespace InstaSharper.Classes.ResponseWrappers +{ + public class InstaCollectionItemResponse : BaseLoadableResponse + { + [JsonProperty("collection_id")] + public long CollectionId { get; set; } + + [JsonProperty("collection_name")] + public string CollectionName { get; set; } + + [JsonProperty("has_related_media")] + public bool HasRelatedMedia { get; set; } + + [JsonProperty("cover_media")] + public InstaCoverMediaResponse CoverMedia { get; set; } + + [JsonProperty("items")] + public InstaMediaListResponse Media { get; set; } + } +} \ No newline at end of file diff --git a/InstaSharper/Classes/ResponseWrappers/InstaCollectionsResponse.cs b/InstaSharper/Classes/ResponseWrappers/InstaCollectionsResponse.cs new file mode 100644 index 00000000..15e5cd0e --- /dev/null +++ b/InstaSharper/Classes/ResponseWrappers/InstaCollectionsResponse.cs @@ -0,0 +1,12 @@ +using System.Collections.Generic; +using InstaSharper.Classes.ResponseWrappers.BaseResponse; +using Newtonsoft.Json; + +namespace InstaSharper.Classes.ResponseWrappers +{ + public class InstaCollectionsResponse : BaseLoadableResponse + { + [JsonProperty("items")] + public List Items { get; set; } + } +} \ No newline at end of file diff --git a/InstaSharper/Classes/ResponseWrappers/InstaCommentListResponse.cs b/InstaSharper/Classes/ResponseWrappers/InstaCommentListResponse.cs index 0c25e21e..c62da1c4 100644 --- a/InstaSharper/Classes/ResponseWrappers/InstaCommentListResponse.cs +++ b/InstaSharper/Classes/ResponseWrappers/InstaCommentListResponse.cs @@ -4,7 +4,7 @@ namespace InstaSharper.Classes.ResponseWrappers { - internal class InstaCommentListResponse : BaseStatusResponse + public class InstaCommentListResponse : BaseStatusResponse { [JsonProperty("comment_count")] public int CommentsCount { get; set; } diff --git a/InstaSharper/Classes/ResponseWrappers/InstaCommentResponse.cs b/InstaSharper/Classes/ResponseWrappers/InstaCommentResponse.cs index cbbfbb05..a2e3ff69 100644 --- a/InstaSharper/Classes/ResponseWrappers/InstaCommentResponse.cs +++ b/InstaSharper/Classes/ResponseWrappers/InstaCommentResponse.cs @@ -2,7 +2,7 @@ namespace InstaSharper.Classes.ResponseWrappers { - internal class InstaCommentResponse + public class InstaCommentResponse { [JsonProperty("type")] public int Type { get; set; } diff --git a/InstaSharper/Classes/ResponseWrappers/InstaCoverMediaResponse.cs b/InstaSharper/Classes/ResponseWrappers/InstaCoverMediaResponse.cs new file mode 100644 index 00000000..3d4416b7 --- /dev/null +++ b/InstaSharper/Classes/ResponseWrappers/InstaCoverMediaResponse.cs @@ -0,0 +1,22 @@ +using Newtonsoft.Json; + +namespace InstaSharper.Classes.ResponseWrappers +{ + public class InstaCoverMediaResponse + { + [JsonProperty("id")] + public long Id { get; set; } + + [JsonProperty("image_versions2")] + public InstaImageCandidatesResponse ImageVersions { get; set; } + + [JsonProperty("media_type")] + public int MediaType { get; set; } + + [JsonProperty("original_height")] + public int OriginalHeight { get; set; } + + [JsonProperty("original_width")] + public int OriginalWidth { get; set; } + } +} \ No newline at end of file diff --git a/InstaSharper/Classes/ResponseWrappers/InstaCurrentUserResponse.cs b/InstaSharper/Classes/ResponseWrappers/InstaCurrentUserResponse.cs index c9f1a57e..26c5b56a 100644 --- a/InstaSharper/Classes/ResponseWrappers/InstaCurrentUserResponse.cs +++ b/InstaSharper/Classes/ResponseWrappers/InstaCurrentUserResponse.cs @@ -2,7 +2,7 @@ namespace InstaSharper.Classes.ResponseWrappers { - internal class InstaCurrentUserResponse : InstaUserShortResponse + public class InstaCurrentUserResponse : InstaUserShortResponse { [JsonProperty("has_anonymous_profile_picture")] public bool HasAnonymousProfilePicture { get; set; } diff --git a/InstaSharper/Classes/ResponseWrappers/InstaDirectInboxContainerResponse.cs b/InstaSharper/Classes/ResponseWrappers/InstaDirectInboxContainerResponse.cs index 51c353ba..827acbd4 100644 --- a/InstaSharper/Classes/ResponseWrappers/InstaDirectInboxContainerResponse.cs +++ b/InstaSharper/Classes/ResponseWrappers/InstaDirectInboxContainerResponse.cs @@ -4,7 +4,7 @@ namespace InstaSharper.Classes.ResponseWrappers { - internal class InstaDirectInboxContainerResponse : BaseStatusResponse + public class InstaDirectInboxContainerResponse : BaseStatusResponse { [JsonProperty("pending_requests_total")] public int PendingRequestsCount { get; set; } diff --git a/InstaSharper/Classes/ResponseWrappers/InstaDirectInboxItemResponse.cs b/InstaSharper/Classes/ResponseWrappers/InstaDirectInboxItemResponse.cs index 28d81abb..8375348d 100644 --- a/InstaSharper/Classes/ResponseWrappers/InstaDirectInboxItemResponse.cs +++ b/InstaSharper/Classes/ResponseWrappers/InstaDirectInboxItemResponse.cs @@ -4,11 +4,14 @@ namespace InstaSharper.Classes.ResponseWrappers { - internal class InstaDirectInboxItemResponse : BaseStatusResponse + public class InstaDirectInboxItemResponse : BaseStatusResponse { [JsonProperty("text")] public string Text { get; set; } + [JsonProperty("like")] + public string Like { get; set; } + [JsonProperty("user_id")] public long UserId { get; set; } @@ -24,6 +27,12 @@ internal class InstaDirectInboxItemResponse : BaseStatusResponse [JsonProperty("media_share")] public InstaMediaItemResponse MediaShare { get; set; } + [JsonProperty("media")] + public InstaInboxMediaResponse Media { get; set; } + + [JsonProperty("link")] + public InstaWebLinkResponse Link { get; set; } + [JsonProperty("client_context")] public Guid ClientContext { get; set; } } diff --git a/InstaSharper/Classes/ResponseWrappers/InstaDirectInboxResponse.cs b/InstaSharper/Classes/ResponseWrappers/InstaDirectInboxResponse.cs index 51c17d8f..689976f6 100644 --- a/InstaSharper/Classes/ResponseWrappers/InstaDirectInboxResponse.cs +++ b/InstaSharper/Classes/ResponseWrappers/InstaDirectInboxResponse.cs @@ -3,7 +3,7 @@ namespace InstaSharper.Classes.ResponseWrappers { - internal class InstaDirectInboxResponse + public class InstaDirectInboxResponse { [JsonProperty("has_older")] public bool HasOlder { get; set; } diff --git a/InstaSharper/Classes/ResponseWrappers/InstaDirectInboxSubscriptionResponse.cs b/InstaSharper/Classes/ResponseWrappers/InstaDirectInboxSubscriptionResponse.cs index 5a9573c7..7f0c234d 100644 --- a/InstaSharper/Classes/ResponseWrappers/InstaDirectInboxSubscriptionResponse.cs +++ b/InstaSharper/Classes/ResponseWrappers/InstaDirectInboxSubscriptionResponse.cs @@ -2,7 +2,7 @@ namespace InstaSharper.Classes.ResponseWrappers { - internal class InstaDirectInboxSubscriptionResponse + public class InstaDirectInboxSubscriptionResponse { [JsonProperty("topic")] public string Topic { get; set; } diff --git a/InstaSharper/Classes/ResponseWrappers/InstaDirectInboxThreadResponse.cs b/InstaSharper/Classes/ResponseWrappers/InstaDirectInboxThreadResponse.cs index 21302911..c01664b3 100644 --- a/InstaSharper/Classes/ResponseWrappers/InstaDirectInboxThreadResponse.cs +++ b/InstaSharper/Classes/ResponseWrappers/InstaDirectInboxThreadResponse.cs @@ -5,7 +5,7 @@ namespace InstaSharper.Classes.ResponseWrappers { - internal class InstaDirectInboxThreadResponse : BaseStatusResponse + public class InstaDirectInboxThreadResponse : BaseStatusResponse { [JsonProperty("muted")] public bool Muted { get; set; } diff --git a/InstaSharper/Classes/ResponseWrappers/InstaExploreFeedResponse.cs b/InstaSharper/Classes/ResponseWrappers/InstaExploreFeedResponse.cs index 4c6a41a7..30a6c88c 100644 --- a/InstaSharper/Classes/ResponseWrappers/InstaExploreFeedResponse.cs +++ b/InstaSharper/Classes/ResponseWrappers/InstaExploreFeedResponse.cs @@ -3,7 +3,7 @@ namespace InstaSharper.Classes.ResponseWrappers { - internal class InstaExploreFeedResponse : BaseLoadableResponse + public class InstaExploreFeedResponse : BaseLoadableResponse { [JsonIgnore] public InstaExploreItemsResponse Items { get; set; } = new InstaExploreItemsResponse(); diff --git a/InstaSharper/Classes/ResponseWrappers/InstaExploreItemsResponse.cs b/InstaSharper/Classes/ResponseWrappers/InstaExploreItemsResponse.cs index 5d104e58..c6d7d831 100644 --- a/InstaSharper/Classes/ResponseWrappers/InstaExploreItemsResponse.cs +++ b/InstaSharper/Classes/ResponseWrappers/InstaExploreItemsResponse.cs @@ -4,7 +4,7 @@ namespace InstaSharper.Classes.ResponseWrappers { - internal class InstaExploreItemsResponse : BaseLoadableResponse + public class InstaExploreItemsResponse : BaseLoadableResponse { [JsonIgnore] public InstaStoryTrayResponse StoryTray { get; set; } = new InstaStoryTrayResponse(); diff --git a/InstaSharper/Classes/ResponseWrappers/InstaFeedResponse.cs b/InstaSharper/Classes/ResponseWrappers/InstaFeedResponse.cs index 5f8e2c5e..2f2dfdb5 100644 --- a/InstaSharper/Classes/ResponseWrappers/InstaFeedResponse.cs +++ b/InstaSharper/Classes/ResponseWrappers/InstaFeedResponse.cs @@ -4,7 +4,7 @@ namespace InstaSharper.Classes.ResponseWrappers { - internal class InstaFeedResponse : BaseLoadableResponse + public class InstaFeedResponse : BaseLoadableResponse { [JsonProperty("is_direct_v2_enabled")] public bool IsDirectV2Enabled { get; set; } diff --git a/InstaSharper/Classes/ResponseWrappers/InstaFriendshipStatusResponse.cs b/InstaSharper/Classes/ResponseWrappers/InstaFriendshipStatusResponse.cs index 116dce27..9762950e 100644 --- a/InstaSharper/Classes/ResponseWrappers/InstaFriendshipStatusResponse.cs +++ b/InstaSharper/Classes/ResponseWrappers/InstaFriendshipStatusResponse.cs @@ -3,7 +3,7 @@ namespace InstaSharper.Classes.ResponseWrappers { - internal class InstaFriendshipStatusResponse : BaseStatusResponse + public class InstaFriendshipStatusResponse : BaseStatusResponse { [JsonProperty("following")] public bool Following { get; set; } diff --git a/InstaSharper/Classes/ResponseWrappers/InstaHashtagResponse.cs b/InstaSharper/Classes/ResponseWrappers/InstaHashtagResponse.cs index b8955de3..86feca73 100644 --- a/InstaSharper/Classes/ResponseWrappers/InstaHashtagResponse.cs +++ b/InstaSharper/Classes/ResponseWrappers/InstaHashtagResponse.cs @@ -2,7 +2,7 @@ namespace InstaSharper.Classes.ResponseWrappers { - internal class InstaHashtagResponse + public class InstaHashtagResponse { [JsonProperty("id")] public long Id { get; set; } diff --git a/InstaSharper/Classes/ResponseWrappers/InstaImageCandidatesResponse.cs b/InstaSharper/Classes/ResponseWrappers/InstaImageCandidatesResponse.cs index 19ea185f..403fffe0 100644 --- a/InstaSharper/Classes/ResponseWrappers/InstaImageCandidatesResponse.cs +++ b/InstaSharper/Classes/ResponseWrappers/InstaImageCandidatesResponse.cs @@ -3,7 +3,7 @@ namespace InstaSharper.Classes.ResponseWrappers { - internal class InstaImageCandidatesResponse + public class InstaImageCandidatesResponse { [JsonProperty("candidates")] public List Candidates { get; set; } diff --git a/InstaSharper/Classes/ResponseWrappers/InstaInboxMediaResponse.cs b/InstaSharper/Classes/ResponseWrappers/InstaInboxMediaResponse.cs new file mode 100644 index 00000000..292ec3d2 --- /dev/null +++ b/InstaSharper/Classes/ResponseWrappers/InstaInboxMediaResponse.cs @@ -0,0 +1,20 @@ +using InstaSharper.Classes.Models; +using Newtonsoft.Json; + +namespace InstaSharper.Classes.ResponseWrappers +{ + public class InstaInboxMediaResponse + { + [JsonProperty("image_versions2")] + public InstaImageCandidatesResponse ImageCandidates { get; set; } + + [JsonProperty("original_width")] + public long OriginalWidth { get; set; } + + [JsonProperty("original_height")] + public long OriginalHeight { get; set; } + + [JsonProperty("media_type")] + public InstaMediaType MediaType { get; set; } + } +} \ No newline at end of file diff --git a/InstaSharper/Classes/ResponseWrappers/InstaInlineFollowResponse.cs b/InstaSharper/Classes/ResponseWrappers/InstaInlineFollowResponse.cs index 5a15d3f7..26945b8c 100644 --- a/InstaSharper/Classes/ResponseWrappers/InstaInlineFollowResponse.cs +++ b/InstaSharper/Classes/ResponseWrappers/InstaInlineFollowResponse.cs @@ -2,7 +2,7 @@ namespace InstaSharper.Classes.ResponseWrappers { - internal class InstaInlineFollowResponse + public class InstaInlineFollowResponse { [JsonProperty("outgoing_request")] public bool IsOutgoingRequest { get; set; } diff --git a/InstaSharper/Classes/ResponseWrappers/InstaLinkResponse.cs b/InstaSharper/Classes/ResponseWrappers/InstaLinkResponse.cs index b4aa1b3e..d2ac3ddd 100644 --- a/InstaSharper/Classes/ResponseWrappers/InstaLinkResponse.cs +++ b/InstaSharper/Classes/ResponseWrappers/InstaLinkResponse.cs @@ -2,7 +2,7 @@ namespace InstaSharper.Classes.ResponseWrappers { - internal class InstaLinkResponse + public class InstaLinkResponse { [JsonProperty("type")] public string Type { get; set; } diff --git a/InstaSharper/Classes/ResponseWrappers/InstaLocationFeedResponse.cs b/InstaSharper/Classes/ResponseWrappers/InstaLocationFeedResponse.cs new file mode 100644 index 00000000..e542adf9 --- /dev/null +++ b/InstaSharper/Classes/ResponseWrappers/InstaLocationFeedResponse.cs @@ -0,0 +1,24 @@ +using System.Collections.Generic; +using InstaSharper.Classes.ResponseWrappers.BaseResponse; +using Newtonsoft.Json; + +namespace InstaSharper.Classes.ResponseWrappers +{ + public class InstaLocationFeedResponse : BaseLoadableResponse + { + [JsonProperty("ranked_items")] + public List RankedItems { get; set; } = new List(); + + [JsonProperty("items")] + public List Items { get; set; } = new List(); + + [JsonProperty("story")] + public InstaStoryResponse Story { get; set; } + + [JsonProperty("media_count")] + public long MediaCount { get; set; } + + [JsonProperty("location")] + public InstaLocationResponse Location { get; set; } + } +} \ No newline at end of file diff --git a/InstaSharper/Classes/ResponseWrappers/InstaLocationResponse.cs b/InstaSharper/Classes/ResponseWrappers/InstaLocationResponse.cs index 0579684a..03f4da53 100644 --- a/InstaSharper/Classes/ResponseWrappers/InstaLocationResponse.cs +++ b/InstaSharper/Classes/ResponseWrappers/InstaLocationResponse.cs @@ -2,7 +2,7 @@ namespace InstaSharper.Classes.ResponseWrappers { - internal class InstaLocationResponse + public class InstaLocationResponse : InstaLocationShortResponse { [JsonProperty("facebook_places_id")] public long FacebookPlacesId { get; set; } @@ -10,24 +10,9 @@ internal class InstaLocationResponse [JsonProperty("city")] public string City { get; set; } - [JsonProperty("address")] - public string Address { get; set; } - - [JsonProperty("external_source")] - public string ExternalSource { get; set; } - - [JsonProperty("lng")] - public double Lng { get; set; } - [JsonProperty("pk")] public long Pk { get; set; } - [JsonProperty("lat")] - public double Lat { get; set; } - - [JsonProperty("name")] - public string Name { get; set; } - [JsonProperty("short_name")] public string ShortName { get; set; } } diff --git a/InstaSharper/Classes/ResponseWrappers/InstaLocationSearchResponse.cs b/InstaSharper/Classes/ResponseWrappers/InstaLocationSearchResponse.cs new file mode 100644 index 00000000..1f887456 --- /dev/null +++ b/InstaSharper/Classes/ResponseWrappers/InstaLocationSearchResponse.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace InstaSharper.Classes.ResponseWrappers +{ + public class InstaLocationSearchResponse + { + [JsonProperty("venues")] + public List Locations { get; set; } + + [JsonProperty("request_id")] + public string RequestId { get; set; } + + [JsonProperty("status")] + public string Status { get; set; } + } +} \ No newline at end of file diff --git a/InstaSharper/Classes/ResponseWrappers/InstaLocationShortResponse.cs b/InstaSharper/Classes/ResponseWrappers/InstaLocationShortResponse.cs new file mode 100644 index 00000000..2d9d890a --- /dev/null +++ b/InstaSharper/Classes/ResponseWrappers/InstaLocationShortResponse.cs @@ -0,0 +1,25 @@ +using Newtonsoft.Json; + +namespace InstaSharper.Classes.ResponseWrappers +{ + public class InstaLocationShortResponse + { + [JsonProperty("lat")] + public double Lat { get; set; } + + [JsonProperty("lng")] + public double Lng { get; set; } + + [JsonProperty("address")] + public string Address { get; set; } + + [JsonProperty("external_id")] + public string ExternalId { get; set; } + + [JsonProperty("external_id_source")] + public string ExternalIdSource { get; set; } + + [JsonProperty("name")] + public string Name { get; set; } + } +} \ No newline at end of file diff --git a/InstaSharper/Classes/ResponseWrappers/InstaLoginResponse.cs b/InstaSharper/Classes/ResponseWrappers/InstaLoginResponse.cs index d86cac3d..c16d8393 100644 --- a/InstaSharper/Classes/ResponseWrappers/InstaLoginResponse.cs +++ b/InstaSharper/Classes/ResponseWrappers/InstaLoginResponse.cs @@ -2,7 +2,7 @@ namespace InstaSharper.Classes.ResponseWrappers { - internal class InstaLoginResponse + public class InstaLoginResponse { [JsonProperty("status")] public string Status { get; set; } diff --git a/InstaSharper/Classes/ResponseWrappers/InstaMediaItemResponse.cs b/InstaSharper/Classes/ResponseWrappers/InstaMediaItemResponse.cs index 122042a8..58e73a62 100644 --- a/InstaSharper/Classes/ResponseWrappers/InstaMediaItemResponse.cs +++ b/InstaSharper/Classes/ResponseWrappers/InstaMediaItemResponse.cs @@ -4,7 +4,7 @@ namespace InstaSharper.Classes.ResponseWrappers { - internal class InstaMediaItemResponse + public class InstaMediaItemResponse { [JsonProperty("taken_at")] public string TakenAtUnixLike { get; set; } @@ -21,7 +21,6 @@ internal class InstaMediaItemResponse [JsonProperty("media_type")] public InstaMediaType MediaType { get; set; } - [JsonProperty("code")] public string Code { get; set; } @@ -81,5 +80,11 @@ internal class InstaMediaItemResponse [JsonProperty("carousel_media")] public InstaCarouselResponse CarouselMedia { get; set; } + + [JsonProperty("location")] + public InstaLocationResponse Location { get; set; } + + [JsonProperty("preview_comments")] + public List PreviewComments { get; set; } } } \ No newline at end of file diff --git a/InstaSharper/Classes/ResponseWrappers/InstaMediaLikersResponse.cs b/InstaSharper/Classes/ResponseWrappers/InstaMediaLikersResponse.cs index 12062a7b..c878b9b5 100644 --- a/InstaSharper/Classes/ResponseWrappers/InstaMediaLikersResponse.cs +++ b/InstaSharper/Classes/ResponseWrappers/InstaMediaLikersResponse.cs @@ -3,7 +3,7 @@ namespace InstaSharper.Classes.ResponseWrappers { - internal class InstaMediaLikersResponse : BadStatusResponse + public class InstaMediaLikersResponse : BadStatusResponse { [JsonProperty("users")] public List Users { get; set; } diff --git a/InstaSharper/Classes/ResponseWrappers/InstaMediaListResponse.cs b/InstaSharper/Classes/ResponseWrappers/InstaMediaListResponse.cs index 47d5a147..8d5192d3 100644 --- a/InstaSharper/Classes/ResponseWrappers/InstaMediaListResponse.cs +++ b/InstaSharper/Classes/ResponseWrappers/InstaMediaListResponse.cs @@ -4,7 +4,7 @@ namespace InstaSharper.Classes.ResponseWrappers { - internal class InstaMediaListResponse : BaseLoadableResponse + public class InstaMediaListResponse : BaseLoadableResponse { [JsonProperty("items")] public List Medias { get; set; } = new List(); diff --git a/InstaSharper/Classes/ResponseWrappers/InstaOembedUrlResponse.cs b/InstaSharper/Classes/ResponseWrappers/InstaOembedUrlResponse.cs new file mode 100644 index 00000000..4f910963 --- /dev/null +++ b/InstaSharper/Classes/ResponseWrappers/InstaOembedUrlResponse.cs @@ -0,0 +1,10 @@ +using Newtonsoft.Json; + +namespace InstaSharper.Classes.ResponseWrappers +{ + public class InstaOembedUrlResponse + { + [JsonProperty("media_id")] //media_id is enough. + public string MediaId { get; set; } + } +} \ No newline at end of file diff --git a/InstaSharper/Classes/ResponseWrappers/InstaPermalinkResponse.cs b/InstaSharper/Classes/ResponseWrappers/InstaPermalinkResponse.cs new file mode 100644 index 00000000..2eb949f6 --- /dev/null +++ b/InstaSharper/Classes/ResponseWrappers/InstaPermalinkResponse.cs @@ -0,0 +1,11 @@ +using InstaSharper.Classes.ResponseWrappers.BaseResponse; +using Newtonsoft.Json; + +namespace InstaSharper.Classes.ResponseWrappers +{ + public class InstaPermalinkResponse : BaseStatusResponse + { + [JsonProperty("permalink")] + public string Permalink { get; set; } + } +} \ No newline at end of file diff --git a/InstaSharper/Classes/ResponseWrappers/InstaRankedRecipientsResponse.cs b/InstaSharper/Classes/ResponseWrappers/InstaRankedRecipientsResponse.cs index a46d7513..c3a96cf9 100644 --- a/InstaSharper/Classes/ResponseWrappers/InstaRankedRecipientsResponse.cs +++ b/InstaSharper/Classes/ResponseWrappers/InstaRankedRecipientsResponse.cs @@ -2,7 +2,7 @@ namespace InstaSharper.Classes.ResponseWrappers { - internal class InstaRankedRecipientsResponse : InstaRecipientsResponse, IInstaRecipientsResponse + public class InstaRankedRecipientsResponse : InstaRecipientsResponse, IInstaRecipientsResponse { [JsonProperty("ranked_recipients")] public RankedRecipientResponse[] RankedRecipients { get; set; } diff --git a/InstaSharper/Classes/ResponseWrappers/InstaRecentActivityFeedResponse.cs b/InstaSharper/Classes/ResponseWrappers/InstaRecentActivityFeedResponse.cs index 9d7b357a..ba350fb4 100644 --- a/InstaSharper/Classes/ResponseWrappers/InstaRecentActivityFeedResponse.cs +++ b/InstaSharper/Classes/ResponseWrappers/InstaRecentActivityFeedResponse.cs @@ -2,7 +2,7 @@ namespace InstaSharper.Classes.ResponseWrappers { - internal class InstaRecentActivityFeedResponse + public class InstaRecentActivityFeedResponse { [JsonProperty("args")] public InstaRecentActivityStoryItemResponse Args { get; set; } diff --git a/InstaSharper/Classes/ResponseWrappers/InstaRecentActivityResponse.cs b/InstaSharper/Classes/ResponseWrappers/InstaRecentActivityResponse.cs index 18fb5a23..702d2270 100644 --- a/InstaSharper/Classes/ResponseWrappers/InstaRecentActivityResponse.cs +++ b/InstaSharper/Classes/ResponseWrappers/InstaRecentActivityResponse.cs @@ -4,7 +4,7 @@ namespace InstaSharper.Classes.ResponseWrappers { - internal class InstaRecentActivityResponse : BaseLoadableResponse + public class InstaRecentActivityResponse : BaseLoadableResponse { public bool IsOwnActivity { get; set; } = false; diff --git a/InstaSharper/Classes/ResponseWrappers/InstaRecentActivityStoryItemResponse.cs b/InstaSharper/Classes/ResponseWrappers/InstaRecentActivityStoryItemResponse.cs index dabe325d..7c6ab6a0 100644 --- a/InstaSharper/Classes/ResponseWrappers/InstaRecentActivityStoryItemResponse.cs +++ b/InstaSharper/Classes/ResponseWrappers/InstaRecentActivityStoryItemResponse.cs @@ -3,7 +3,7 @@ namespace InstaSharper.Classes.ResponseWrappers { - internal class InstaRecentActivityStoryItemResponse + public class InstaRecentActivityStoryItemResponse { [JsonProperty("profile_id")] public long ProfileId { get; set; } diff --git a/InstaSharper/Classes/ResponseWrappers/InstaRecentRecipientsResponse.cs b/InstaSharper/Classes/ResponseWrappers/InstaRecentRecipientsResponse.cs index af129245..8f622d86 100644 --- a/InstaSharper/Classes/ResponseWrappers/InstaRecentRecipientsResponse.cs +++ b/InstaSharper/Classes/ResponseWrappers/InstaRecentRecipientsResponse.cs @@ -2,7 +2,7 @@ namespace InstaSharper.Classes.ResponseWrappers { - internal class InstaRecentRecipientsResponse : InstaRecipientsResponse, IInstaRecipientsResponse + public class InstaRecentRecipientsResponse : InstaRecipientsResponse, IInstaRecipientsResponse { [JsonProperty("recent_recipients")] public RankedRecipientResponse[] RankedRecipients { get; set; } diff --git a/InstaSharper/Classes/ResponseWrappers/InstaRecipientsResponse.cs b/InstaSharper/Classes/ResponseWrappers/InstaRecipientsResponse.cs index 6774a321..c5ebcb16 100644 --- a/InstaSharper/Classes/ResponseWrappers/InstaRecipientsResponse.cs +++ b/InstaSharper/Classes/ResponseWrappers/InstaRecipientsResponse.cs @@ -3,7 +3,7 @@ namespace InstaSharper.Classes.ResponseWrappers { - internal class InstaRecipientsResponse : BaseStatusResponse + public class InstaRecipientsResponse : BaseStatusResponse { [JsonProperty("expires")] public long Expires { get; set; } diff --git a/InstaSharper/Classes/ResponseWrappers/InstaReelMentionResponse.cs b/InstaSharper/Classes/ResponseWrappers/InstaReelMentionResponse.cs index 2b54bf45..e1e0cf15 100644 --- a/InstaSharper/Classes/ResponseWrappers/InstaReelMentionResponse.cs +++ b/InstaSharper/Classes/ResponseWrappers/InstaReelMentionResponse.cs @@ -2,7 +2,7 @@ namespace InstaSharper.Classes.ResponseWrappers { - internal class InstaReelMentionResponse + public class InstaReelMentionResponse { [JsonProperty("rotation")] public double Rotation { get; set; } diff --git a/InstaSharper/Classes/ResponseWrappers/InstaSearchUserResponse.cs b/InstaSharper/Classes/ResponseWrappers/InstaSearchUserResponse.cs index fd478469..50f8718b 100644 --- a/InstaSharper/Classes/ResponseWrappers/InstaSearchUserResponse.cs +++ b/InstaSharper/Classes/ResponseWrappers/InstaSearchUserResponse.cs @@ -3,7 +3,7 @@ namespace InstaSharper.Classes.ResponseWrappers { - internal class InstaSearchUserResponse + public class InstaSearchUserResponse { [JsonProperty("has_more")] public bool MoreAvailable { get; set; } diff --git a/InstaSharper/Classes/ResponseWrappers/InstaSendDirectMessageResponse.cs b/InstaSharper/Classes/ResponseWrappers/InstaSendDirectMessageResponse.cs index 7d75fccb..16049e7b 100644 --- a/InstaSharper/Classes/ResponseWrappers/InstaSendDirectMessageResponse.cs +++ b/InstaSharper/Classes/ResponseWrappers/InstaSendDirectMessageResponse.cs @@ -3,7 +3,7 @@ namespace InstaSharper.Classes.ResponseWrappers { - internal class InstaSendDirectMessageResponse : BaseStatusResponse + public class InstaSendDirectMessageResponse : BaseStatusResponse { public List Threads { get; set; } = new List(); } diff --git a/InstaSharper/Classes/ResponseWrappers/InstaStoryFeedResponse.cs b/InstaSharper/Classes/ResponseWrappers/InstaStoryFeedResponse.cs index ac394b3c..aff2e2a2 100644 --- a/InstaSharper/Classes/ResponseWrappers/InstaStoryFeedResponse.cs +++ b/InstaSharper/Classes/ResponseWrappers/InstaStoryFeedResponse.cs @@ -4,7 +4,7 @@ namespace InstaSharper.Classes.ResponseWrappers { - internal class InstaStoryFeedResponse : BaseStatusResponse + public class InstaStoryFeedResponse : BaseStatusResponse { [JsonProperty("face_filter_nux_version")] public int FaceFilterNuxVersion { get; set; } diff --git a/InstaSharper/Classes/ResponseWrappers/InstaStoryItemResponse.cs b/InstaSharper/Classes/ResponseWrappers/InstaStoryItemResponse.cs index cc2a5ea0..ca1f9165 100644 --- a/InstaSharper/Classes/ResponseWrappers/InstaStoryItemResponse.cs +++ b/InstaSharper/Classes/ResponseWrappers/InstaStoryItemResponse.cs @@ -3,7 +3,7 @@ namespace InstaSharper.Classes.ResponseWrappers { - internal class InstaStoryItemResponse + public class InstaStoryItemResponse { [JsonProperty("has_liked")] public bool HasLiked { get; set; } diff --git a/InstaSharper/Classes/ResponseWrappers/InstaStoryLocationResponse.cs b/InstaSharper/Classes/ResponseWrappers/InstaStoryLocationResponse.cs index 3b46795b..59590d2a 100644 --- a/InstaSharper/Classes/ResponseWrappers/InstaStoryLocationResponse.cs +++ b/InstaSharper/Classes/ResponseWrappers/InstaStoryLocationResponse.cs @@ -2,7 +2,7 @@ namespace InstaSharper.Classes.ResponseWrappers { - internal class InstaStoryLocationResponse + public class InstaStoryLocationResponse { [JsonProperty("rotation")] public long Rotation { get; set; } diff --git a/InstaSharper/Classes/ResponseWrappers/InstaStoryMediaResponse.cs b/InstaSharper/Classes/ResponseWrappers/InstaStoryMediaResponse.cs index 34241572..c625dfc1 100644 --- a/InstaSharper/Classes/ResponseWrappers/InstaStoryMediaResponse.cs +++ b/InstaSharper/Classes/ResponseWrappers/InstaStoryMediaResponse.cs @@ -2,7 +2,7 @@ namespace InstaSharper.Classes.ResponseWrappers { - internal class InstaStoryMediaResponse + public class InstaStoryMediaResponse { [JsonProperty("media")] public InstaStoryItemResponse Media { get; set; } diff --git a/InstaSharper/Classes/ResponseWrappers/InstaStoryResponse.cs b/InstaSharper/Classes/ResponseWrappers/InstaStoryResponse.cs index e530b9be..00b95341 100644 --- a/InstaSharper/Classes/ResponseWrappers/InstaStoryResponse.cs +++ b/InstaSharper/Classes/ResponseWrappers/InstaStoryResponse.cs @@ -3,7 +3,7 @@ namespace InstaSharper.Classes.ResponseWrappers { - internal class InstaStoryResponse + public class InstaStoryResponse { [JsonProperty("can_reply")] public bool CanReply { get; set; } @@ -15,13 +15,13 @@ internal class InstaStoryResponse public InstaUserShortResponse User { get; set; } [JsonProperty("owner")] - public InstaOwnerResponse Owner { get; set; } + public InstaUserShortResponse Owner { get; set; } [JsonProperty("source_token")] public string SourceToken { get; set; } [JsonProperty("seen")] - public double? Seen { get; set; } + public long? Seen { get; set; } [JsonProperty("latest_reel_media")] public string LatestReelMedia { get; set; } diff --git a/InstaSharper/Classes/ResponseWrappers/InstaStoryTrayResponse.cs b/InstaSharper/Classes/ResponseWrappers/InstaStoryTrayResponse.cs index b587a1ba..1b17b2e2 100644 --- a/InstaSharper/Classes/ResponseWrappers/InstaStoryTrayResponse.cs +++ b/InstaSharper/Classes/ResponseWrappers/InstaStoryTrayResponse.cs @@ -3,7 +3,7 @@ namespace InstaSharper.Classes.ResponseWrappers { - internal class InstaStoryTrayResponse + public class InstaStoryTrayResponse { [JsonProperty("id")] public long Id { get; set; } diff --git a/InstaSharper/Classes/ResponseWrappers/InstaTagFeedResponse.cs b/InstaSharper/Classes/ResponseWrappers/InstaTagFeedResponse.cs index 69b0a65b..a861532d 100644 --- a/InstaSharper/Classes/ResponseWrappers/InstaTagFeedResponse.cs +++ b/InstaSharper/Classes/ResponseWrappers/InstaTagFeedResponse.cs @@ -3,7 +3,7 @@ namespace InstaSharper.Classes.ResponseWrappers { - internal class InstaTagFeedResponse : InstaMediaListResponse + public class InstaTagFeedResponse : InstaMediaListResponse { [JsonProperty("ranked_items")] public List RankedItems { get; set; } = new List(); diff --git a/InstaSharper/Classes/ResponseWrappers/InstaTopLiveResponse.cs b/InstaSharper/Classes/ResponseWrappers/InstaTopLiveResponse.cs index d76963bb..0bdacc7d 100644 --- a/InstaSharper/Classes/ResponseWrappers/InstaTopLiveResponse.cs +++ b/InstaSharper/Classes/ResponseWrappers/InstaTopLiveResponse.cs @@ -3,7 +3,7 @@ namespace InstaSharper.Classes.ResponseWrappers { - internal class InstaTopLiveResponse + public class InstaTopLiveResponse { [JsonProperty("ranked_position")] public int RankedPosition { get; set; } diff --git a/InstaSharper/Classes/ResponseWrappers/InstaUserListResponse.cs b/InstaSharper/Classes/ResponseWrappers/InstaUserListResponse.cs index 5d096455..12d47174 100644 --- a/InstaSharper/Classes/ResponseWrappers/InstaUserListResponse.cs +++ b/InstaSharper/Classes/ResponseWrappers/InstaUserListResponse.cs @@ -4,7 +4,7 @@ namespace InstaSharper.Classes.ResponseWrappers { - internal class InstaUserListResponse : BaseStatusResponse + public class InstaUserListResponse : BaseStatusResponse { [JsonProperty("users")] public List Items { get; set; } diff --git a/InstaSharper/Classes/ResponseWrappers/InstaUserListShortResponse.cs b/InstaSharper/Classes/ResponseWrappers/InstaUserListShortResponse.cs index 8a149baa..98199245 100644 --- a/InstaSharper/Classes/ResponseWrappers/InstaUserListShortResponse.cs +++ b/InstaSharper/Classes/ResponseWrappers/InstaUserListShortResponse.cs @@ -4,7 +4,7 @@ namespace InstaSharper.Classes.ResponseWrappers { - internal class InstaUserListShortResponse : BaseStatusResponse + public class InstaUserListShortResponse : BaseStatusResponse { [JsonProperty("users")] public List Items { get; set; } diff --git a/InstaSharper/Classes/ResponseWrappers/InstaUserResponse.cs b/InstaSharper/Classes/ResponseWrappers/InstaUserResponse.cs index 05a7abe8..80bdd6d1 100644 --- a/InstaSharper/Classes/ResponseWrappers/InstaUserResponse.cs +++ b/InstaSharper/Classes/ResponseWrappers/InstaUserResponse.cs @@ -2,7 +2,7 @@ namespace InstaSharper.Classes.ResponseWrappers { - internal class InstaUserResponse : InstaUserShortResponse + public class InstaUserResponse : InstaUserShortResponse { [JsonProperty("friendship_status")] public InstaFriendshipStatusResponse FriendshipStatus { get; set; } diff --git a/InstaSharper/Classes/ResponseWrappers/InstaUserShortResponse.cs b/InstaSharper/Classes/ResponseWrappers/InstaUserShortResponse.cs index ba330c27..473a9445 100644 --- a/InstaSharper/Classes/ResponseWrappers/InstaUserShortResponse.cs +++ b/InstaSharper/Classes/ResponseWrappers/InstaUserShortResponse.cs @@ -3,7 +3,7 @@ namespace InstaSharper.Classes.ResponseWrappers { - internal class InstaUserShortResponse : BaseStatusResponse + public class InstaUserShortResponse : BaseStatusResponse { [JsonProperty("username")] public string UserName { get; set; } @@ -24,6 +24,6 @@ internal class InstaUserShortResponse : BaseStatusResponse public bool IsPrivate { get; set; } [JsonProperty("pk")] - public string Pk { get; set; } + public long Pk { get; set; } } } \ No newline at end of file diff --git a/InstaSharper/Classes/ResponseWrappers/InstaUserTagListResponse.cs b/InstaSharper/Classes/ResponseWrappers/InstaUserTagListResponse.cs index a5c0ffc1..cbd960b3 100644 --- a/InstaSharper/Classes/ResponseWrappers/InstaUserTagListResponse.cs +++ b/InstaSharper/Classes/ResponseWrappers/InstaUserTagListResponse.cs @@ -3,7 +3,7 @@ namespace InstaSharper.Classes.ResponseWrappers { - internal class InstaUserTagListResponse + public class InstaUserTagListResponse { [JsonProperty("in")] public List In { get; set; } = new List(); diff --git a/InstaSharper/Classes/ResponseWrappers/InstaUserTagResponse.cs b/InstaSharper/Classes/ResponseWrappers/InstaUserTagResponse.cs index 89006efe..d57b238c 100644 --- a/InstaSharper/Classes/ResponseWrappers/InstaUserTagResponse.cs +++ b/InstaSharper/Classes/ResponseWrappers/InstaUserTagResponse.cs @@ -2,7 +2,7 @@ namespace InstaSharper.Classes.ResponseWrappers { - internal class InstaUserTagResponse + public class InstaUserTagResponse { [JsonProperty("position")] public double[] Position { get; set; } diff --git a/InstaSharper/Classes/ResponseWrappers/InstaVideoResponse.cs b/InstaSharper/Classes/ResponseWrappers/InstaVideoResponse.cs index 483eeb0b..6f394ace 100644 --- a/InstaSharper/Classes/ResponseWrappers/InstaVideoResponse.cs +++ b/InstaSharper/Classes/ResponseWrappers/InstaVideoResponse.cs @@ -2,7 +2,7 @@ namespace InstaSharper.Classes.ResponseWrappers { - internal class InstaVideoResponse + public class InstaVideoResponse { [JsonProperty("id")] public string Id { get; set; } diff --git a/InstaSharper/Classes/ResponseWrappers/InstaWebLinkContextResponse.cs b/InstaSharper/Classes/ResponseWrappers/InstaWebLinkContextResponse.cs new file mode 100644 index 00000000..c94e33d0 --- /dev/null +++ b/InstaSharper/Classes/ResponseWrappers/InstaWebLinkContextResponse.cs @@ -0,0 +1,19 @@ +using Newtonsoft.Json; + +namespace InstaSharper.Classes.ResponseWrappers +{ + public class InstaWebLinkContextResponse + { + [JsonProperty("link_url")] + public string LinkUrl { get; set; } + + [JsonProperty("link_title")] + public string LinkTitle { get; set; } + + [JsonProperty("link_summary")] + public string LinkSummary { get; set; } + + [JsonProperty("link_image_url")] + public string LinkImageUrl { get; set; } + } +} \ No newline at end of file diff --git a/InstaSharper/Classes/ResponseWrappers/InstaWebLinkResponse.cs b/InstaSharper/Classes/ResponseWrappers/InstaWebLinkResponse.cs new file mode 100644 index 00000000..b8e644c1 --- /dev/null +++ b/InstaSharper/Classes/ResponseWrappers/InstaWebLinkResponse.cs @@ -0,0 +1,13 @@ +using Newtonsoft.Json; + +namespace InstaSharper.Classes.ResponseWrappers +{ + public class InstaWebLinkResponse + { + [JsonProperty("text")] + public string Text { get; set; } + + [JsonProperty("link_context")] + public InstaWebLinkContextResponse LinkContext { get; set; } + } +} \ No newline at end of file diff --git a/InstaSharper/Classes/ResponseWrappers/InsteReelFeedResponse.cs b/InstaSharper/Classes/ResponseWrappers/InsteReelFeedResponse.cs index 026de411..f4326fde 100644 --- a/InstaSharper/Classes/ResponseWrappers/InsteReelFeedResponse.cs +++ b/InstaSharper/Classes/ResponseWrappers/InsteReelFeedResponse.cs @@ -3,7 +3,7 @@ namespace InstaSharper.Classes.ResponseWrappers { - internal class InsteReelFeedResponse + public class InsteReelFeedResponse { [JsonProperty("has_besties_media")] public long HasBestiesMedia { get; set; } diff --git a/InstaSharper/Classes/ResponseWrappers/RankedRecipientResponse.cs b/InstaSharper/Classes/ResponseWrappers/RankedRecipientResponse.cs index e0231ac2..bdaea3e4 100644 --- a/InstaSharper/Classes/ResponseWrappers/RankedRecipientResponse.cs +++ b/InstaSharper/Classes/ResponseWrappers/RankedRecipientResponse.cs @@ -2,9 +2,12 @@ namespace InstaSharper.Classes.ResponseWrappers { - internal class RankedRecipientResponse + public class RankedRecipientResponse { [JsonProperty("thread")] public RankedRecipientThreadResponse Thread { get; set; } + + [JsonProperty("user")] + public InstaUserShortResponse User { get; set; } } } \ No newline at end of file diff --git a/InstaSharper/Classes/ResponseWrappers/RankedRecipientThreadResponse.cs b/InstaSharper/Classes/ResponseWrappers/RankedRecipientThreadResponse.cs index e08b16fa..99e59c57 100644 --- a/InstaSharper/Classes/ResponseWrappers/RankedRecipientThreadResponse.cs +++ b/InstaSharper/Classes/ResponseWrappers/RankedRecipientThreadResponse.cs @@ -2,7 +2,7 @@ namespace InstaSharper.Classes.ResponseWrappers { - internal class RankedRecipientThreadResponse + public class RankedRecipientThreadResponse { [JsonProperty("canonical")] public bool Canonical { get; set; } diff --git a/InstaSharper/Classes/Result.cs b/InstaSharper/Classes/Result.cs index 4a3dbea1..c4792e0f 100644 --- a/InstaSharper/Classes/Result.cs +++ b/InstaSharper/Classes/Result.cs @@ -57,6 +57,11 @@ public static IResult Fail(string errMsg, T resValue) return new Result(false, resValue, new ResultInfo(errMsg)); } + public static IResult Fail(Exception exception, T resValue) + { + return new Result(false, resValue, new ResultInfo(exception)); + } + public static IResult Fail(ResultInfo info, T resValue) { return new Result(false, resValue, info); @@ -94,6 +99,7 @@ public static IResult UnExpectedResponse(HttpResponseMessage response, str responseType = ResponseType.SentryBlock; break; } + var resultInfo = new ResultInfo(responseType, status.Message); return new Result(false, default(T), resultInfo); } diff --git a/InstaSharper/Classes/ResultInfo.cs b/InstaSharper/Classes/ResultInfo.cs index 824afb52..8c3f5b05 100644 --- a/InstaSharper/Classes/ResultInfo.cs +++ b/InstaSharper/Classes/ResultInfo.cs @@ -12,6 +12,8 @@ public ResultInfo(string message) public ResultInfo(Exception exception) { Exception = exception; + Message = exception?.Message; + ResponseType = ResponseType.InternalException; } public ResultInfo(ResponseType responseType, string errorMessage) @@ -28,9 +30,7 @@ public ResultInfo(ResponseType responseType, string errorMessage) public override string ToString() { - var message = $"{ResponseType.ToString()}: {Message}."; - if (Exception != null) message += $"Exception: {Exception.Message}"; - return message; + return $"{ResponseType.ToString()}: {Message}."; } } } \ No newline at end of file diff --git a/InstaSharper/Classes/TwoFactorLoginInfo.cs b/InstaSharper/Classes/TwoFactorLoginInfo.cs new file mode 100644 index 00000000..09e4cfff --- /dev/null +++ b/InstaSharper/Classes/TwoFactorLoginInfo.cs @@ -0,0 +1,24 @@ +using Newtonsoft.Json; + +namespace InstaSharper.Classes +{ + public class TwoFactorLoginInfo + { + [JsonProperty("obfuscated_phone_number")] + public short ObfuscatedPhoneNumber { get; set; } + + [JsonProperty("show_messenger_code_option")] + public bool ShowMessengerCodeOption { get; set; } + + [JsonProperty("two_factor_identifier")] + public string TwoFactorIdentifier { get; set; } + + [JsonProperty("username")] + public string Username { get; set; } + + [JsonProperty("phone_verification_settings")] + public PhoneVerificationSettings PhoneVerificationSettings { get; set; } + + public static TwoFactorLoginInfo Empty => new TwoFactorLoginInfo(); + } +} \ No newline at end of file diff --git a/InstaSharper/Classes/UserSessionData.cs b/InstaSharper/Classes/UserSessionData.cs index 5b11f2b8..4ab03b60 100644 --- a/InstaSharper/Classes/UserSessionData.cs +++ b/InstaSharper/Classes/UserSessionData.cs @@ -13,5 +13,18 @@ public class UserSessionData public string RankToken { get; set; } public string CsrfToken { get; set; } + + public static UserSessionData Empty => new UserSessionData(); + + public static UserSessionData ForUsername(string username) + { + return new UserSessionData {UserName = username}; + } + + public UserSessionData WithPassword(string password) + { + Password = password; + return this; + } } } \ No newline at end of file diff --git a/InstaSharper/Converters/ConvertersFabric.cs b/InstaSharper/Converters/ConvertersFabric.cs index bf570967..3b1b6541 100644 --- a/InstaSharper/Converters/ConvertersFabric.cs +++ b/InstaSharper/Converters/ConvertersFabric.cs @@ -1,208 +1,256 @@ -using InstaSharper.Classes.Models; +using System; +using InstaSharper.Classes.Models; using InstaSharper.Classes.ResponseWrappers; namespace InstaSharper.Converters { - internal class ConvertersFabric + internal class ConvertersFabric : IConvertersFabric { - internal static IObjectConverter GetUserShortConverter( + private static readonly Lazy LazyInstance = + new Lazy(() => new ConvertersFabric()); + + public static ConvertersFabric Instance => LazyInstance.Value; + + public IObjectConverter GetUserShortConverter( InstaUserShortResponse instaresponse) { return new InstaUserShortConverter {SourceObject = instaresponse}; } - internal static IObjectConverter GetCurrentUserConverter( + public IObjectConverter GetCurrentUserConverter( InstaCurrentUserResponse instaresponse) { return new InstaCurrentUserConverter {SourceObject = instaresponse}; } - internal static IObjectConverter GetUserConverter(InstaUserResponse instaresponse) + public IObjectConverter GetUserConverter(InstaUserResponse instaresponse) { return new InstaUserConverter {SourceObject = instaresponse}; } - public static IObjectConverter GetSingleMediaConverter( + public IObjectConverter GetSingleMediaConverter( InstaMediaItemResponse responseMedia) { return new InstaMediaConverter {SourceObject = responseMedia}; } - internal static IObjectConverter GetFeedConverter( + public IObjectConverter GetFeedConverter( InstaFeedResponse feedResponse) { return new InstaFeedConverter {SourceObject = feedResponse}; } - internal static IObjectConverter GetTagFeedConverter( + public IObjectConverter GetTagFeedConverter( InstaTagFeedResponse feedResponse) { return new InstaTagFeedConverter {SourceObject = feedResponse}; } - public static IObjectConverter GetMediaListConverter( + public IObjectConverter GetMediaListConverter( InstaMediaListResponse mediaResponse) { return new InstaMediaListConverter {SourceObject = mediaResponse}; } - public static IObjectConverter GetCaptionConverter( + public IObjectConverter GetCaptionConverter( InstaCaptionResponse captionResponse) { return new InstaCaptionConverter {SourceObject = captionResponse}; } - public static IObjectConverter + public IObjectConverter GetFriendShipStatusConverter(InstaFriendshipStatusResponse friendshipStatusResponse) { return new InstaFriendshipStatusConverter {SourceObject = friendshipStatusResponse}; } - public static IObjectConverter GetSingleStoryConverter( + public IObjectConverter GetSingleStoryConverter( InstaStoryResponse storyResponse) { return new InstaStoryConverter {SourceObject = storyResponse}; } - public static IObjectConverter GetUserTagConverter(InstaUserTagResponse tag) + public IObjectConverter GetUserTagConverter(InstaUserTagResponse tag) { return new InstaUserTagConverter {SourceObject = tag}; } - public static IObjectConverter + public IObjectConverter GetDirectInboxConverter(InstaDirectInboxContainerResponse inbox) { return new InstaDirectInboxConverter {SourceObject = inbox}; } - public static IObjectConverter GetDirectThreadConverter( + public IObjectConverter GetDirectThreadConverter( InstaDirectInboxThreadResponse thread) { return new InstaDirectThreadConverter {SourceObject = thread}; } - public static IObjectConverter GetDirectThreadItemConverter( + public IObjectConverter GetDirectThreadItemConverter( InstaDirectInboxItemResponse threadItem) { return new InstaDirectThreadItemConverter {SourceObject = threadItem}; } - public static IObjectConverter + public IObjectConverter GetDirectSubscriptionConverter(InstaDirectInboxSubscriptionResponse subscription) { return new InstaDirectInboxSubscriptionConverter {SourceObject = subscription}; } - public static IObjectConverter + public IObjectConverter GetSingleRecentActivityConverter(InstaRecentActivityFeedResponse feedResponse) { return new InstaRecentActivityConverter {SourceObject = feedResponse}; } - public static IObjectConverter GetRecipientsConverter( + public IObjectConverter GetRecipientsConverter( IInstaRecipientsResponse recipients) { return new InstaRecipientsConverter {SourceObject = recipients}; } - public static IObjectConverter GetCommentConverter( + public IObjectConverter GetCommentConverter( InstaCommentResponse comment) { return new InstaCommentConverter {SourceObject = comment}; } - public static IObjectConverter GetCommentListConverter( + public IObjectConverter GetCommentListConverter( InstaCommentListResponse commentList) { return new InstaCommentListConverter {SourceObject = commentList}; } - public static IObjectConverter GetCarouselConverter( + public IObjectConverter GetCarouselConverter( InstaCarouselResponse carousel) { return new InstaCarouselConverter {SourceObject = carousel}; } - public static IObjectConverter GetCarouselItemConverter( + public IObjectConverter GetCarouselItemConverter( InstaCarouselItemResponse carouselItem) { return new InstaCarouselItemConverter {SourceObject = carouselItem}; } - public static IObjectConverter GetStoryItemConverter( + public IObjectConverter GetStoryItemConverter( InstaStoryItemResponse storyItem) { return new InstaStoryItemConverter {SourceObject = storyItem}; } - public static IObjectConverter GetStoryConverter(InstaStoryResponse storyItem) + public IObjectConverter GetStoryConverter(InstaStoryResponse storyItem) { return new InstaStoryConverter {SourceObject = storyItem}; } - public static IObjectConverter GetStoryTrayConverter( + public IObjectConverter GetStoryTrayConverter( InstaStoryTrayResponse storyTray) { return new InstaStoryTrayConverter {SourceObject = storyTray}; } - public static IObjectConverter GetStoryMediaConverter( + public IObjectConverter GetStoryMediaConverter( InstaStoryMediaResponse storyMedia) { return new InstaStoryMediaConverter {SourceObject = storyMedia}; } - public static IObjectConverter GetImageConverter(ImageResponse imageResponse) + public IObjectConverter GetImageConverter(ImageResponse imageResponse) { return new InstaMediaImageConverter {SourceObject = imageResponse}; } - public static IObjectConverter GetExploreFeedConverter( + public IObjectConverter GetExploreFeedConverter( InstaExploreFeedResponse feedResponse) { return new InstaExploreFeedConverter {SourceObject = feedResponse}; } - public static IObjectConverter GetChannelConverter( + public IObjectConverter GetChannelConverter( InstaChannelResponse response) { return new InstaChannelConverter {SourceObject = response}; } - public static IObjectConverter GetTopLiveConverter( + public IObjectConverter GetTopLiveConverter( InstaTopLiveResponse response) { return new InstaTopLiveConverter {SourceObject = response}; } - public static IObjectConverter GetReelFeedConverter( + public IObjectConverter GetReelFeedConverter( InsteReelFeedResponse response) { return new InsteReelFeedConverter {SourceObject = response}; } - public static IObjectConverter GetMentionConverter( + public IObjectConverter GetMentionConverter( InstaReelMentionResponse response) { return new InstaReelMentionConverter {SourceObject = response}; } - public static IObjectConverter GetLocationConverter( + public IObjectConverter GetLocationConverter( InstaLocationResponse response) { return new InstaLocationConverter {SourceObject = response}; } - public static IObjectConverter GetHashTagConverter( + public IObjectConverter GetHashTagConverter( InstaHashtagResponse response) { return new InstaHashtagConverter {SourceObject = response}; } - public static IObjectConverter GetStoryFeedConverter( + public IObjectConverter GetStoryFeedConverter( InstaStoryFeedResponse response) { return new InstaStoryFeedConverter {SourceObject = response}; } + + public IObjectConverter GetCollectionConverter( + InstaCollectionItemResponse response) + { + return new InstaCollectionConverter {SourceObject = response}; + } + + public IObjectConverter GetCollectionsConverter( + InstaCollectionsResponse response) + { + return new InstaCollectionsConverter {SourceObject = response}; + } + + public IObjectConverter GetCoverMediaConverter( + InstaCoverMediaResponse response) + { + return new InstaCoverMediaConverter {SourceObject = response}; + } + + public IObjectConverter GetInboxMediaConverter( + InstaInboxMediaResponse response) + { + return new InstaInboxMediaConverter {SourceObject = response}; + } + + public IObjectConverter GetLocationsSearchConverter( + InstaLocationSearchResponse response) + { + return new InstaLocationSearchConverter {SourceObject = response}; + } + + public IObjectConverter GetLocationShortConverter( + InstaLocationShortResponse response) + { + return new InstaLocationShortConverter {SourceObject = response}; + } + + public IObjectConverter GetLocationFeedConverter( + InstaLocationFeedResponse response) + { + return new InstaLocationFeedConverter {SourceObject = response}; + } } } \ No newline at end of file diff --git a/InstaSharper/Converters/IConvertersFabric.cs b/InstaSharper/Converters/IConvertersFabric.cs new file mode 100644 index 00000000..35bcf05e --- /dev/null +++ b/InstaSharper/Converters/IConvertersFabric.cs @@ -0,0 +1,115 @@ +using InstaSharper.Classes.Models; +using InstaSharper.Classes.ResponseWrappers; + +namespace InstaSharper.Converters +{ + public interface IConvertersFabric + { + IObjectConverter GetUserShortConverter( + InstaUserShortResponse instaresponse); + + IObjectConverter GetCurrentUserConverter( + InstaCurrentUserResponse instaresponse); + + IObjectConverter GetUserConverter(InstaUserResponse instaresponse); + + IObjectConverter GetSingleMediaConverter( + InstaMediaItemResponse responseMedia); + + IObjectConverter GetFeedConverter( + InstaFeedResponse feedResponse); + + IObjectConverter GetTagFeedConverter( + InstaTagFeedResponse feedResponse); + + IObjectConverter GetMediaListConverter( + InstaMediaListResponse mediaResponse); + + IObjectConverter GetCaptionConverter( + InstaCaptionResponse captionResponse); + + IObjectConverter + GetFriendShipStatusConverter(InstaFriendshipStatusResponse friendshipStatusResponse); + + IObjectConverter GetSingleStoryConverter( + InstaStoryResponse storyResponse); + + IObjectConverter GetUserTagConverter(InstaUserTagResponse tag); + + IObjectConverter + GetDirectInboxConverter(InstaDirectInboxContainerResponse inbox); + + IObjectConverter GetDirectThreadConverter( + InstaDirectInboxThreadResponse thread); + + IObjectConverter GetDirectThreadItemConverter( + InstaDirectInboxItemResponse threadItem); + + IObjectConverter + GetDirectSubscriptionConverter(InstaDirectInboxSubscriptionResponse subscription); + + IObjectConverter + GetSingleRecentActivityConverter(InstaRecentActivityFeedResponse feedResponse); + + IObjectConverter GetRecipientsConverter( + IInstaRecipientsResponse recipients); + + IObjectConverter GetCommentConverter( + InstaCommentResponse comment); + + IObjectConverter GetCommentListConverter( + InstaCommentListResponse commentList); + + IObjectConverter GetCarouselConverter( + InstaCarouselResponse carousel); + + IObjectConverter GetCarouselItemConverter( + InstaCarouselItemResponse carouselItem); + + IObjectConverter GetStoryItemConverter( + InstaStoryItemResponse storyItem); + + IObjectConverter GetStoryConverter(InstaStoryResponse storyItem); + + IObjectConverter GetStoryTrayConverter( + InstaStoryTrayResponse storyTray); + + IObjectConverter GetStoryMediaConverter( + InstaStoryMediaResponse storyMedia); + + IObjectConverter GetImageConverter(ImageResponse imageResponse); + + IObjectConverter GetExploreFeedConverter( + InstaExploreFeedResponse feedResponse); + + IObjectConverter GetChannelConverter( + InstaChannelResponse response); + + IObjectConverter GetTopLiveConverter( + InstaTopLiveResponse response); + + IObjectConverter GetReelFeedConverter( + InsteReelFeedResponse response); + + IObjectConverter GetMentionConverter( + InstaReelMentionResponse response); + + IObjectConverter GetLocationConverter( + InstaLocationResponse response); + + IObjectConverter GetHashTagConverter( + InstaHashtagResponse response); + + IObjectConverter GetStoryFeedConverter( + InstaStoryFeedResponse response); + + IObjectConverter GetCollectionConverter( + InstaCollectionItemResponse response); + + IObjectConverter GetCollectionsConverter( + InstaCollectionsResponse response); + + IObjectConverter GetCoverMediaConverter( + InstaCoverMediaResponse response); + } +} \ No newline at end of file diff --git a/InstaSharper/Converters/IObjectConverter.cs b/InstaSharper/Converters/IObjectConverter.cs index 2d1b0319..121bd01e 100644 --- a/InstaSharper/Converters/IObjectConverter.cs +++ b/InstaSharper/Converters/IObjectConverter.cs @@ -1,6 +1,6 @@ namespace InstaSharper.Converters { - internal interface IObjectConverter + public interface IObjectConverter { TT SourceObject { get; set; } T Convert(); diff --git a/InstaSharper/Converters/InstaCaptionConverter.cs b/InstaSharper/Converters/InstaCaptionConverter.cs index 1c27aaba..e033a872 100644 --- a/InstaSharper/Converters/InstaCaptionConverter.cs +++ b/InstaSharper/Converters/InstaCaptionConverter.cs @@ -17,7 +17,7 @@ public InstaCaption Convert() CreatedAtUtc = DateTimeHelper.UnixTimestampToDateTime(SourceObject.CreatedAtUtcUnixLike), MediaId = SourceObject.MediaId, Text = SourceObject.Text, - User = ConvertersFabric.GetUserShortConverter(SourceObject.User).Convert(), + User = ConvertersFabric.Instance.GetUserShortConverter(SourceObject.User).Convert(), UserId = SourceObject.UserId }; return caption; diff --git a/InstaSharper/Converters/InstaCarouselConverter.cs b/InstaSharper/Converters/InstaCarouselConverter.cs index 4c71c3e0..96f41ee7 100644 --- a/InstaSharper/Converters/InstaCarouselConverter.cs +++ b/InstaSharper/Converters/InstaCarouselConverter.cs @@ -14,7 +14,7 @@ public InstaCarousel Convert() if (SourceObject == null) throw new ArgumentNullException($"Source object"); foreach (var item in SourceObject) { - var carouselItem = ConvertersFabric.GetCarouselItemConverter(item); + var carouselItem = ConvertersFabric.Instance.GetCarouselItemConverter(item); carousel.Add(carouselItem.Convert()); } return carousel; diff --git a/InstaSharper/Converters/InstaCarouselItemConverter.cs b/InstaSharper/Converters/InstaCarouselItemConverter.cs index ab6fb198..8fe4f255 100644 --- a/InstaSharper/Converters/InstaCarouselItemConverter.cs +++ b/InstaSharper/Converters/InstaCarouselItemConverter.cs @@ -17,8 +17,10 @@ public InstaCarouselItem Convert() Height = int.Parse(SourceObject.Height), Width = int.Parse(SourceObject.Width) }; - foreach (var image in SourceObject.Images.Candidates) - carouselItem.Images.Add(new InstaImage(image.Url, int.Parse(image.Width), int.Parse(image.Height))); + if (SourceObject?.Images?.Candidates != null) + foreach (var image in SourceObject.Images.Candidates) + carouselItem.Images.Add(new InstaImage(image.Url, int.Parse(image.Width), int.Parse(image.Height))); + return carouselItem; } } diff --git a/InstaSharper/Converters/InstaChannelConverter.cs b/InstaSharper/Converters/InstaChannelConverter.cs index 53007d1f..976120ca 100644 --- a/InstaSharper/Converters/InstaChannelConverter.cs +++ b/InstaSharper/Converters/InstaChannelConverter.cs @@ -17,9 +17,10 @@ public InstaChannel Convert() ChannelType = SourceObject.ChannelType, Context = SourceObject.Context, Header = SourceObject.Header, - Title = SourceObject.Title, - Media = ConvertersFabric.GetSingleMediaConverter(SourceObject.Media).Convert() + Title = SourceObject.Title }; + if (SourceObject.Media != null) + channel.Media = ConvertersFabric.Instance.GetSingleMediaConverter(SourceObject.Media).Convert(); return channel; } } diff --git a/InstaSharper/Converters/InstaCollectionConverter.cs b/InstaSharper/Converters/InstaCollectionConverter.cs new file mode 100644 index 00000000..c440007a --- /dev/null +++ b/InstaSharper/Converters/InstaCollectionConverter.cs @@ -0,0 +1,32 @@ +using System.Linq; +using InstaSharper.Classes.Models; +using InstaSharper.Classes.ResponseWrappers; + +namespace InstaSharper.Converters +{ + internal class InstaCollectionConverter : IObjectConverter + { + public InstaCollectionItemResponse SourceObject { get; set; } + + public InstaCollectionItem Convert() + { + var instaMediaList = new InstaMediaList(); + + if (SourceObject.Media != null) + instaMediaList.AddRange(SourceObject.Media.Medias + .Select(ConvertersFabric.Instance.GetSingleMediaConverter) + .Select(converter => converter.Convert())); + + return new InstaCollectionItem + { + CollectionId = SourceObject.CollectionId, + CollectionName = SourceObject.CollectionName, + HasRelatedMedia = SourceObject.HasRelatedMedia, + Media = instaMediaList, + CoverMedia = SourceObject.CoverMedia != null + ? ConvertersFabric.Instance.GetCoverMediaConverter(SourceObject.CoverMedia).Convert() + : null + }; + } + } +} \ No newline at end of file diff --git a/InstaSharper/Converters/InstaCollectionsConverter.cs b/InstaSharper/Converters/InstaCollectionsConverter.cs new file mode 100644 index 00000000..2f81e73e --- /dev/null +++ b/InstaSharper/Converters/InstaCollectionsConverter.cs @@ -0,0 +1,25 @@ +using System.Collections.Generic; +using System.Linq; +using InstaSharper.Classes.Models; +using InstaSharper.Classes.ResponseWrappers; + +namespace InstaSharper.Converters +{ + public class InstaCollectionsConverter : IObjectConverter + { + public InstaCollectionsResponse SourceObject { get; set; } + + public InstaCollections Convert() + { + var instaCollectionList = new List(); + instaCollectionList.AddRange(SourceObject.Items.Select(ConvertersFabric.Instance.GetCollectionConverter) + .Select(converter => converter.Convert())); + + return new InstaCollections + { + Items = instaCollectionList, + MoreCollectionsAvailable = SourceObject.MoreAvailable + }; + } + } +} \ No newline at end of file diff --git a/InstaSharper/Converters/InstaCommentConverter.cs b/InstaSharper/Converters/InstaCommentConverter.cs index ad7924df..c8de08a1 100644 --- a/InstaSharper/Converters/InstaCommentConverter.cs +++ b/InstaSharper/Converters/InstaCommentConverter.cs @@ -24,7 +24,7 @@ public InstaComment Convert() Text = SourceObject.Text, Type = SourceObject.Type, UserId = SourceObject.UserId, - User = ConvertersFabric.GetUserShortConverter(SourceObject.User).Convert() + User = ConvertersFabric.Instance.GetUserShortConverter(SourceObject.User).Convert() }; return comment; } diff --git a/InstaSharper/Converters/InstaCommentListConverter.cs b/InstaSharper/Converters/InstaCommentListConverter.cs index 80b5fc24..ade87110 100644 --- a/InstaSharper/Converters/InstaCommentListConverter.cs +++ b/InstaSharper/Converters/InstaCommentListConverter.cs @@ -12,17 +12,18 @@ public InstaCommentList Convert() var commentList = new InstaCommentList { Caption = SourceObject.Caption != null - ? ConvertersFabric.GetCaptionConverter(SourceObject.Caption).Convert() + ? ConvertersFabric.Instance.GetCaptionConverter(SourceObject.Caption).Convert() : null, CaptionIsEdited = SourceObject.CaptionIsEdited, - CommentsCount = SourceObject.CommentsCount, LikesEnabled = SourceObject.LikesEnabled, MoreComentsAvailable = SourceObject.MoreComentsAvailable, - MoreHeadLoadAvailable = SourceObject.MoreHeadLoadAvailable + MoreHeadLoadAvailable = SourceObject.MoreHeadLoadAvailable, + NextId = SourceObject.NextMaxId }; + if (SourceObject.Comments == null || !(SourceObject?.Comments?.Count > 0)) return commentList; foreach (var commentResponse in SourceObject.Comments) { - var converter = ConvertersFabric.GetCommentConverter(commentResponse); + var converter = ConvertersFabric.Instance.GetCommentConverter(commentResponse); commentList.Comments.Add(converter.Convert()); } return commentList; diff --git a/InstaSharper/Converters/InstaCoverMediaConverter.cs b/InstaSharper/Converters/InstaCoverMediaConverter.cs new file mode 100644 index 00000000..378897c1 --- /dev/null +++ b/InstaSharper/Converters/InstaCoverMediaConverter.cs @@ -0,0 +1,30 @@ +using System.Collections.Generic; +using System.Linq; +using InstaSharper.Classes.Models; +using InstaSharper.Classes.ResponseWrappers; + +namespace InstaSharper.Converters +{ + internal class InstaCoverMediaConverter : IObjectConverter + { + public InstaCoverMediaResponse SourceObject { get; set; } + + public InstaCoverMedia Convert() + { + var instaImageList = new List(); + + if (SourceObject.ImageVersions != null) + instaImageList.AddRange(SourceObject.ImageVersions.Candidates + .Select(ConvertersFabric.Instance.GetImageConverter).Select(converter => converter.Convert())); + + return new InstaCoverMedia + { + Id = SourceObject.Id, + ImageVersions = instaImageList, + MediaType = SourceObject.MediaType, + OriginalHeight = SourceObject.OriginalHeight, + OriginalWidth = SourceObject.OriginalWidth + }; + } + } +} \ No newline at end of file diff --git a/InstaSharper/Converters/InstaCurrentUserConverter.cs b/InstaSharper/Converters/InstaCurrentUserConverter.cs index 734251de..7c688cd0 100644 --- a/InstaSharper/Converters/InstaCurrentUserConverter.cs +++ b/InstaSharper/Converters/InstaCurrentUserConverter.cs @@ -11,7 +11,7 @@ internal class InstaCurrentUserConverter : IObjectConverter 0) foreach (var imageResponse in SourceObject.HDProfilePicVersions) { - var converter = ConvertersFabric.GetImageConverter(imageResponse); + var converter = ConvertersFabric.Instance.GetImageConverter(imageResponse); user.HdProfileImages.Add(converter.Convert()); } if (SourceObject.HDProfilePicture != null) { - var converter = ConvertersFabric.GetImageConverter(SourceObject.HDProfilePicture); + var converter = ConvertersFabric.Instance.GetImageConverter(SourceObject.HDProfilePicture); user.HdProfilePicture = converter.Convert(); } return user; diff --git a/InstaSharper/Converters/InstaDirectInboxConverter.cs b/InstaSharper/Converters/InstaDirectInboxConverter.cs index 34163c34..2a5108a3 100644 --- a/InstaSharper/Converters/InstaDirectInboxConverter.cs +++ b/InstaSharper/Converters/InstaDirectInboxConverter.cs @@ -18,7 +18,7 @@ public InstaDirectInboxContainer Convert() }; if (SourceObject.Subscription != null) { - var converter = ConvertersFabric.GetDirectSubscriptionConverter(SourceObject.Subscription); + var converter = ConvertersFabric.Instance.GetDirectSubscriptionConverter(SourceObject.Subscription); inbox.Subscription = converter.Convert(); } if (SourceObject.Inbox != null) @@ -35,7 +35,7 @@ public InstaDirectInboxContainer Convert() inbox.Inbox.Threads = new List(); foreach (var inboxThread in SourceObject.Inbox.Threads) { - var converter = ConvertersFabric.GetDirectThreadConverter(inboxThread); + var converter = ConvertersFabric.Instance.GetDirectThreadConverter(inboxThread); inbox.Inbox.Threads.Add(converter.Convert()); } } @@ -44,7 +44,7 @@ public InstaDirectInboxContainer Convert() { foreach (var user in SourceObject.PendingUsers) { - var converter = ConvertersFabric.GetUserShortConverter(user); + var converter = ConvertersFabric.Instance.GetUserShortConverter(user); inbox.PendingUsers.Add(converter.Convert()); } } diff --git a/InstaSharper/Converters/InstaDirectThreadConverter.cs b/InstaSharper/Converters/InstaDirectThreadConverter.cs index 5c3f2be1..3d5cd41b 100644 --- a/InstaSharper/Converters/InstaDirectThreadConverter.cs +++ b/InstaSharper/Converters/InstaDirectThreadConverter.cs @@ -29,7 +29,7 @@ public InstaDirectInboxThread Convert() thread.Title = SourceObject.Title; if (SourceObject.Inviter != null) { - var userConverter = ConvertersFabric.GetUserShortConverter(SourceObject.Inviter); + var userConverter = ConvertersFabric.Instance.GetUserShortConverter(SourceObject.Inviter); thread.Inviter = userConverter.Convert(); } if (SourceObject.Items != null && SourceObject.Items.Count > 0) @@ -37,7 +37,7 @@ public InstaDirectInboxThread Convert() thread.Items = new List(); foreach (var item in SourceObject.Items) { - var converter = ConvertersFabric.GetDirectThreadItemConverter(item); + var converter = ConvertersFabric.Instance.GetDirectThreadItemConverter(item); thread.Items.Add(converter.Convert()); } } @@ -46,7 +46,7 @@ public InstaDirectInboxThread Convert() thread.Users = new InstaUserShortList(); foreach (var user in SourceObject.Users) { - var converter = ConvertersFabric.GetUserShortConverter(user); + var converter = ConvertersFabric.Instance.GetUserShortConverter(user); thread.Users.Add(converter.Convert()); } } diff --git a/InstaSharper/Converters/InstaDirectThreadItemConverter.cs b/InstaSharper/Converters/InstaDirectThreadItemConverter.cs index b23fc091..3d12104f 100644 --- a/InstaSharper/Converters/InstaDirectThreadItemConverter.cs +++ b/InstaSharper/Converters/InstaDirectThreadItemConverter.cs @@ -1,4 +1,5 @@ -using InstaSharper.Classes.Models; +using System; +using InstaSharper.Classes.Models; using InstaSharper.Classes.ResponseWrappers; using InstaSharper.Helpers; @@ -15,21 +16,38 @@ public InstaDirectInboxItem Convert() ClientContext = SourceObject.ClientContext, ItemId = SourceObject.ItemId }; - switch (SourceObject.ItemType) - { - case "text": - threadItem.ItemType = InstaDirectThreadItemType.Text; - break; - case "media_share": - threadItem.ItemType = InstaDirectThreadItemType.MediaShare; - break; - } - threadItem.Text = SourceObject.Text; + threadItem.TimeStamp = DateTimeHelper.UnixTimestampMilisecondsToDateTime(SourceObject.TimeStamp); threadItem.UserId = SourceObject.UserId; - if (SourceObject.MediaShare == null) return threadItem; - var converter = ConvertersFabric.GetSingleMediaConverter(SourceObject.MediaShare); - threadItem.MediaShare = converter.Convert(); + + var truncatedItemType = SourceObject.ItemType.Trim().Replace("_", ""); + if (Enum.TryParse(truncatedItemType, true, out InstaDirectThreadItemType type)) + threadItem.ItemType = type; + + if (threadItem.ItemType == InstaDirectThreadItemType.Link) + { + threadItem.Text = SourceObject.Link?.LinkContext?.LinkUrl; + } + else if (threadItem.ItemType == InstaDirectThreadItemType.Like) + { + threadItem.Text = SourceObject.Like; + } + else if (threadItem.ItemType == InstaDirectThreadItemType.Media + && SourceObject.Media != null) + { + var converter = ConvertersFabric.Instance.GetInboxMediaConverter(SourceObject.Media); + threadItem.Media = converter.Convert(); + } + else if (threadItem.ItemType == InstaDirectThreadItemType.MediaShare + && SourceObject.MediaShare != null) + { + var converter = ConvertersFabric.Instance.GetSingleMediaConverter(SourceObject.MediaShare); + threadItem.MediaShare = converter.Convert(); + } + else + { + threadItem.Text = SourceObject.Text; + } return threadItem; } } diff --git a/InstaSharper/Converters/InstaExploreFeedConverter.cs b/InstaSharper/Converters/InstaExploreFeedConverter.cs index 9805288e..deb6c2d3 100644 --- a/InstaSharper/Converters/InstaExploreFeedConverter.cs +++ b/InstaSharper/Converters/InstaExploreFeedConverter.cs @@ -17,10 +17,13 @@ public InstaExploreFeed Convert() List ConvertMedia(List mediasResponse) { var medias = new List(); + if (mediasResponse == null) + return medias; foreach (var instaUserFeedItemResponse in mediasResponse) { if (instaUserFeedItemResponse?.Type != 0) continue; - var feedItem = ConvertersFabric.GetSingleMediaConverter(instaUserFeedItemResponse).Convert(); + var feedItem = ConvertersFabric.Instance.GetSingleMediaConverter(instaUserFeedItemResponse) + .Convert(); medias.Add(feedItem); } return medias; @@ -28,12 +31,15 @@ List ConvertMedia(List mediasResponse) var feed = new InstaExploreFeed { - StoryTray = ConvertersFabric.GetStoryTrayConverter(SourceObject.Items.StoryTray).Convert(), - Channel = ConvertersFabric.GetChannelConverter(SourceObject.Items.Channel).Convert() + NextId = SourceObject.NextMaxId }; - feed.Medias.AddRange(ConvertMedia(SourceObject.Items.Medias)); - feed.Medias.PageSize = feed.Medias.Count; + if (SourceObject.Items?.StoryTray != null) + feed.StoryTray = ConvertersFabric.Instance.GetStoryTrayConverter(SourceObject.Items.StoryTray) + .Convert(); + if (SourceObject.Items?.Channel != null) + feed.Channel = ConvertersFabric.Instance.GetChannelConverter(SourceObject.Items.Channel).Convert(); + feed.Medias.AddRange(ConvertMedia(SourceObject.Items?.Medias)); return feed; } } diff --git a/InstaSharper/Converters/InstaFeedConverter.cs b/InstaSharper/Converters/InstaFeedConverter.cs index 105594aa..9ba944f9 100644 --- a/InstaSharper/Converters/InstaFeedConverter.cs +++ b/InstaSharper/Converters/InstaFeedConverter.cs @@ -16,10 +16,10 @@ public InstaFeed Convert() foreach (var instaUserFeedItemResponse in SourceObject.Items) { if (instaUserFeedItemResponse?.Type != 0) continue; - var feedItem = ConvertersFabric.GetSingleMediaConverter(instaUserFeedItemResponse).Convert(); + var feedItem = ConvertersFabric.Instance.GetSingleMediaConverter(instaUserFeedItemResponse).Convert(); feed.Medias.Add(feedItem); } - feed.Medias.PageSize = SourceObject.Items.Count; + feed.NextId = SourceObject.NextMaxId; return feed; } } diff --git a/InstaSharper/Converters/InstaInboxMediaConverter.cs b/InstaSharper/Converters/InstaInboxMediaConverter.cs new file mode 100644 index 00000000..6c4e749b --- /dev/null +++ b/InstaSharper/Converters/InstaInboxMediaConverter.cs @@ -0,0 +1,26 @@ +using System; +using InstaSharper.Classes.Models; +using InstaSharper.Classes.ResponseWrappers; + +namespace InstaSharper.Converters +{ + internal class InstaInboxMediaConverter : IObjectConverter + { + public InstaInboxMediaResponse SourceObject { get; set; } + + public InstaInboxMedia Convert() + { + if (SourceObject == null) throw new ArgumentNullException($"Source object"); + var inboxMedia = new InstaInboxMedia + { + MediaType = SourceObject.MediaType, + OriginalHeight = SourceObject.OriginalHeight, + OriginalWidth = SourceObject.OriginalWidth + }; + if (SourceObject?.ImageCandidates?.Candidates == null) return inboxMedia; + foreach (var image in SourceObject.ImageCandidates.Candidates) + inboxMedia.Images.Add(new InstaImage(image.Url, int.Parse(image.Width), int.Parse(image.Height))); + return inboxMedia; + } + } +} \ No newline at end of file diff --git a/InstaSharper/Converters/InstaLocationConverter.cs b/InstaSharper/Converters/InstaLocationConverter.cs index 8e048468..18685dd9 100644 --- a/InstaSharper/Converters/InstaLocationConverter.cs +++ b/InstaSharper/Converters/InstaLocationConverter.cs @@ -16,8 +16,8 @@ public InstaLocation Convert() Name = SourceObject.Name, Address = SourceObject.Address, City = SourceObject.City, - ExternalSource = SourceObject.ExternalSource, - FacebookPlacesId = SourceObject.FacebookPlacesId, + ExternalSource = SourceObject.ExternalIdSource, + ExternalId = SourceObject.ExternalId, Lat = SourceObject.Lat, Lng = SourceObject.Lng, Pk = SourceObject.Pk, diff --git a/InstaSharper/Converters/InstaLocationFeedConverter.cs b/InstaSharper/Converters/InstaLocationFeedConverter.cs new file mode 100644 index 00000000..439ec3ec --- /dev/null +++ b/InstaSharper/Converters/InstaLocationFeedConverter.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using InstaSharper.Classes.Models; +using InstaSharper.Classes.ResponseWrappers; + +namespace InstaSharper.Converters +{ + internal class InstaLocationFeedConverter : IObjectConverter + { + public InstaLocationFeedResponse SourceObject { get; set; } + + public InstaLocationFeed Convert() + { + if (SourceObject == null) + throw new ArgumentNullException("SourceObject"); + + InstaMediaList ConvertMedia(List mediasResponse) + { + var medias = new InstaMediaList(); + if (mediasResponse == null) + return medias; + foreach (var instaUserFeedItemResponse in mediasResponse) + { + if (instaUserFeedItemResponse?.Type != 0) continue; + var feedItem = ConvertersFabric.Instance.GetSingleMediaConverter(instaUserFeedItemResponse) + .Convert(); + medias.Add(feedItem); + } + return medias; + } + + var feed = new InstaLocationFeed + { + MediaCount = SourceObject.MediaCount, + NextId = SourceObject.NextMaxId, + Medias = ConvertMedia(SourceObject.Items), + RankedMedias = ConvertMedia(SourceObject.RankedItems), + Location = ConvertersFabric.Instance.GetLocationConverter(SourceObject.Location).Convert(), + Story = ConvertersFabric.Instance.GetStoryConverter(SourceObject.Story).Convert() + }; + return feed; + } + } +} \ No newline at end of file diff --git a/InstaSharper/Converters/InstaLocationSearchConverter.cs b/InstaSharper/Converters/InstaLocationSearchConverter.cs new file mode 100644 index 00000000..6112e377 --- /dev/null +++ b/InstaSharper/Converters/InstaLocationSearchConverter.cs @@ -0,0 +1,21 @@ +using System; +using System.Linq; +using InstaSharper.Classes.Models; +using InstaSharper.Classes.ResponseWrappers; + +namespace InstaSharper.Converters +{ + internal class InstaLocationSearchConverter : IObjectConverter + { + public InstaLocationSearchResponse SourceObject { get; set; } + + public InstaLocationShortList Convert() + { + if (SourceObject == null) throw new ArgumentNullException($"Source object"); + var locations = new InstaLocationShortList(); + locations.AddRange(SourceObject.Locations.Select(location => + ConvertersFabric.Instance.GetLocationShortConverter(location).Convert())); + return locations; + } + } +} \ No newline at end of file diff --git a/InstaSharper/Converters/InstaLocationShortConverter.cs b/InstaSharper/Converters/InstaLocationShortConverter.cs new file mode 100644 index 00000000..1b7b237d --- /dev/null +++ b/InstaSharper/Converters/InstaLocationShortConverter.cs @@ -0,0 +1,26 @@ +using System; +using InstaSharper.Classes.Models; +using InstaSharper.Classes.ResponseWrappers; + +namespace InstaSharper.Converters +{ + internal class InstaLocationShortConverter : IObjectConverter + { + public InstaLocationShortResponse SourceObject { get; set; } + + public InstaLocationShort Convert() + { + if (SourceObject == null) throw new ArgumentNullException($"Source object"); + var location = new InstaLocationShort + { + Name = SourceObject.Name, + Address = SourceObject.Address, + ExternalSource = SourceObject.ExternalIdSource, + ExternalId = SourceObject.ExternalId, + Lat = SourceObject.Lat, + Lng = SourceObject.Lng + }; + return location; + } + } +} \ No newline at end of file diff --git a/InstaSharper/Converters/InstaMediaConverter.cs b/InstaSharper/Converters/InstaMediaConverter.cs index fa35d292..3c02f7f0 100644 --- a/InstaSharper/Converters/InstaMediaConverter.cs +++ b/InstaSharper/Converters/InstaMediaConverter.cs @@ -34,18 +34,23 @@ public InstaMedia Convert() ViewCount = int.Parse(SourceObject.ViewCount.ToString(CultureInfo.InvariantCulture)) }; if (SourceObject.CarouselMedia != null) - media.Carousel = ConvertersFabric.GetCarouselConverter(SourceObject.CarouselMedia).Convert(); + media.Carousel = ConvertersFabric.Instance.GetCarouselConverter(SourceObject.CarouselMedia).Convert(); if (SourceObject.User != null) - media.User = ConvertersFabric.GetUserConverter(SourceObject.User).Convert(); + media.User = ConvertersFabric.Instance.GetUserConverter(SourceObject.User).Convert(); if (SourceObject.Caption != null) - media.Caption = ConvertersFabric.GetCaptionConverter(SourceObject.Caption).Convert(); + media.Caption = ConvertersFabric.Instance.GetCaptionConverter(SourceObject.Caption).Convert(); if (SourceObject.NextMaxId != null) media.NextMaxId = SourceObject.NextMaxId; if (SourceObject.Likers != null && SourceObject.Likers?.Count > 0) foreach (var liker in SourceObject.Likers) - media.Likers.Add(ConvertersFabric.GetUserShortConverter(liker).Convert()); + media.Likers.Add(ConvertersFabric.Instance.GetUserShortConverter(liker).Convert()); if (SourceObject.UserTagList?.In != null && SourceObject.UserTagList?.In?.Count > 0) foreach (var tag in SourceObject.UserTagList.In) - media.Tags.Add(ConvertersFabric.GetUserTagConverter(tag).Convert()); + media.Tags.Add(ConvertersFabric.Instance.GetUserTagConverter(tag).Convert()); + if (SourceObject.PreviewComments != null) + foreach (var comment in SourceObject.PreviewComments) + media.PreviewComments.Add(ConvertersFabric.Instance.GetCommentConverter(comment).Convert()); + if (SourceObject.Location != null) + media.Location = ConvertersFabric.Instance.GetLocationConverter(SourceObject.Location).Convert(); if (SourceObject.Images?.Candidates == null) return media; foreach (var image in SourceObject.Images.Candidates) media.Images.Add(new InstaImage(image.Url, int.Parse(image.Width), int.Parse(image.Height))); diff --git a/InstaSharper/Converters/InstaMediaListConverter.cs b/InstaSharper/Converters/InstaMediaListConverter.cs index f6036476..e23d6671 100644 --- a/InstaSharper/Converters/InstaMediaListConverter.cs +++ b/InstaSharper/Converters/InstaMediaListConverter.cs @@ -14,7 +14,7 @@ public InstaMediaList Convert() if (SourceObject == null) throw new ArgumentNullException($"Source object"); var mediaList = new InstaMediaList(); mediaList.AddRange( - SourceObject.Medias.Select(ConvertersFabric.GetSingleMediaConverter) + SourceObject.Medias.Select(ConvertersFabric.Instance.GetSingleMediaConverter) .Select(converter => converter.Convert())); mediaList.PageSize = SourceObject.ResultsCount; return mediaList; diff --git a/InstaSharper/Converters/InstaRecentActivityConverter.cs b/InstaSharper/Converters/InstaRecentActivityConverter.cs index 679baeea..ee2f4d83 100644 --- a/InstaSharper/Converters/InstaRecentActivityConverter.cs +++ b/InstaSharper/Converters/InstaRecentActivityConverter.cs @@ -38,7 +38,8 @@ public InstaRecentActivityFeed Convert() }; if (SourceObject.Args.InlineFollow.UserInfo != null) activityStory.InlineFollow.User = - ConvertersFabric.GetUserShortConverter(SourceObject.Args.InlineFollow.UserInfo).Convert(); + ConvertersFabric.Instance.GetUserShortConverter(SourceObject.Args.InlineFollow.UserInfo) + .Convert(); } return activityStory; } diff --git a/InstaSharper/Converters/InstaRecipientsConverter.cs b/InstaSharper/Converters/InstaRecipientsConverter.cs index e1eea11f..15c4f070 100644 --- a/InstaSharper/Converters/InstaRecipientsConverter.cs +++ b/InstaSharper/Converters/InstaRecipientsConverter.cs @@ -3,36 +3,47 @@ namespace InstaSharper.Converters { - internal class InstaRecipientsConverter : IObjectConverter + internal class InstaRecipientsConverter : IObjectConverter { public IInstaRecipientsResponse SourceObject { get; set; } - public InstaRecipientThreads Convert() + public InstaRecipients Convert() { - var recipients = new InstaRecipientThreads + var recipients = new InstaRecipients { ExpiresIn = SourceObject.Expires, Filtered = SourceObject.Filtered, RankToken = SourceObject.RankToken, RequestId = SourceObject.RequestId }; - foreach (var recipient in SourceObject.RankedRecipients) - { - var rankedThread = new InstaRankedRecipientThread + if (SourceObject?.RankedRecipients?.Length > 0) + foreach (var recipient in SourceObject.RankedRecipients) { - Canonical = recipient.Thread.Canonical, - Named = recipient.Thread.Named, - Pending = recipient.Thread.Pending, - ThreadId = recipient.Thread.ThreadId, - ThreadTitle = recipient.Thread.ThreadTitle, - ThreadType = recipient.Thread.ThreadType, - ViewerId = recipient.Thread.ViewerId - }; - foreach (var user in recipient.Thread.Users) - rankedThread.Users.Add(ConvertersFabric.GetUserShortConverter(user).Convert()); - recipients.Items.Add(rankedThread); - } + if (recipient == null) continue; + + if (recipient.Thread != null) + { + var rankedThread = new InstaRankedRecipientThread + { + Canonical = recipient.Thread.Canonical, + Named = recipient.Thread.Named, + Pending = recipient.Thread.Pending, + ThreadId = recipient.Thread.ThreadId, + ThreadTitle = recipient.Thread.ThreadTitle, + ThreadType = recipient.Thread.ThreadType, + ViewerId = recipient.Thread.ViewerId + }; + foreach (var user in recipient.Thread.Users) + rankedThread.Users.Add(ConvertersFabric.Instance.GetUserShortConverter(user).Convert()); + recipients.Threads.Add(rankedThread); + } + if (recipient.User != null) + { + var user = ConvertersFabric.Instance.GetUserShortConverter(recipient.User).Convert(); + recipients.Users.Add(user); + } + } return recipients; } } diff --git a/InstaSharper/Converters/InstaReelMentionConverter.cs b/InstaSharper/Converters/InstaReelMentionConverter.cs index c2a4957e..f1234ef0 100644 --- a/InstaSharper/Converters/InstaReelMentionConverter.cs +++ b/InstaSharper/Converters/InstaReelMentionConverter.cs @@ -21,9 +21,9 @@ public InstaReelMention Convert() Y = SourceObject.Y }; if (SourceObject.Hashtag != null) - mention.Hashtag = ConvertersFabric.GetHashTagConverter(SourceObject.Hashtag).Convert(); + mention.Hashtag = ConvertersFabric.Instance.GetHashTagConverter(SourceObject.Hashtag).Convert(); if (SourceObject.User != null) - mention.User = ConvertersFabric.GetUserShortConverter(SourceObject.User).Convert(); + mention.User = ConvertersFabric.Instance.GetUserShortConverter(SourceObject.User).Convert(); return mention; } } diff --git a/InstaSharper/Converters/InstaStoryConverter.cs b/InstaSharper/Converters/InstaStoryConverter.cs index 60d16ed5..5174f132 100644 --- a/InstaSharper/Converters/InstaStoryConverter.cs +++ b/InstaSharper/Converters/InstaStoryConverter.cs @@ -1,6 +1,7 @@ using System; using InstaSharper.Classes.Models; using InstaSharper.Classes.ResponseWrappers; +using InstaSharper.Helpers; namespace InstaSharper.Converters { @@ -11,7 +12,29 @@ internal class InstaStoryConverter : IObjectConverter ConvertMedia(List mediasResponse) foreach (var instaUserFeedItemResponse in mediasResponse) { if (instaUserFeedItemResponse?.Type != 0) continue; - var feedItem = ConvertersFabric.GetSingleMediaConverter(instaUserFeedItemResponse).Convert(); + var feedItem = ConvertersFabric.Instance.GetSingleMediaConverter(instaUserFeedItemResponse) + .Convert(); medias.Add(feedItem); } return medias; @@ -29,10 +30,10 @@ List ConvertMedia(List mediasResponse) feed.RankedMedias.AddRange(ConvertMedia(SourceObject.RankedItems)); feed.Medias.AddRange(ConvertMedia(SourceObject.Medias)); - feed.Medias.PageSize = feed.Medias.Count; + feed.NextId = SourceObject.NextMaxId; foreach (var story in SourceObject.Stories) { - var feedItem = ConvertersFabric.GetStoryConverter(story).Convert(); + var feedItem = ConvertersFabric.Instance.GetStoryConverter(story).Convert(); feed.Stories.Add(feedItem); } return feed; diff --git a/InstaSharper/Converters/InstaTopLiveConverter.cs b/InstaSharper/Converters/InstaTopLiveConverter.cs index 589a0775..8de64b18 100644 --- a/InstaSharper/Converters/InstaTopLiveConverter.cs +++ b/InstaSharper/Converters/InstaTopLiveConverter.cs @@ -14,7 +14,7 @@ public InstaTopLive Convert() var storyTray = new InstaTopLive {RankedPosition = SourceObject.RankedPosition}; foreach (var owner in SourceObject.BroadcastOwners) { - var userOwner = ConvertersFabric.GetUserShortConverter(owner).Convert(); + var userOwner = ConvertersFabric.Instance.GetUserShortConverter(owner).Convert(); storyTray.BroadcastOwners.Add(userOwner); } return storyTray; diff --git a/InstaSharper/Converters/InstaUserConverter.cs b/InstaSharper/Converters/InstaUserConverter.cs index 59731f62..b6b85eeb 100644 --- a/InstaSharper/Converters/InstaUserConverter.cs +++ b/InstaSharper/Converters/InstaUserConverter.cs @@ -11,20 +11,23 @@ internal class InstaUserConverter : IObjectConverter>(); + + var listMedia = new InstaMediaListResponse(); + feed.ForEach(item => listMedia.Medias.Add(item.Media)); + + return listMedia; + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + serializer.Serialize(writer, value); + } + + private class InstaCollectionItemsToMedia + { + [JsonProperty("media")] + public InstaMediaItemResponse Media { get; set; } + } + } +} \ No newline at end of file diff --git a/InstaSharper/Converters/Json/InstaMediaDataConverter.cs b/InstaSharper/Converters/Json/InstaMediaDataConverter.cs new file mode 100644 index 00000000..c715c215 --- /dev/null +++ b/InstaSharper/Converters/Json/InstaMediaDataConverter.cs @@ -0,0 +1,32 @@ +using System; +using InstaSharper.Classes.ResponseWrappers; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace InstaSharper.Converters.Json +{ + internal class InstaMediaDataConverter : JsonConverter + { + public override bool CanConvert(Type objectType) + { + return objectType == typeof(InstaMediaItemResponse); + } + + public override object ReadJson(JsonReader reader, + Type objectType, + object existingValue, + JsonSerializer serializer) + { + var root = JToken.Load(reader); + var media = root.ToObject(); + if (media?.Pk != null) return media; + var mediaToken = root.SelectToken("media"); + return mediaToken.ToObject(); + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + serializer.Serialize(writer, value); + } + } +} \ No newline at end of file diff --git a/InstaSharper/Converters/Json/InstaTagFeedDataConverter.cs b/InstaSharper/Converters/Json/InstaTagFeedDataConverter.cs index 81b68739..79fa4f5e 100644 --- a/InstaSharper/Converters/Json/InstaTagFeedDataConverter.cs +++ b/InstaSharper/Converters/Json/InstaTagFeedDataConverter.cs @@ -34,8 +34,10 @@ List GetMedias(JToken token) .Where(media => !string.IsNullOrEmpty(media?.Pk)).ToList(); } - feed.Medias.AddRange(GetMedias(items)); - feed.RankedItems.AddRange(GetMedias(rankedItems)); + if (items != null) + feed.Medias.AddRange(GetMedias(items)); + if (rankedItems != null) + feed.RankedItems.AddRange(GetMedias(rankedItems)); if (storiesTray == null) return feed; foreach (var storyItem in storiesTray) { diff --git a/InstaSharper/Helpers/ConvertersHelper.cs b/InstaSharper/Helpers/ConvertersHelper.cs new file mode 100644 index 00000000..b89b2bdc --- /dev/null +++ b/InstaSharper/Helpers/ConvertersHelper.cs @@ -0,0 +1,12 @@ +using InstaSharper.Converters; + +namespace InstaSharper.Helpers +{ + public static class ConvertersHelper + { + public static IConvertersFabric GetDefaultFabric() + { + return ConvertersFabric.Instance; + } + } +} \ No newline at end of file diff --git a/InstaSharper/Helpers/DateTimeHelper.cs b/InstaSharper/Helpers/DateTimeHelper.cs index f7332585..aeb2ae4d 100644 --- a/InstaSharper/Helpers/DateTimeHelper.cs +++ b/InstaSharper/Helpers/DateTimeHelper.cs @@ -65,5 +65,11 @@ public static long ToUnixTime(this DateTime date) return 0; } } + + public static long GetUnixTimestampSeconds() + { + var timeSpan = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0); + return (long) timeSpan.TotalSeconds; + } } } \ No newline at end of file diff --git a/InstaSharper/Helpers/DictionaryExtensions.cs b/InstaSharper/Helpers/DictionaryExtensions.cs new file mode 100644 index 00000000..ae0658a1 --- /dev/null +++ b/InstaSharper/Helpers/DictionaryExtensions.cs @@ -0,0 +1,28 @@ +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Text; + +namespace InstaSharper.Helpers +{ + internal static class DictionaryExtensions + { + public static string AsQueryString(this Dictionary parameters) + { + if (!parameters.Any()) + return ""; + + var builder = new StringBuilder("?"); + + var separator = ""; + foreach (var kvp in parameters.Where(kvp => kvp.Value != null)) + { + builder.AppendFormat("{0}{1}={2}", separator, WebUtility.UrlEncode(kvp.Key), + WebUtility.UrlEncode(kvp.Value)); + separator = "&"; + } + + return builder.ToString(); + } + } +} \ No newline at end of file diff --git a/InstaSharper/Helpers/UriCreator.cs b/InstaSharper/Helpers/UriCreator.cs index 91b23f16..8530f04f 100644 --- a/InstaSharper/Helpers/UriCreator.cs +++ b/InstaSharper/Helpers/UriCreator.cs @@ -24,18 +24,22 @@ public static Uri GetUserUri(string username) return userUriBuilder.Uri; } - public static Uri GetUserFeedUri() + public static Uri GetUserFeedUri(string maxId = "") { if (!Uri.TryCreate(BaseInstagramUri, InstaApiConstants.TIMELINEFEED, out var instaUri)) throw new Exception("Cant create timeline feed URI"); - return instaUri; + return !string.IsNullOrEmpty(maxId) + ? new UriBuilder(instaUri) {Query = $"max_id={maxId}"}.Uri + : instaUri; } - public static Uri GetUserMediaListUri(string userPk) + public static Uri GetUserMediaListUri(long userPk, string nextId = "") { if (!Uri.TryCreate(BaseInstagramUri, InstaApiConstants.USEREFEED + userPk, out var instaUri)) throw new Exception("Cant create URI for user media retrieval"); - return instaUri; + return !string.IsNullOrEmpty(nextId) + ? new UriBuilder(instaUri) {Query = $"max_id={nextId}"}.Uri + : instaUri; } public static Uri GetLoginUri() @@ -45,19 +49,17 @@ public static Uri GetLoginUri() return instaUri; } - public static Uri GetTimelineWithMaxIdUri(string nextId) + public static Uri GetTwoFactorLoginUri() { - if (!Uri.TryCreate(BaseInstagramUri, InstaApiConstants.TIMELINEFEED, out var instaUri)) - throw new Exception("Cant create search URI for timeline"); - var uriBuilder = new UriBuilder(instaUri) {Query = $"max_id={nextId}"}; - return uriBuilder.Uri; + if (!Uri.TryCreate(BaseInstagramUri, InstaApiConstants.ACCOUNTS_2FA_LOGIN, out var instaUri)) + throw new Exception("Cant create URI for user 2FA login"); + return instaUri; } - public static Uri GetMediaListWithMaxIdUri(string userPk, string nextId) + public static Uri GetTimelineWithMaxIdUri(string nextId) { - if ( - !Uri.TryCreate(new Uri(InstaApiConstants.INSTAGRAM_URL), InstaApiConstants.USEREFEED + userPk, - out var instaUri)) throw new Exception("Cant create URI for media list"); + if (!Uri.TryCreate(BaseInstagramUri, InstaApiConstants.TIMELINEFEED, out var instaUri)) + throw new Exception("Cant create search URI for timeline"); var uriBuilder = new UriBuilder(instaUri) {Query = $"max_id={nextId}"}; return uriBuilder.Uri; } @@ -69,7 +71,7 @@ public static Uri GetCurrentUserUri() return instaUri; } - internal static Uri GetUserFollowersUri(string userPk, string rankToken, string maxId = "") + internal static Uri GetUserFollowersUri(long userPk, string rankToken, string maxId = "") { if ( !Uri.TryCreate(BaseInstagramUri, string.Format(InstaApiConstants.GET_USER_FOLLOWERS, userPk, rankToken), @@ -79,7 +81,7 @@ internal static Uri GetUserFollowersUri(string userPk, string rankToken, string return uriBuilder.Uri; } - internal static Uri GetUserFollowingUri(string userPk, string rankToken, string maxId = "") + internal static Uri GetUserFollowingUri(long userPk, string rankToken, string maxId = "") { if ( !Uri.TryCreate(BaseInstagramUri, string.Format(InstaApiConstants.GET_USER_FOLLOWING, userPk, rankToken), @@ -89,11 +91,13 @@ internal static Uri GetUserFollowingUri(string userPk, string rankToken, string return uriBuilder.Uri; } - public static Uri GetTagFeedUri(string tag) + public static Uri GetTagFeedUri(string tag, string maxId = "") { if (!Uri.TryCreate(BaseInstagramUri, string.Format(InstaApiConstants.GET_TAG_FEED, tag), out var instaUri)) throw new Exception("Cant create URI for discover tag feed"); - return instaUri; + return !string.IsNullOrEmpty(maxId) + ? new UriBuilder(instaUri) {Query = $"max_id={maxId}"}.Uri + : instaUri; } public static Uri GetLogoutUri() @@ -135,7 +139,7 @@ public static Uri GetDirectInboxThreadUri(string threadId) return instaUri; } - public static Uri GetUserTagsUri(string userPk, string rankToken, string maxId = null) + public static Uri GetUserTagsUri(long userPk, string rankToken, string maxId = null) { if (!Uri.TryCreate(BaseInstagramUri, string.Format(InstaApiConstants.GET_USER_TAGS, userPk), out var instaUri)) @@ -195,12 +199,14 @@ public static Uri GetLikeMediaUri(string mediaId) return instaUri; } - public static Uri GetMediaCommentsUri(string mediaId) + public static Uri GetMediaCommentsUri(string mediaId, string nextId = "") { if (!Uri.TryCreate(BaseInstagramUri, string.Format(InstaApiConstants.MEDIA_COMMENTS, mediaId), out var instaUri)) throw new Exception("Cant create URI for getting media comments"); - return instaUri; + return !string.IsNullOrEmpty(nextId) + ? new UriBuilder(instaUri) {Query = $"max_id={nextId}"}.Uri + : instaUri; } public static Uri GetMediaLikersUri(string mediaId) @@ -227,6 +233,24 @@ public static Uri GetUnFollowUserUri(long userId) return instaUri; } + + public static Uri GetBlockUserUri(long userId) + { + if (!Uri.TryCreate(BaseInstagramUri, string.Format(InstaApiConstants.BLOCK_USER, userId), + out var instaUri)) + throw new Exception("Cant create URI for getting media likers"); + return instaUri; + } + + public static Uri GetUnBlockUserUri(long userId) + { + if (!Uri.TryCreate(BaseInstagramUri, string.Format(InstaApiConstants.UNBLOCK_USER, userId), + out var instaUri)) + throw new Exception("Cant create URI for getting media likers"); + return instaUri; + } + + public static Uri GetUriSetAccountPrivate() { if (!Uri.TryCreate(BaseInstagramUri, InstaApiConstants.SET_ACCOUNT_PRIVATE, out var instaUri)) @@ -292,6 +316,14 @@ public static Uri GetMediaConfigureUri() return instaUri; } + public static Uri GetMediaAlbumConfigureUri() + { + if ( + !Uri.TryCreate(BaseInstagramUri, InstaApiConstants.MEDIA_ALBUM_CONFIGURE, out var instaUri)) + throw new Exception("Cant create URI for configuring media album"); + return instaUri; + } + public static Uri GetStoryFeedUri() { if (!Uri.TryCreate(BaseInstagramUri, InstaApiConstants.GET_STORY_TRAY, out var instaUri)) @@ -362,5 +394,61 @@ public static Uri GetUserReelFeedUri(long userId) throw new Exception("Can't create URI for getting user reel feed"); return instaUri; } + + public static Uri GetCollectionUri(long collectionId) + { + if (!Uri.TryCreate(BaseInstagramUri, string.Format(InstaApiConstants.GET_COLLECTION, collectionId), + out var instaUri)) + throw new Exception("Can't create URI for getting collection"); + return instaUri; + } + + public static Uri GetEditCollectionUri(long collectionId) + { + if (!Uri.TryCreate(BaseInstagramUri, string.Format(InstaApiConstants.EDIT_COLLECTION, collectionId), + out var instaUri)) + throw new Exception("Can't create URI for editing collection"); + return instaUri; + } + + public static Uri GetCollectionsUri() + { + if (!Uri.TryCreate(BaseInstagramUri, InstaApiConstants.GET_LIST_COLLECTIONS, + out var instaUri)) + throw new Exception("Can't create URI for getting collections"); + return instaUri; + } + + public static Uri GetCreateCollectionUri() + { + if (!Uri.TryCreate(BaseInstagramUri, InstaApiConstants.CREATE_COLLECTION, + out var instaUri)) + throw new Exception("Can't create URI for creating collection"); + return instaUri; + } + + public static Uri GetDeleteCollectionUri(long collectionId) + { + if (!Uri.TryCreate(BaseInstagramUri, string.Format(InstaApiConstants.DELETE_COLLECTION, collectionId), + out var instaUri)) + throw new Exception("Can't create URI for deleting collection"); + return instaUri; + } + + public static Uri GetMediaIdFromUrlUri(Uri uri) + { + if (!Uri.TryCreate(BaseInstagramUri, string.Format(InstaApiConstants.GET_MEDIAID, uri.AbsoluteUri), + out var instaUri)) + throw new Exception("Can't create URI for getting media id"); + return instaUri; + } + + public static Uri GetShareLinkFromMediaId(string mediaId) + { + if (!Uri.TryCreate(BaseInstagramUri, string.Format(InstaApiConstants.GET_SHARE_LINK, mediaId), + out var instaUri)) + throw new Exception("Can't create URI for getting share link"); + return instaUri; + } } } \ No newline at end of file diff --git a/InstaSharper/InstaSharper.csproj b/InstaSharper/InstaSharper.csproj index 315e2df4..65856013 100644 --- a/InstaSharper/InstaSharper.csproj +++ b/InstaSharper/InstaSharper.csproj @@ -11,9 +11,9 @@ false false True - 1.3.0 - 1.3.0 - 1.3.0 + 1.4.0 + 1.4.0 + 1.4.0 Private API for Instagram @@ -27,6 +27,11 @@ + + + + + diff --git a/README.md b/README.md index d9891645..430a8d6a 100644 --- a/README.md +++ b/README.md @@ -4,11 +4,13 @@ Tokenless, butthurtless private API for Instagram. Get account information, medi Note that: there is a simple [Instagram API](https://github.com/a-legotin/InstagramAPI-Web) based on web-version of Instagram. This repository based on Instagram API for mobile devices. [![Build status](https://ci.appveyor.com/api/projects/status/6os0fhi1awbplbka?svg=true)](https://ci.appveyor.com/project/a-legotin/instasharper) -[![Build Status](https://travis-ci.org/a-legotin/InstaSharper.svg?branch=master)](https://travis-ci.org/a-legotin/InstaSharper) +[![Build status](https://travis-ci.org/a-legotin/InstaSharper.svg?branch=master)](https://travis-ci.org/a-legotin/InstaSharper) +[![NuGet](https://img.shields.io/nuget/v/InstaSharper.svg)](https://www.nuget.org/packages/InstaSharper/) +[![MyGet](https://img.shields.io/myget/a-legotin/v/instasharper-develop.svg)](https://www.myget.org/feed/Details/instasharper-develop) [![Telegram chat](https://img.shields.io/badge/telegram-channel-blue.svg)](https://t.me/instasharper) [![GitHub stars](https://img.shields.io/github/stars/a-legotin/InstaSharper.svg)](https://github.com/a-legotin/InstaSharper/stargazers) -#### Current version: 1.3.0 [Stable], 1.4.0 [Under development] +#### Current version: 1.3.8 [Stable], 1.4.0 [Under development] ## Overview This project intends to provide all the features available in the Instagram API up to 12.0.0.7.91. It is being developed in C# for .NET Framework 4.5.2 and .NET Standart 2.0 @@ -23,7 +25,7 @@ Build with dotnet core. Can be used on Mac, Linux, Windows. Use library as dll, reference from [nuget](https://www.nuget.org/packages/InstaSharper/) or clone source code. Pre-release version available at [myget feed](https://www.myget.org/feed/Details/instasharper-develop) together with [symbols](https://www.myget.org/F/instasharper-develop/symbols/) -##Features +## Features Currently the library supports following coverage of the following Instagram APIs: diff --git a/appveyor.yml b/appveyor.yml index 9e35819e..ed0b2b64 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -version: 1.3.{build} +version: 1.4.{build} os: Previous Visual Studio 2017 platform: Any CPU configuration: Release