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 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)); } } } }
private static bool UseUserAuth(AuthenticationType authType, string token, UserRateLimitInfo userStatus) { return(userStatus != null && userStatus.Available && token != null && (authType == AuthenticationType.User || authType == AuthenticationType.Both)); }
private static bool UseApplicationAuth(AuthenticationType authType, string token, AppRateLimitInfo appStatus, UserRateLimitInfo userStatus) { return(appStatus.Available && (authType == AuthenticationType.Application || (authType == AuthenticationType.Both && (null == token || !userStatus.Available)))); }
private static bool TryAuthorize(HttpRequestMessage request, IConfiguration config, AuthenticationType authType, string token, string tokenSecret, AppRateLimitInfo appStatus, UserRateLimitInfo userStatus, out AuthenticationType?used) { if (UseApplicationAuth(authType, token, appStatus, userStatus)) { AddBearerAuth(config, request); used = AuthenticationType.Application; } else if (UseUserAuth(authType, token, userStatus)) { AddUserAuth(config, request, token, tokenSecret); used = AuthenticationType.User; } else { used = null; return(false); } return(true); }
/// <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}"); } }