diff --git a/src/BirdsiteLive.Twitter/Models/ApiStatistics.cs b/src/BirdsiteLive.Twitter/Models/ApiStatistics.cs new file mode 100644 index 0000000..55325ad --- /dev/null +++ b/src/BirdsiteLive.Twitter/Models/ApiStatistics.cs @@ -0,0 +1,12 @@ +namespace BirdsiteLive.Twitter.Models +{ + public class ApiStatistics + { + public int UserCallsCount { get; set; } + public int UserCallsMax { get; set; } + public int TweetCallsCount { get; set; } + public int TweetCallsMax { get; set; } + public int TimelineCallsCount { get; set; } + public int TimelineCallsMax { get; set; } + } +} \ No newline at end of file diff --git a/src/BirdsiteLive.Twitter/Statistics/TwitterStatisticsHandler.cs b/src/BirdsiteLive.Twitter/Statistics/TwitterStatisticsHandler.cs new file mode 100644 index 0000000..ef64fd1 --- /dev/null +++ b/src/BirdsiteLive.Twitter/Statistics/TwitterStatisticsHandler.cs @@ -0,0 +1,80 @@ +using System.Threading; +using System.Timers; +using BirdsiteLive.Twitter.Models; + +namespace BirdsiteLive.Statistics.Domain +{ + public interface ITwitterStatisticsHandler + { + void CalledUserApi(); + void CalledTweetApi(); + void CalledTimelineApi(); + ApiStatistics GetStatistics(); + } + + //Rate limits: https://developer.twitter.com/en/docs/twitter-api/v1/rate-limits + public class TwitterStatisticsHandler : ITwitterStatisticsHandler + { + private static int _previousUserCalls; + private static int _previousTweetCalls; + private static int _previousTimelineCalls; + + private static int _userCalls; + private static int _tweetCalls; + private static int _timelineCalls; + + private static System.Timers.Timer _resetTimer; + + #region Ctor + public TwitterStatisticsHandler() + { + if (_resetTimer == null) + { + _resetTimer = new System.Timers.Timer(); + _resetTimer.Elapsed += OnTimeResetEvent; + _resetTimer.Interval = 15 * 60 * 1000; // 15" + _resetTimer.Enabled = true; + } + } + #endregion + + private void OnTimeResetEvent(object sender, ElapsedEventArgs e) + { + _previousUserCalls = _userCalls; + _previousTweetCalls = _tweetCalls; + _previousTimelineCalls = _timelineCalls; + + Interlocked.Exchange(ref _userCalls, 0); + Interlocked.Exchange(ref _tweetCalls, 0); + Interlocked.Exchange(ref _timelineCalls, 0); + } + + public void CalledUserApi() //GET users/show - 900/15mins + { + Interlocked.Increment(ref _userCalls); + } + + public void CalledTweetApi() //GET statuses/lookup - 300/15mins + { + Interlocked.Increment(ref _tweetCalls); + } + + public void CalledTimelineApi() // GET statuses/user_timeline - 1500/15 mins + { + Interlocked.Increment(ref _timelineCalls); + } + + public ApiStatistics GetStatistics() + { + return new ApiStatistics + { + UserCallsCount = _previousUserCalls, + UserCallsMax = 900, + TweetCallsCount = _previousTweetCalls, + TweetCallsMax = 300, + TimelineCallsCount = _previousTimelineCalls, + TimelineCallsMax = 1500 + }; + } + } +} \ No newline at end of file diff --git a/src/BirdsiteLive.Twitter/TwitterService.cs b/src/BirdsiteLive.Twitter/TwitterService.cs index f49f089..47f799c 100644 --- a/src/BirdsiteLive.Twitter/TwitterService.cs +++ b/src/BirdsiteLive.Twitter/TwitterService.cs @@ -4,6 +4,7 @@ using System.IO; using System.Linq; using System.Threading.Tasks; using BirdsiteLive.Common.Settings; +using BirdsiteLive.Statistics.Domain; using BirdsiteLive.Twitter.Extractors; using BirdsiteLive.Twitter.Models; using Tweetinvi; @@ -24,12 +25,14 @@ namespace BirdsiteLive.Twitter { private readonly TwitterSettings _settings; private readonly ITweetExtractor _tweetExtractor; + private readonly ITwitterStatisticsHandler _statisticsHandler; #region Ctor - public TwitterService(TwitterSettings settings, ITweetExtractor tweetExtractor) + public TwitterService(TwitterSettings settings, ITweetExtractor tweetExtractor, ITwitterStatisticsHandler statisticsHandler) { _settings = settings; _tweetExtractor = tweetExtractor; + _statisticsHandler = statisticsHandler; Auth.SetApplicationOnlyCredentials(_settings.ConsumerKey, _settings.ConsumerSecret, true); } #endregion @@ -37,6 +40,7 @@ namespace BirdsiteLive.Twitter public TwitterUser GetUser(string username) { var user = User.GetUserFromScreenName(username); + _statisticsHandler.CalledUserApi(); if (user == null) return null; return new TwitterUser @@ -55,6 +59,8 @@ namespace BirdsiteLive.Twitter { TweetinviConfig.CurrentThreadSettings.TweetMode = TweetMode.Extended; var tweet = Tweet.GetTweet(statusId); + _statisticsHandler.CalledTweetApi(); + if (tweet == null) return null; //TODO: test this return _tweetExtractor.Extract(tweet); } @@ -63,10 +69,12 @@ namespace BirdsiteLive.Twitter TweetinviConfig.CurrentThreadSettings.TweetMode = TweetMode.Extended; var user = User.GetUserFromScreenName(username); + _statisticsHandler.CalledUserApi(); var tweets = new List(); if (fromTweetId == -1) { var timeline = Timeline.GetUserTimeline(user.Id, nberTweets); + _statisticsHandler.CalledTimelineApi(); if (timeline != null) tweets.AddRange(timeline); } else @@ -77,11 +85,11 @@ namespace BirdsiteLive.Twitter MaximumNumberOfTweetsToRetrieve = nberTweets }; var timeline = Timeline.GetUserTimeline(user.Id, timelineRequestParameters); + _statisticsHandler.CalledTimelineApi(); if (timeline != null) tweets.AddRange(timeline); } return tweets.Select(_tweetExtractor.Extract).ToArray(); - //return tweets.Where(x => returnReplies || string.IsNullOrWhiteSpace(x.InReplyToScreenName)).ToArray(); } } } diff --git a/src/BirdsiteLive/Controllers/StatisticsController.cs b/src/BirdsiteLive/Controllers/StatisticsController.cs index 90d58e2..58e4a7a 100644 --- a/src/BirdsiteLive/Controllers/StatisticsController.cs +++ b/src/BirdsiteLive/Controllers/StatisticsController.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using BirdsiteLive.DAL.Contracts; -using BirdsiteLive.Models.StatisticsModels; +using BirdsiteLive.Statistics.Domain; using Microsoft.AspNetCore.Mvc; namespace BirdsiteLive.Controllers @@ -12,21 +12,24 @@ namespace BirdsiteLive.Controllers { private readonly ITwitterUserDal _twitterUserDal; private readonly IFollowersDal _followersDal; + private readonly ITwitterStatisticsHandler _twitterStatistics; #region Ctor - public StatisticsController(ITwitterUserDal twitterUserDal, IFollowersDal followersDal) + public StatisticsController(ITwitterUserDal twitterUserDal, IFollowersDal followersDal, ITwitterStatisticsHandler twitterStatistics) { _twitterUserDal = twitterUserDal; _followersDal = followersDal; + _twitterStatistics = twitterStatistics; } #endregion public async Task Index() { - var stats = new Statistics + var stats = new Models.StatisticsModels.Statistics { FollowersCount = await _followersDal.GetFollowersCountAsync(), - TwitterUserCount = await _twitterUserDal.GetTwitterUsersCountAsync() + TwitterUserCount = await _twitterUserDal.GetTwitterUsersCountAsync(), + TwitterStatistics = _twitterStatistics.GetStatistics() }; return View(stats); } diff --git a/src/BirdsiteLive/Models/StatisticsModels/Statistics.cs b/src/BirdsiteLive/Models/StatisticsModels/Statistics.cs index bf62e58..59cff91 100644 --- a/src/BirdsiteLive/Models/StatisticsModels/Statistics.cs +++ b/src/BirdsiteLive/Models/StatisticsModels/Statistics.cs @@ -1,8 +1,11 @@ -namespace BirdsiteLive.Models.StatisticsModels +using BirdsiteLive.Twitter.Models; + +namespace BirdsiteLive.Models.StatisticsModels { public class Statistics { public int FollowersCount { get; set; } public int TwitterUserCount { get; set; } + public ApiStatistics TwitterStatistics { get; set; } } } \ No newline at end of file diff --git a/src/BirdsiteLive/Views/Statistics/Index.cshtml b/src/BirdsiteLive/Views/Statistics/Index.cshtml index 31dc9f7..90a5e02 100644 --- a/src/BirdsiteLive/Views/Statistics/Index.cshtml +++ b/src/BirdsiteLive/Views/Statistics/Index.cshtml @@ -6,7 +6,15 @@

Statistics

+

Instance

+ +

Twitter API

+ \ No newline at end of file