private static string GetUriString(TwitterAPIEndpoint endpoint) { switch (endpoint) { case TwitterAPIEndpoint.SearchTweets: return("https://api.twitter.com/1.1/search/tweets.json"); case TwitterAPIEndpoint.UsersShow: return("https://api.twitter.com/1.1/users/show.json"); case TwitterAPIEndpoint.RateLimitStatus: return("https://api.twitter.com/1.1/application/rate_limit_status.json"); case TwitterAPIEndpoint.UsersLookup: return("https://api.twitter.com/1.1/users/lookup.json"); case TwitterAPIEndpoint.OAuthAuthorize: return("https://api.twitter.com/oauth/request_token"); case TwitterAPIEndpoint.FollowersIDs: return("https://api.twitter.com/1.1/followers/ids.json"); case TwitterAPIEndpoint.FriendsIDs: return("https://api.twitter.com/1.1/friends/ids.json"); default: throw new Exception("Unimplemented TwitterAPIEndpoint"); } }
internal static string HashtagSearchQuery(string hashtag, TwitterAPIEndpoint endpoint) { string result = $"q=%23{hashtag}&count={TweetCount}&tweet_mode={TweetMode}&include_entities={IncludeEntities}"; if (hashtag == QueryHistory.Get[endpoint].LastQuery && QueryHistory.Get[endpoint].NextMaxID != "") { result += $"&max_id={QueryHistory.Get[endpoint].NextMaxID}"; } return(result); }
internal QueryInfo this[TwitterAPIEndpoint key] { get { if (!history.ContainsKey(key)) { history.Add(key, new QueryInfo()); } return(history[key]); } }
internal static bool UsesMaxID(TwitterAPIEndpoint endpoint) { switch (endpoint) { case TwitterAPIEndpoint.SearchTweets: return(true); default: return(false); } }
private static void HandleFailure(TwitterAPIEndpoint endpoint, UserRateLimitInfo userStatus, AuthenticationType?authTypeUsed, HttpResponseMessage response) { if (!response.IsSuccessStatusCode) { if (authTypeUsed.Value == AuthenticationType.Application) { RateLimitCache.Get[endpoint].Update(RateLimitCache.Get[endpoint].Limit - 1); } else { userStatus?.Update(userStatus.Limit - 1); } } }
private static void LogQuery(string query, TwitterAPIEndpoint endpoint, IQueryResults results) { QueryHistory.Get[endpoint].LastQuery = query; if (QueryInfo.UsesMaxID(endpoint)) { TweetSearchResults statusResults = results as TweetSearchResults; // Exclude lowest ID to prevent duplicate results string nextMaxID = (long.TryParse(statusResults.MinStatusID, out long result)) ? (result - 1).ToString() : ""; QueryHistory.Get[endpoint].NextMaxID = nextMaxID; } if (QueryInfo.UsesCursor(endpoint)) { UserIdsResults idResults = results as UserIdsResults; QueryHistory.Get[endpoint].NextCursor = idResults.NextCursorStr; } }
private static HttpMethod HttpMethod(TwitterAPIEndpoint endpoint) { switch (endpoint) { case TwitterAPIEndpoint.SearchTweets: case TwitterAPIEndpoint.UsersShow: case TwitterAPIEndpoint.RateLimitStatus: case TwitterAPIEndpoint.OAuthAuthorize: case TwitterAPIEndpoint.FriendsIDs: case TwitterAPIEndpoint.FollowersIDs: return(System.Net.Http.HttpMethod.Get); case TwitterAPIEndpoint.UsersLookup: return(System.Net.Http.HttpMethod.Post); default: throw new Exception("Unimplemented TwitterAPIEndpoint"); } }
internal static bool UsesCursor(TwitterAPIEndpoint endpoint) { switch (endpoint) { case TwitterAPIEndpoint.SearchTweets: case TwitterAPIEndpoint.UsersShow: case TwitterAPIEndpoint.UsersLookup: case TwitterAPIEndpoint.RateLimitStatus: case TwitterAPIEndpoint.OAuthAuthorize: return(false); case TwitterAPIEndpoint.FriendsIDs: case TwitterAPIEndpoint.FollowersIDs: return(true); default: throw new Exception("Unimplemented TwitterAPIEndpoint"); } }
private static void UpdateRateLimits(TwitterAPIEndpoint endpoint, UserRateLimitInfo userStatus, AuthenticationType?authTypeUsed, HttpResponseMessage response) { if (response.Headers.TryGetValues("x-rate-limit-remaining", out IEnumerable <string> remaining) && response.Headers.TryGetValues("x-rate-limit-reset", out IEnumerable <string> reset)) { if (int.TryParse(remaining.FirstOrDefault(), out int limitRemaining) && double.TryParse(reset.FirstOrDefault(), out double secondsUntilReset)) { if (authTypeUsed.Value == AuthenticationType.Application) { RateLimitCache.Get[endpoint].Update(limitRemaining, UntilEpochSeconds(secondsUntilReset)); } else { userStatus?.Update(limitRemaining, UntilEpochSeconds(secondsUntilReset)); } } } }
internal static string FollowersFriendsIDsQueryByID(string userID, TwitterAPIEndpoint endpoint) { string result = $"user_id={userID}"; return(result); }
internal static string FollowersFriendsIDsQuery(string screenName, TwitterAPIEndpoint endpoint) { string result = $"screen_name={screenName}"; return(result); }
internal static string HashtagIgnoreRepeatSearchQuery(string hashtag, TwitterAPIEndpoint endpoint) { return($"q=%23{hashtag}&count={TweetCount}&tweet_mode={TweetMode}&include_entities={IncludeEntities}"); }
internal AppRateLimitInfo this[TwitterAPIEndpoint type] => cache[type];
internal static string UserIDSearchQuery(string id, TwitterAPIEndpoint endpoint) { return($"user_id={id}&include_entities={IncludeEntities}"); }
internal static string UserSearchQuery(string screenName, TwitterAPIEndpoint endpoint) { return($"screen_name={screenName}&include_entities={IncludeEntities}"); }
internal static Uri GetUri(TwitterAPIEndpoint endpoint, string query) => new UriBuilder(GetUriString(endpoint)) { Query = query }.Uri;
/// <summary> /// Attempts to authenticate a call to the Twitter API with the given query string and authentication info. Updates rate limits if applicable. /// </summary> /// <param name="config"></param> /// <param name="authType"></param> /// <param name="endpoint"></param> /// <param name="query"></param> /// <param name="token"></param> /// <param name="tokenSecret"></param> /// <param name="userStatus"></param> /// <returns></returns> internal static async Task <string> GetResponse(IConfiguration config, AuthenticationType authType, TwitterAPIEndpoint endpoint, string query, string token, string tokenSecret, UserRateLimitInfo userStatus) { // Yep, that's a lot of parameters. AppRateLimitInfo appStatus = RateLimitCache.Get[endpoint]; if (!appStatus.Available) { throw new Exception($"Endpoint {endpoint} currently unavailable due to rate limits. Time until reset: {appStatus.UntilReset}"); } appStatus.ResetIfNeeded(); try { using (var client = new HttpClient()) using (var request = new HttpRequestMessage(HttpMethod(endpoint), GetUri(endpoint, query))) { if (!TryAuthorize(request, config, authType, token, tokenSecret, appStatus, userStatus, out AuthenticationType? authTypeUsed)) { throw new Exception($"Unable to authenticate Twitter API call of type {authType}, endpoint {endpoint}."); } using (HttpResponseMessage response = await client.SendAsync(request, HttpCompletionOption.ResponseContentRead)) { HandleFailure(endpoint, userStatus, authTypeUsed, response); response.EnsureSuccessStatusCode(); UpdateRateLimits(endpoint, userStatus, authTypeUsed, response); return(await response.Content.ReadAsStringAsync()); } } } catch (Exception ex) { throw new Exception($"Twitter API call failed: {ex.Message}"); } }
internal static string UserLookupQuery(IEnumerable <string> userIds, TwitterAPIEndpoint endpoint) { return($"user_id={string.Join(",", userIds)}"); }
/// <summary> /// Get rate limits for the current user. /// </summary> /// <param name="rateLimitDb"></param> /// <param name="endpoint"></param> /// <param name="userManager"></param> /// <param name="user"></param> /// <returns></returns> internal static UserRateLimitInfo GetCurrentUserInfo(RateLimitDbContext rateLimitDb, TwitterAPIEndpoint endpoint, UserManager <ApplicationUser> userManager, ClaimsPrincipal user) { if (!user.Identity.IsAuthenticated || user.GetTwitterAccessToken() == null) { return(null); } var info = rateLimitDb.Find(typeof(UserRateLimitInfo), new object[] { userManager.GetUserId(user), endpoint }) as UserRateLimitInfo; if (info == null) { info = new UserRateLimitInfo() { UserID = userManager.GetUserId(user), Type = endpoint }; rateLimitDb.Add(info); rateLimitDb.SaveChanges(); } else { info.ResetIfNeeded(); rateLimitDb.Update(info); rateLimitDb.SaveChanges(); } return(info); }
private async Task <T> GetResults <T>(string query, AuthenticationType authType, Func <string, TwitterAPIEndpoint, string> buildQueryString, TwitterAPIEndpoint endpoint) where T : IQueryResults { UserRateLimitInfo userInfo = RateLimitController.GetCurrentUserInfo(rateLimitDb, endpoint, userManager, User); string responseBody = await TwitterAPIUtils.GetResponse( Configuration, authType, endpoint, buildQueryString(query, endpoint), User.GetTwitterAccessToken(), User.GetTwitterAccessTokenSecret(), userInfo); if (userInfo != null) { userInfo.ResetIfNeeded(); rateLimitDb.Update(userInfo); rateLimitDb.SaveChanges(); } if (responseBody == null) { return(default);
private static void LogQuerySet(IEnumerable <string> querySet, TwitterAPIEndpoint endpoint, IEnumerable <IQueryResults> resultSet) { QueryHistory.Get[endpoint].LastQuerySet = querySet; }