diff --git a/src/Tweetinvi.Controllers/Timeline/TimelinesV2Controller.cs b/src/Tweetinvi.Controllers/Timeline/TimelinesV2Controller.cs new file mode 100644 index 000000000..3c3011958 --- /dev/null +++ b/src/Tweetinvi.Controllers/Timeline/TimelinesV2Controller.cs @@ -0,0 +1,71 @@ +using System; +using System.Threading.Tasks; +using Tweetinvi.Core.Iterators; +using Tweetinvi.Core.Controllers.V2; +using Tweetinvi.Core.Web; +using Tweetinvi.Models; +using Tweetinvi.Models.V2; +using Tweetinvi.Parameters.V2; + +namespace Tweetinvi.Controllers.Timeline +{ + public class TimelinesV2Controller : ITimelinesV2Controller + { + private readonly ITimelinesV2QueryExecutor _queryExecutor; + + public TimelinesV2Controller(ITimelinesV2QueryExecutor queryExecutor) + { + _queryExecutor = queryExecutor; + } + + private ITwitterPageIterator, string> GetIterator( + Func>> Method, + IGetTimelinesV2Parameters parameters, + ITwitterRequest request) + { + Func>> getNext = nextToken => + { + var cursoredParameters = new GetTimelinesV2Parameters(parameters) + { + PaginationToken = nextToken + }; + + return Method(cursoredParameters, request); + }; + + var twitterCursorResult = new TwitterPageIterator, string>( + parameters.PaginationToken, + getNext, + page => + { + if (page.Model.Tweets.Length == 0) + { + return null; + } + + return page.Model.Meta.NextToken; + }, + page => + { + if (page.Model.Tweets.Length == 0) + { + return true; + } + + return page.Model.Meta.NextToken == null; + }); + + return twitterCursorResult; + } + + public ITwitterPageIterator, string> GetUserTweetsTimelineIterator(IGetTimelinesV2Parameters parameters, ITwitterRequest request) + { + return GetIterator(_queryExecutor.GetUserTweetsTimelineAsync, parameters, request); + } + + public ITwitterPageIterator, string> GetUserMentionedTimelineIterator(IGetTimelinesV2Parameters parameters, ITwitterRequest request) + { + return GetIterator(_queryExecutor.GetUserMentionedTimelineAsync, parameters, request); + } + } +} diff --git a/src/Tweetinvi.Controllers/Timeline/TimelinesV2QueryExecutor.cs b/src/Tweetinvi.Controllers/Timeline/TimelinesV2QueryExecutor.cs new file mode 100644 index 000000000..4a9bb3a13 --- /dev/null +++ b/src/Tweetinvi.Controllers/Timeline/TimelinesV2QueryExecutor.cs @@ -0,0 +1,44 @@ +using System.Threading.Tasks; +using Tweetinvi.Core.QueryGenerators.V2; +using Tweetinvi.Core.Web; +using Tweetinvi.Models; +using Tweetinvi.Models.V2; +using Tweetinvi.Parameters.V2; + +namespace Tweetinvi.Controllers.Timeline +{ + public interface ITimelinesV2QueryExecutor + { + Task> GetUserTweetsTimelineAsync(IGetTimelinesV2Parameters parameters, ITwitterRequest request); + Task> GetUserMentionedTimelineAsync(IGetTimelinesV2Parameters parameters, ITwitterRequest request); + } + + public class TimelinesV2QueryExecutor : ITimelinesV2QueryExecutor + { + private readonly JsonContentFactory _jsonContentFactory; + private readonly ITimelinesV2QueryGenerator _timelinesQueryGenerator; + private readonly ITwitterAccessor _twitterAccessor; + + public TimelinesV2QueryExecutor( + JsonContentFactory jsonContentFactory, + ITimelinesV2QueryGenerator timelinesQueryGenerator, + ITwitterAccessor twitterAccessor) + { + _jsonContentFactory = jsonContentFactory; + _timelinesQueryGenerator = timelinesQueryGenerator; + _twitterAccessor = twitterAccessor; + } + + public Task> GetUserTweetsTimelineAsync(IGetTimelinesV2Parameters parameters, ITwitterRequest request) + { + request.Query.Url = _timelinesQueryGenerator.GetTimelineQuery(parameters); + request.Query.HttpMethod = HttpMethod.GET; + return _twitterAccessor.ExecuteRequestAsync(request); + } + public Task> GetUserMentionedTimelineAsync(IGetTimelinesV2Parameters parameters, ITwitterRequest request) + { + request.Query.Url = _timelinesQueryGenerator.GetMentionTimelineQuery(parameters); + return _twitterAccessor.ExecuteRequestAsync(request); + } + } +} diff --git a/src/Tweetinvi.Controllers/Timeline/TimelinesV2QueryGenerator.cs b/src/Tweetinvi.Controllers/Timeline/TimelinesV2QueryGenerator.cs new file mode 100644 index 000000000..611a0ac86 --- /dev/null +++ b/src/Tweetinvi.Controllers/Timeline/TimelinesV2QueryGenerator.cs @@ -0,0 +1,48 @@ +using System.Text; +using Tweetinvi.Controllers.Properties; +using Tweetinvi.Core.Extensions; +using Tweetinvi.Core.QueryGenerators.V2; +using Tweetinvi.Parameters.V2; + +namespace Tweetinvi.Controllers.Timeline +{ + public class TimelinesV2QueryGenerator : ITimelinesV2QueryGenerator + { + private readonly ITweetsV2QueryGenerator _tweetsV2QueryGenerator; + + public TimelinesV2QueryGenerator(ITweetsV2QueryGenerator tweetsV2QueryGenerator) + { + _tweetsV2QueryGenerator = tweetsV2QueryGenerator; + } + + public string GetTimelineQuery(IGetTimelinesV2Parameters parameters) + { + var query = new StringBuilder($"{Resources.UserV2_Get}/{parameters.UserId}/tweets"); + AddTimelineFieldsParameters(parameters, query); + query.AddFormattedParameterToQuery(parameters.FormattedCustomQueryParameters); + return query.ToString(); + } + + public string GetMentionTimelineQuery(IGetTimelinesV2Parameters parameters) + { + var query = new StringBuilder($"{Resources.UserV2_Get}/{parameters.UserId}/mentions"); + AddTimelineFieldsParameters(parameters, query); + query.AddFormattedParameterToQuery(parameters.FormattedCustomQueryParameters); + return query.ToString(); + } + + public void AddTimelineFieldsParameters(IGetTimelinesV2Parameters parameters, StringBuilder query) + { + _tweetsV2QueryGenerator.AddTweetFieldsParameters(parameters, query); + + // specific timeline parameters + query.AddParameterToQuery("exclude", parameters.Exclude); + query.AddParameterToQuery("max_results", parameters.MaxResults); + query.AddParameterToQuery("pagination_token", parameters.PaginationToken); + query.AddParameterToQuery("since_id", parameters.SinceId); + query.AddParameterToQuery("start_time", parameters.StartTime?.ToString("yyy-MM-ddThh:mm:ssZ")); + query.AddParameterToQuery("end_time", parameters.EndTime?.ToString("yyy-MM-ddThh:mm:ssZ")); + query.AddParameterToQuery("until_id", parameters.UntilId); + } + } +} diff --git a/src/Tweetinvi.Controllers/TweetinviControllersModule.cs b/src/Tweetinvi.Controllers/TweetinviControllersModule.cs index 6fa05b782..0549f0662 100644 --- a/src/Tweetinvi.Controllers/TweetinviControllersModule.cs +++ b/src/Tweetinvi.Controllers/TweetinviControllersModule.cs @@ -52,6 +52,7 @@ private void InitializeControllers(ITweetinviContainer container) container.RegisterType(RegistrationLifetime.InstancePerApplication); container.RegisterType(RegistrationLifetime.InstancePerApplication); container.RegisterType(RegistrationLifetime.InstancePerApplication); + container.RegisterType(RegistrationLifetime.InstancePerApplication); container.RegisterType(RegistrationLifetime.InstancePerApplication); } @@ -77,6 +78,7 @@ private void InitializeQueryExecutors(ITweetinviContainer container) container.RegisterType(); container.RegisterType(); container.RegisterType(); + container.RegisterType(); container.RegisterType(RegistrationLifetime.InstancePerApplication); } @@ -107,6 +109,7 @@ private void InitializeQueryGenerators(ITweetinviContainer container) container.RegisterType(RegistrationLifetime.InstancePerApplication); container.RegisterType(RegistrationLifetime.InstancePerApplication); container.RegisterType(RegistrationLifetime.InstancePerApplication); + container.RegisterType(RegistrationLifetime.InstancePerApplication); container.RegisterType(RegistrationLifetime.InstancePerApplication); } diff --git a/src/Tweetinvi.Core/Core/Controllers/V2/ITimelinesV2Controller.cs b/src/Tweetinvi.Core/Core/Controllers/V2/ITimelinesV2Controller.cs new file mode 100644 index 000000000..abde4c8f1 --- /dev/null +++ b/src/Tweetinvi.Core/Core/Controllers/V2/ITimelinesV2Controller.cs @@ -0,0 +1,14 @@ +using Tweetinvi.Core.Iterators; +using Tweetinvi.Core.Web; +using Tweetinvi.Models; +using Tweetinvi.Models.V2; +using Tweetinvi.Parameters.V2; + +namespace Tweetinvi.Core.Controllers.V2 +{ + public interface ITimelinesV2Controller + { + ITwitterPageIterator, string> GetUserTweetsTimelineIterator(IGetTimelinesV2Parameters parameters, ITwitterRequest request); + ITwitterPageIterator, string> GetUserMentionedTimelineIterator(IGetTimelinesV2Parameters parameters, ITwitterRequest request); + } +} diff --git a/src/Tweetinvi.Core/Core/QueryGenerators/V2/ITimelinesV2QueryGenerator.cs b/src/Tweetinvi.Core/Core/QueryGenerators/V2/ITimelinesV2QueryGenerator.cs new file mode 100644 index 000000000..e85d14fc5 --- /dev/null +++ b/src/Tweetinvi.Core/Core/QueryGenerators/V2/ITimelinesV2QueryGenerator.cs @@ -0,0 +1,12 @@ +using System.Text; +using Tweetinvi.Parameters.V2; + +namespace Tweetinvi.Core.QueryGenerators.V2 +{ + public interface ITimelinesV2QueryGenerator + { + string GetTimelineQuery(IGetTimelinesV2Parameters parameters); + string GetMentionTimelineQuery(IGetTimelinesV2Parameters parameters); + void AddTimelineFieldsParameters(IGetTimelinesV2Parameters parameters, StringBuilder query); + } +} diff --git a/src/Tweetinvi.Core/Public/Client/Clients/V2/ITimelinesV2Client.cs b/src/Tweetinvi.Core/Public/Client/Clients/V2/ITimelinesV2Client.cs new file mode 100644 index 000000000..1d89886c5 --- /dev/null +++ b/src/Tweetinvi.Core/Public/Client/Clients/V2/ITimelinesV2Client.cs @@ -0,0 +1,27 @@ +using Tweetinvi.Iterators; +using Tweetinvi.Models.V2; +using Tweetinvi.Parameters.V2; + +namespace Tweetinvi.Client.V2 +{ + public interface ITimelinesV2Client + { + /// + /// Returns Tweets composed by a single user, specified by the requested user ID. By default, + /// the most recent ten Tweets are returned per request. Using pagination, the most recent + /// 3,200 Tweets can be retrieved. + /// Read more : https://developer.twitter.com/en/docs/twitter-api/tweets/timelines/api-reference/get-users-id-tweets + /// + /// The Timeline + ITwitterRequestIterator GetUserTweetsTimelineIterator(IGetTimelinesV2Parameters parameters); + + /// + /// Returns Tweets mentioning a single user specified by the requested user ID. By default, + /// the most recent ten Tweets are returned per request. Using pagination, up to the most + /// recent 800 Tweets can be retrieved. + /// Read more : https://developer.twitter.com/en/docs/twitter-api/tweets/timelines/api-reference/get-users-id-mentions + /// + ITwitterRequestIterator GetUserMentionedTimelineIterator(IGetTimelinesV2Parameters parameters); + + } +} diff --git a/src/Tweetinvi.Core/Public/Client/Requesters/V2/ITimelinesV2Requester.cs b/src/Tweetinvi.Core/Public/Client/Requesters/V2/ITimelinesV2Requester.cs new file mode 100644 index 000000000..658529736 --- /dev/null +++ b/src/Tweetinvi.Core/Public/Client/Requesters/V2/ITimelinesV2Requester.cs @@ -0,0 +1,27 @@ +using Tweetinvi.Core.Iterators; +using Tweetinvi.Core.Web; +using Tweetinvi.Models.V2; +using Tweetinvi.Parameters.V2; + +namespace Tweetinvi.Client.Requesters.V2 +{ + public interface ITimelinesV2Requester + { + /// + /// Returns Tweets composed by a single user, specified by the requested user ID. By default, + /// the most recent ten Tweets are returned per request. Using pagination, the most recent + /// 3,200 Tweets can be retrieved. + /// Read more : https://developer.twitter.com/en/docs/twitter-api/tweets/timelines/api-reference/get-users-id-tweets + /// + /// The Timeline + ITwitterPageIterator, string> GetUserTweetsTimelineIterator(IGetTimelinesV2Parameters parameters); + + /// + /// Returns Tweets mentioning a single user specified by the requested user ID. By default, + /// the most recent ten Tweets are returned per request. Using pagination, up to the most + /// recent 800 Tweets can be retrieved. + /// Read more : https://developer.twitter.com/en/docs/twitter-api/tweets/timelines/api-reference/get-users-id-mentions + /// + ITwitterPageIterator, string> GetUserMentionedTimelineIterator(IGetTimelinesV2Parameters parameters); + } +} diff --git a/src/Tweetinvi.Core/Public/ITwitterClient.cs b/src/Tweetinvi.Core/Public/ITwitterClient.cs index 3050c299c..d3e3389b7 100644 --- a/src/Tweetinvi.Core/Public/ITwitterClient.cs +++ b/src/Tweetinvi.Core/Public/ITwitterClient.cs @@ -129,7 +129,6 @@ public interface ITwitterClient /// IParametersValidator ParametersValidator { get; } - /// /// Creates skeleton request representing a request from the client /// diff --git a/src/Tweetinvi.Core/Public/Models/V2/Responses/TimelinesV2Response.cs b/src/Tweetinvi.Core/Public/Models/V2/Responses/TimelinesV2Response.cs new file mode 100644 index 000000000..0f1c7c6c2 --- /dev/null +++ b/src/Tweetinvi.Core/Public/Models/V2/Responses/TimelinesV2Response.cs @@ -0,0 +1,29 @@ +using Newtonsoft.Json; + +namespace Tweetinvi.Models.V2 +{ + public class TimelinesV2Response + { + /// + /// Tweets returned by the request + /// + [JsonProperty("data")] public TweetV2[] Tweets { get; set; } = new TweetV2[0]; + + /// + /// Contains all the requested expansions + /// + [JsonProperty("includes")] public TweetIncludesV2 Includes { get; set; } + + /// + /// All errors that prevented Twitter to send some data, + /// but which did not prevent the request to be resolved. + /// + [JsonProperty("errors")] public ErrorV2[] Errors { get; set; } + + /// + /// This object contains information about the Timeline Tweets + /// returned in the current request and pagination details. + /// + [JsonProperty("meta")] public TimelineMetaV2 Meta { get; set; } + } +} diff --git a/src/Tweetinvi.Core/Public/Models/V2/TimelineMetaV2.cs b/src/Tweetinvi.Core/Public/Models/V2/TimelineMetaV2.cs new file mode 100644 index 000000000..7d3cd797d --- /dev/null +++ b/src/Tweetinvi.Core/Public/Models/V2/TimelineMetaV2.cs @@ -0,0 +1,40 @@ +using System; +using Newtonsoft.Json; + +namespace Tweetinvi.Models.V2 +{ + /// + /// A Tweet + /// Read more here : https://developer.twitter.com/en/docs/twitter-api/tweets/timelines/api-reference/get-users-id-tweets + /// + public class TimelineMetaV2 + { + /// + /// The Tweet ID of the oldest Tweet returned in the response. + /// + [JsonProperty("oldest_id")] public string OldestId { get; set; } + + /// + /// The Tweet ID of the most recent Tweet returned in the response. + /// + [JsonProperty("newest_id")] public string NewestId { get; set; } + + /// + /// The number of Tweet results returned in the response. + /// + [JsonProperty("count")] public int Count { get; set; } + + /// + /// A value that encodes the next 'page' of results that can be requested, + /// via the pagination_token request parameter. + /// + [JsonProperty("next_token")] public string NextToken { get; set; } + + /// + /// A value that encodes the previous 'page' of results that can be requested, + /// via the pagination_token request parameter. + /// + [JsonProperty("previous_token")] public string PreviousToken { get; set; } + + } +} diff --git a/src/Tweetinvi.Core/Public/Parameters/V2/TimelinesClientV2/GetTimelinesV2Parameters.cs b/src/Tweetinvi.Core/Public/Parameters/V2/TimelinesClientV2/GetTimelinesV2Parameters.cs new file mode 100644 index 000000000..87f8b8298 --- /dev/null +++ b/src/Tweetinvi.Core/Public/Parameters/V2/TimelinesClientV2/GetTimelinesV2Parameters.cs @@ -0,0 +1,53 @@ +using System; +using Tweetinvi.Core.Parameters; + +namespace Tweetinvi.Parameters.V2 +{ + public interface IGetTimelinesV2Parameters : IBaseTweetsV2Parameters + { + string UserId { get; set; } + string Exclude { get; set; } + int? MaxResults { get; set; } + string PaginationToken { get; set; } + string SinceId { get; set; } + DateTime? StartTime { get; set; } + DateTime? EndTime { get; set; } + string UntilId { get; set; } + } + + public class GetTimelinesV2Parameters : BaseTweetsV2Parameters, IGetTimelinesV2Parameters + { + public GetTimelinesV2Parameters(string userId) + { + UserId = userId; + } + public GetTimelinesV2Parameters(IGetTimelinesV2Parameters parameters) + { + UserId = parameters.UserId; + Exclude = parameters?.Exclude; + MaxResults = parameters?.MaxResults; + PaginationToken = parameters?.PaginationToken; + SinceId = parameters?.SinceId; + StartTime = parameters?.StartTime; + EndTime = parameters?.EndTime; + UntilId = parameters?.UntilId; + + Expansions = parameters.Expansions; + MediaFields = parameters.MediaFields; + PlaceFields = parameters.PlaceFields; + PollFields = parameters.PollFields; + TweetFields = parameters.TweetFields; + UserFields = parameters.UserFields; + + } + + public string UserId { get; set; } + public string Exclude { get; set; } + public int? MaxResults { get; set; } + public string PaginationToken { get; set; } + public string SinceId { get; set; } + public DateTime? StartTime { get; set; } + public DateTime? EndTime { get; set; } + public string UntilId { get; set; } + } +} diff --git a/src/Tweetinvi/Client/Clients/V2/TimelinesV2Client.cs b/src/Tweetinvi/Client/Clients/V2/TimelinesV2Client.cs new file mode 100644 index 000000000..ff5f45dc4 --- /dev/null +++ b/src/Tweetinvi/Client/Clients/V2/TimelinesV2Client.cs @@ -0,0 +1,34 @@ +using System; +using Tweetinvi.Core.Iterators; +using Tweetinvi.Iterators; +using Tweetinvi.Client.Requesters.V2; +using Tweetinvi.Core.Web; +using Tweetinvi.Models.V2; +using Tweetinvi.Parameters.V2; + + +namespace Tweetinvi.Client.V2 +{ + public class TimelinesV2Client : ITimelinesV2Client + { + private readonly ITimelinesV2Requester _timelinesV2Requester; + + public TimelinesV2Client(ITimelinesV2Requester timelinesV2Requester) + { + _timelinesV2Requester = timelinesV2Requester; + } + + public ITwitterRequestIterator GetUserTweetsTimelineIterator(IGetTimelinesV2Parameters parameters) + { + var iterator = _timelinesV2Requester.GetUserTweetsTimelineIterator(parameters); + + return new IteratorPageProxy, TimelinesV2Response, string>(iterator, input => input.Model); + } + + public ITwitterRequestIterator GetUserMentionedTimelineIterator(IGetTimelinesV2Parameters parameters) + { + var iterator = _timelinesV2Requester.GetUserMentionedTimelineIterator(parameters); + return new IteratorPageProxy, TimelinesV2Response, string>(iterator, input => input.Model); + } + } +} diff --git a/src/Tweetinvi/Client/Requesters/V2/TimelinesV2Requester.cs b/src/Tweetinvi/Client/Requesters/V2/TimelinesV2Requester.cs new file mode 100644 index 000000000..3fb52fdcb --- /dev/null +++ b/src/Tweetinvi/Client/Requesters/V2/TimelinesV2Requester.cs @@ -0,0 +1,35 @@ +using System.Threading.Tasks; +using Tweetinvi.Core.Controllers.V2; +using Tweetinvi.Core.Iterators; +using Tweetinvi.Core.Events; +using Tweetinvi.Core.Web; +using Tweetinvi.Models.V2; +using Tweetinvi.Parameters.V2; + +namespace Tweetinvi.Client.Requesters.V2 +{ + public class TimelinesV2Requester : BaseRequester, ITimelinesV2Requester + { + private readonly ITimelinesV2Controller _timelinesV2Controller; + + public TimelinesV2Requester( + ITwitterClient client, + ITwitterClientEvents twitterClientEvents, + ITimelinesV2Controller timelinesV2Controller) : base(client, twitterClientEvents) + { + _timelinesV2Controller = timelinesV2Controller; + } + + public ITwitterPageIterator, string> GetUserTweetsTimelineIterator(IGetTimelinesV2Parameters parameters) + { + var request = TwitterClient.CreateRequest(); + return _timelinesV2Controller.GetUserTweetsTimelineIterator(parameters, request); + } + + public ITwitterPageIterator, string> GetUserMentionedTimelineIterator(IGetTimelinesV2Parameters parameters) + { + var request = TwitterClient.CreateRequest(); + return _timelinesV2Controller.GetUserMentionedTimelineIterator(parameters, request); + } + } +} diff --git a/src/Tweetinvi/TweetinviModule.cs b/src/Tweetinvi/TweetinviModule.cs index b499e3f99..52b98c2ca 100644 --- a/src/Tweetinvi/TweetinviModule.cs +++ b/src/Tweetinvi/TweetinviModule.cs @@ -76,6 +76,8 @@ public void Initialize(ITweetinviContainer container) container.RegisterType(RegistrationLifetime.InstancePerApplication); container.RegisterType(RegistrationLifetime.InstancePerApplication); + container.RegisterType(RegistrationLifetime.InstancePerApplication); + container.RegisterType(RegistrationLifetime.InstancePerApplication); } } } \ No newline at end of file diff --git a/src/Tweetinvi/TwitterClient.cs b/src/Tweetinvi/TwitterClient.cs index 0f3752618..49259d4d8 100644 --- a/src/Tweetinvi/TwitterClient.cs +++ b/src/Tweetinvi/TwitterClient.cs @@ -136,6 +136,7 @@ void BeforeRegistrationDelegate(object sender, TweetinviContainerEventArgs args) TweetsV2 = _tweetinviContainer.Resolve(); UsersV2 = _tweetinviContainer.Resolve(); StreamsV2 = _tweetinviContainer.Resolve(); + TimelinesV2 = _tweetinviContainer.Resolve(); _tweetinviContainer.AssociatedClient = this; @@ -188,6 +189,9 @@ void BeforeRegistrationDelegate(object sender, TweetinviContainerEventArgs args) /// public IStreamsV2Client StreamsV2 { get; } + /// + public ITimelinesV2Client TimelinesV2 { get; } + /// public IExternalClientEvents Events => _twitterClientEvents;