This commit is contained in:
Vincent Cloutier 2022-05-05 20:15:07 -04:00
parent ed3faab924
commit 6b2579db50
23 changed files with 226 additions and 201 deletions

View file

@ -2,7 +2,7 @@
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework> <TargetFramework>net6</TargetFramework>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>net6</TargetFramework>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>net6</TargetFramework>
</PropertyGroup> </PropertyGroup>
</Project> </Project>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>net6</TargetFramework>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>net6</TargetFramework>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>net6</TargetFramework>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>net6</TargetFramework>
<LangVersion>latest</LangVersion> <LangVersion>latest</LangVersion>
</PropertyGroup> </PropertyGroup>

View file

@ -1,12 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>net6</TargetFramework>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="5.0.0" /> <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="5.0.0" />
<PackageReference Include="TweetinviAPI" Version="4.0.3" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View file

@ -2,129 +2,109 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text.Json;
using BirdsiteLive.Twitter.Models; using BirdsiteLive.Twitter.Models;
using Tweetinvi.Models;
using Tweetinvi.Models.Entities;
namespace BirdsiteLive.Twitter.Extractors namespace BirdsiteLive.Twitter.Extractors
{ {
public interface ITweetExtractor public interface ITweetExtractor
{ {
ExtractedTweet Extract(ITweet tweet); ExtractedTweet Extract(JsonDocument tweet);
} }
public class TweetExtractor : ITweetExtractor public class TweetExtractor : ITweetExtractor
{ {
public ExtractedTweet Extract(ITweet tweet) public ExtractedTweet Extract(JsonDocument tweet)
{ {
var extractedTweet = new ExtractedTweet var extractedTweet = new ExtractedTweet
{ {
Id = tweet.Id, Id = tweet.RootElement.GetProperty("data").GetProperty("id").GetInt64(),
InReplyToStatusId = tweet.InReplyToStatusId, InReplyToStatusId = tweet.RootElement.GetProperty("data").GetProperty("in_reply_to_status_id").GetInt64(),
InReplyToAccount = tweet.InReplyToScreenName, InReplyToAccount = tweet.RootElement.GetProperty("data").GetProperty("in_reply_to_status_id").GetString(),
MessageContent = ExtractMessage(tweet), MessageContent = ExtractMessage(tweet),
Media = ExtractMedia(tweet), Media = ExtractMedia(tweet),
CreatedAt = tweet.CreatedAt.ToUniversalTime(), CreatedAt = tweet.RootElement.GetProperty("data").GetProperty("in_reply_to_status_id").GetDateTime(),
IsReply = tweet.InReplyToUserId != null, IsReply = false,
IsThread = tweet.InReplyToUserId != null && tweet.InReplyToUserId == tweet.CreatedBy.Id, IsThread = false,
IsRetweet = tweet.IsRetweet || tweet.QuotedStatusId != null, IsRetweet = false,
RetweetUrl = ExtractRetweetUrl(tweet) RetweetUrl = ExtractRetweetUrl(tweet)
}; };
return extractedTweet; return extractedTweet;
} }
private string ExtractRetweetUrl(ITweet tweet) private string ExtractRetweetUrl(JsonDocument tweet)
{ {
if (tweet.IsRetweet) var retweetId = "123";
{
if (tweet.RetweetedTweet != null)
{
return tweet.RetweetedTweet.Url;
}
if (tweet.FullText.Contains("https://t.co/"))
{
var retweetId = tweet.FullText.Split(new[] { "https://t.co/" }, StringSplitOptions.RemoveEmptyEntries).Last();
return $"https://t.co/{retweetId}"; return $"https://t.co/{retweetId}";
}
} }
return null; private string ExtractMessage(JsonDocument tweet)
}
public string ExtractMessage(ITweet tweet)
{ {
var message = tweet.FullText; return "hello world";
var tweetUrls = tweet.Media.Select(x => x.URL).Distinct(); //var message = tweet.FullText;
//var tweetUrls = tweet.Media.Select(x => x.URL).Distinct();
if (tweet.IsRetweet && message.StartsWith("RT") && tweet.RetweetedTweet != null) //if (tweet.IsRetweet && message.StartsWith("RT") && tweet.RetweetedTweet != null)
{ //{
message = tweet.RetweetedTweet.FullText; // message = tweet.RetweetedTweet.FullText;
tweetUrls = tweet.RetweetedTweet.Media.Select(x => x.URL).Distinct(); // tweetUrls = tweet.RetweetedTweet.Media.Select(x => x.URL).Distinct();
//}
//foreach (var tweetUrl in tweetUrls)
//{
// if(tweet.IsRetweet)
// message = tweet.RetweetedTweet.FullText.Replace(tweetUrl, string.Empty).Trim();
// else
// message = message.Replace(tweetUrl, string.Empty).Trim();
//}
//if (tweet.QuotedTweet != null) message = $"[Quote {{RT}}]{Environment.NewLine}{message}";
//if (tweet.IsRetweet)
//{
// if (tweet.RetweetedTweet != null && !message.StartsWith("RT"))
// message = $"[{{RT}} @{tweet.RetweetedTweet.CreatedBy.ScreenName}]{Environment.NewLine}{message}";
// else if (tweet.RetweetedTweet != null && message.StartsWith($"RT @{tweet.RetweetedTweet.CreatedBy.ScreenName}:"))
// message = message.Replace($"RT @{tweet.RetweetedTweet.CreatedBy.ScreenName}:", $"[{{RT}} @{tweet.RetweetedTweet.CreatedBy.ScreenName}]{Environment.NewLine}");
// else
// message = message.Replace("RT", "[{{RT}}]");
//}
//// Expand URLs
//foreach (var url in tweet.Urls.OrderByDescending(x => x.URL.Length))
// message = message.Replace(url.URL, url.ExpandedURL);
//return message;
} }
foreach (var tweetUrl in tweetUrls) private ExtractedMedia[] ExtractMedia(JsonDocument tweet)
{ {
if(tweet.IsRetweet) //var media = tweet.Media;
message = tweet.RetweetedTweet.FullText.Replace(tweetUrl, string.Empty).Trim(); //if (tweet.IsRetweet && tweet.RetweetedTweet != null)
else // media = tweet.RetweetedTweet.Media;
message = message.Replace(tweetUrl, string.Empty).Trim();
//var result = new List<ExtractedMedia>();
//foreach (var m in media)
//{
// var mediaUrl = GetMediaUrl(m);
// var mediaType = GetMediaType(m.MediaType, mediaUrl);
// if (mediaType == null) continue;
// var att = new ExtractedMedia
// {
// MediaType = mediaType,
// Url = mediaUrl
// };
// result.Add(att);
//}
//return result.ToArray();
return Array.Empty<ExtractedMedia>();
} }
if (tweet.QuotedTweet != null) message = $"[Quote {{RT}}]{Environment.NewLine}{message}";
if (tweet.IsRetweet)
{
if (tweet.RetweetedTweet != null && !message.StartsWith("RT"))
message = $"[{{RT}} @{tweet.RetweetedTweet.CreatedBy.ScreenName}]{Environment.NewLine}{message}";
else if (tweet.RetweetedTweet != null && message.StartsWith($"RT @{tweet.RetweetedTweet.CreatedBy.ScreenName}:"))
message = message.Replace($"RT @{tweet.RetweetedTweet.CreatedBy.ScreenName}:", $"[{{RT}} @{tweet.RetweetedTweet.CreatedBy.ScreenName}]{Environment.NewLine}");
else
message = message.Replace("RT", "[{{RT}}]");
}
// Expand URLs private string GetMediaType(string mediaType, string mediaUrl)
foreach (var url in tweet.Urls.OrderByDescending(x => x.URL.Length))
message = message.Replace(url.URL, url.ExpandedURL);
return message;
}
public ExtractedMedia[] ExtractMedia(ITweet tweet)
{
var media = tweet.Media;
if (tweet.IsRetweet && tweet.RetweetedTweet != null)
media = tweet.RetweetedTweet.Media;
var result = new List<ExtractedMedia>();
foreach (var m in media)
{
var mediaUrl = GetMediaUrl(m);
var mediaType = GetMediaType(m.MediaType, mediaUrl);
if (mediaType == null) continue;
var att = new ExtractedMedia
{
MediaType = mediaType,
Url = mediaUrl
};
result.Add(att);
}
return result.ToArray();
}
public string GetMediaUrl(IMediaEntity media)
{
switch (media.MediaType)
{
case "photo": return media.MediaURLHttps;
case "animated_gif": return media.VideoDetails.Variants[0].URL;
case "video": return media.VideoDetails.Variants.OrderByDescending(x => x.Bitrate).First().URL;
default: return null;
}
}
public string GetMediaType(string mediaType, string mediaUrl)
{ {
switch (mediaType) switch (mediaType)
{ {

View file

@ -3,13 +3,16 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using BirdsiteLive.Common.Settings; using BirdsiteLive.Common.Settings;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Tweetinvi; using System.Net.Http;
using System.Net.Http.Headers;
using System.Text.Json;
namespace BirdsiteLive.Twitter.Tools namespace BirdsiteLive.Twitter.Tools
{ {
public interface ITwitterAuthenticationInitializer public interface ITwitterAuthenticationInitializer
{ {
void EnsureAuthenticationIsInitialized(); String Token { get; }
Task EnsureAuthenticationIsInitialized();
} }
public class TwitterAuthenticationInitializer : ITwitterAuthenticationInitializer public class TwitterAuthenticationInitializer : ITwitterAuthenticationInitializer
@ -17,7 +20,11 @@ namespace BirdsiteLive.Twitter.Tools
private readonly TwitterSettings _settings; private readonly TwitterSettings _settings;
private readonly ILogger<TwitterAuthenticationInitializer> _logger; private readonly ILogger<TwitterAuthenticationInitializer> _logger;
private static bool _initialized; private static bool _initialized;
private readonly SemaphoreSlim _semaphoregate = new SemaphoreSlim(1); private readonly HttpClient _httpClient = new HttpClient();
private String _token;
public String Token {
get { return _token; }
}
#region Ctor #region Ctor
public TwitterAuthenticationInitializer(TwitterSettings settings, ILogger<TwitterAuthenticationInitializer> logger) public TwitterAuthenticationInitializer(TwitterSettings settings, ILogger<TwitterAuthenticationInitializer> logger)
@ -27,36 +34,42 @@ namespace BirdsiteLive.Twitter.Tools
} }
#endregion #endregion
public void EnsureAuthenticationIsInitialized() public async Task EnsureAuthenticationIsInitialized()
{ {
if (_initialized) return; if (_initialized) return;
_semaphoregate.Wait();
try await InitTwitterCredentials();
{
if (_initialized) return;
InitTwitterCredentials();
}
finally
{
_semaphoregate.Release();
}
} }
private void InitTwitterCredentials() private async Task InitTwitterCredentials()
{ {
for (;;) for (;;)
{ {
try try
{ {
Auth.SetApplicationOnlyCredentials(_settings.ConsumerKey, _settings.ConsumerSecret, true);
using (var request = new HttpRequestMessage(new HttpMethod("POST"), "https://api.twitter.com/oauth2/token"))
{
var base64authorization = Convert.ToBase64String(System.Text.Encoding.ASCII.GetBytes(_settings.ConsumerKey + ":" + _settings.ConsumerSecret));
request.Headers.TryAddWithoutValidation("Authorization", $"Basic {base64authorization}");
request.Content = new StringContent("grant_type=client_credentials");
request.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/x-www-form-urlencoded");
var httpResponse = await _httpClient.SendAsync(request);
var c = await httpResponse.Content.ReadAsStringAsync();
httpResponse.EnsureSuccessStatusCode();
var doc = JsonDocument.Parse(c);
_token = doc.RootElement.GetProperty("access_token").GetString();
}
_initialized = true; _initialized = true;
return; return;
} }
catch (Exception e) catch (Exception e)
{ {
_logger.LogError(e, "Twitter Authentication Failed"); _logger.LogError(e, "Twitter Authentication Failed");
Thread.Sleep(250); await Task.Delay(3600*1000);
} }
} }
} }

View file

@ -1,15 +1,15 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net.Http;
using System.Text.Json;
using System.Threading.Tasks;
using BirdsiteLive.Common.Settings; using BirdsiteLive.Common.Settings;
using BirdsiteLive.Statistics.Domain; using BirdsiteLive.Statistics.Domain;
using BirdsiteLive.Twitter.Extractors; using BirdsiteLive.Twitter.Extractors;
using BirdsiteLive.Twitter.Models; using BirdsiteLive.Twitter.Models;
using BirdsiteLive.Twitter.Tools; using BirdsiteLive.Twitter.Tools;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Tweetinvi;
using Tweetinvi.Models;
using Tweetinvi.Parameters;
namespace BirdsiteLive.Twitter namespace BirdsiteLive.Twitter
{ {
@ -26,6 +26,7 @@ namespace BirdsiteLive.Twitter
private readonly ITwitterStatisticsHandler _statisticsHandler; private readonly ITwitterStatisticsHandler _statisticsHandler;
private readonly ITwitterUserService _twitterUserService; private readonly ITwitterUserService _twitterUserService;
private readonly ILogger<TwitterTweetsService> _logger; private readonly ILogger<TwitterTweetsService> _logger;
private HttpClient _httpClient = new HttpClient();
#region Ctor #region Ctor
public TwitterTweetsService(ITwitterAuthenticationInitializer twitterAuthenticationInitializer, ITweetExtractor tweetExtractor, ITwitterStatisticsHandler statisticsHandler, ITwitterUserService twitterUserService, ILogger<TwitterTweetsService> logger) public TwitterTweetsService(ITwitterAuthenticationInitializer twitterAuthenticationInitializer, ITweetExtractor tweetExtractor, ITwitterStatisticsHandler statisticsHandler, ITwitterUserService twitterUserService, ILogger<TwitterTweetsService> logger)
@ -38,15 +39,27 @@ namespace BirdsiteLive.Twitter
} }
#endregion #endregion
public ExtractedTweet GetTweet(long statusId) public ExtractedTweet GetTweet(long statusId)
{
return GetTweetAsync(statusId).Result;
}
public async Task<ExtractedTweet> GetTweetAsync(long statusId)
{ {
try try
{ {
_twitterAuthenticationInitializer.EnsureAuthenticationIsInitialized(); await _twitterAuthenticationInitializer.EnsureAuthenticationIsInitialized();
ExceptionHandler.SwallowWebExceptions = false; JsonDocument tweet;
TweetinviConfig.CurrentThreadSettings.TweetMode = TweetMode.Extended; using (var request = new HttpRequestMessage(new HttpMethod("GET"), "https://api.twitter.com/2/tweets?ids=" + statusId))
{
request.Headers.TryAddWithoutValidation("Authorization", "Bearer " + _twitterAuthenticationInitializer.Token);
var httpResponse = await _httpClient.SendAsync(request);
httpResponse.EnsureSuccessStatusCode();
var c = await httpResponse.Content.ReadAsStringAsync();
tweet = JsonDocument.Parse(c);
}
var tweet = Tweet.GetTweet(statusId);
_statisticsHandler.CalledTweetApi(); _statisticsHandler.CalledTweetApi();
if (tweet == null) return null; //TODO: test this if (tweet == null) return null; //TODO: test this
return _tweetExtractor.Extract(tweet); return _tweetExtractor.Extract(tweet);
@ -60,34 +73,41 @@ namespace BirdsiteLive.Twitter
public ExtractedTweet[] GetTimeline(string username, int nberTweets, long fromTweetId = -1) public ExtractedTweet[] GetTimeline(string username, int nberTweets, long fromTweetId = -1)
{ {
var tweets = new List<ITweet>(); return GetTimelineAsync(username, nberTweets, fromTweetId).Result;
}
public async Task<ExtractedTweet[]> GetTimelineAsync(string username, int nberTweets, long fromTweetId = -1)
{
_twitterAuthenticationInitializer.EnsureAuthenticationIsInitialized(); await _twitterAuthenticationInitializer.EnsureAuthenticationIsInitialized();
ExceptionHandler.SwallowWebExceptions = false;
TweetinviConfig.CurrentThreadSettings.TweetMode = TweetMode.Extended;
var user = _twitterUserService.GetUser(username); var user = _twitterUserService.GetUser(username);
if (user == null || user.Protected) return new ExtractedTweet[0]; if (user == null || user.Protected) return new ExtractedTweet[0];
if (fromTweetId == -1) JsonDocument tweets;
try
{ {
var timeline = Timeline.GetUserTimeline(user.Id, nberTweets); using (var request = new HttpRequestMessage(new HttpMethod("GET"), "https://api.twitter.com/2/users/" + user + "/tweets?expansions=in_reply_to_user_id,attachments.media_keys,entities.mentions.username,referenced_tweets.id.author_id&tweet.fields=id"))
_statisticsHandler.CalledTimelineApi();
if (timeline != null) tweets.AddRange(timeline);
}
else
{ {
var timelineRequestParameters = new UserTimelineParameters request.Headers.TryAddWithoutValidation("Authorization", "Bearer " + _twitterAuthenticationInitializer.Token);
{
SinceId = fromTweetId, var httpResponse = await _httpClient.SendAsync(request);
MaximumNumberOfTweetsToRetrieve = nberTweets httpResponse.EnsureSuccessStatusCode();
}; var c = await httpResponse.Content.ReadAsStringAsync();
var timeline = Timeline.GetUserTimeline(user.Id, timelineRequestParameters); tweets = JsonDocument.Parse(c);
_statisticsHandler.CalledTimelineApi();
if (timeline != null) tweets.AddRange(timeline);
} }
return tweets.Select(_tweetExtractor.Extract).ToArray(); _statisticsHandler.CalledTweetApi();
if (tweets == null) return null; //TODO: test this
}
catch (Exception e)
{
_logger.LogError(e, "Error retrieving timeline ", username);
return null;
}
return Array.Empty<ExtractedTweet>();
//return tweets.RootElement.GetProperty("data").Select(_tweetExtractor.Extract).ToArray();
} }
} }
} }

View file

@ -1,13 +1,13 @@
using System; using System;
using System.Linq; using System.Linq;
using System.Net.Http;
using System.Text.Json;
using System.Threading.Tasks;
using BirdsiteLive.Common.Settings; using BirdsiteLive.Common.Settings;
using BirdsiteLive.Statistics.Domain; using BirdsiteLive.Statistics.Domain;
using BirdsiteLive.Twitter.Models; using BirdsiteLive.Twitter.Models;
using BirdsiteLive.Twitter.Tools; using BirdsiteLive.Twitter.Tools;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Tweetinvi;
using Tweetinvi.Exceptions;
using Tweetinvi.Models;
namespace BirdsiteLive.Twitter namespace BirdsiteLive.Twitter
{ {
@ -22,6 +22,7 @@ namespace BirdsiteLive.Twitter
private readonly ITwitterAuthenticationInitializer _twitterAuthenticationInitializer; private readonly ITwitterAuthenticationInitializer _twitterAuthenticationInitializer;
private readonly ITwitterStatisticsHandler _statisticsHandler; private readonly ITwitterStatisticsHandler _statisticsHandler;
private readonly ILogger<TwitterUserService> _logger; private readonly ILogger<TwitterUserService> _logger;
private HttpClient _httpClient = new HttpClient();
#region Ctor #region Ctor
public TwitterUserService(ITwitterAuthenticationInitializer twitterAuthenticationInitializer, ITwitterStatisticsHandler statisticsHandler, ILogger<TwitterUserService> logger) public TwitterUserService(ITwitterAuthenticationInitializer twitterAuthenticationInitializer, ITwitterStatisticsHandler statisticsHandler, ILogger<TwitterUserService> logger)
@ -33,38 +34,50 @@ namespace BirdsiteLive.Twitter
#endregion #endregion
public TwitterUser GetUser(string username) public TwitterUser GetUser(string username)
{
return GetUserAsync(username).Result;
}
public async Task<TwitterUser> GetUserAsync(string username)
{ {
//Check if API is saturated //Check if API is saturated
if (IsUserApiRateLimited()) throw new RateLimitExceededException(); if (IsUserApiRateLimited()) throw new RateLimitExceededException();
//Proceed to account retrieval //Proceed to account retrieval
_twitterAuthenticationInitializer.EnsureAuthenticationIsInitialized(); await _twitterAuthenticationInitializer.EnsureAuthenticationIsInitialized();
ExceptionHandler.SwallowWebExceptions = false;
RateLimit.RateLimitTrackerMode = RateLimitTrackerMode.TrackOnly;
IUser user; JsonDocument res;
try try
{ {
user = User.GetUserFromScreenName(username); using (var request = new HttpRequestMessage(new HttpMethod("GET"), "https://api.twitter.com/2/users/by/username/"+ username + "?user.fields=name,username,protected,profile_image_url,url,description"))
}
catch (TwitterException e)
{ {
if (e.TwitterExceptionInfos.Any(x => x.Message.ToLowerInvariant().Contains("User has been suspended".ToLowerInvariant()))) request.Headers.TryAddWithoutValidation("Authorization", "Bearer " + _twitterAuthenticationInitializer.Token);
{
throw new UserHasBeenSuspendedException(); var httpResponse = await _httpClient.SendAsync(request);
httpResponse.EnsureSuccessStatusCode();
var c = await httpResponse.Content.ReadAsStringAsync();
res = JsonDocument.Parse(c);
} }
else if (e.TwitterExceptionInfos.Any(x => x.Message.ToLowerInvariant().Contains("User not found".ToLowerInvariant())))
{
throw new UserNotFoundException();
} }
else if (e.TwitterExceptionInfos.Any(x => x.Message.ToLowerInvariant().Contains("Rate limit exceeded".ToLowerInvariant()))) catch (HttpRequestException e)
{
throw new RateLimitExceededException();
}
else
{ {
throw; throw;
} //if (e.TwitterExceptionInfos.Any(x => x.Message.ToLowerInvariant().Contains("User has been suspended".ToLowerInvariant())))
//{
// throw new UserHasBeenSuspendedException();
//}
//else if (e.TwitterExceptionInfos.Any(x => x.Message.ToLowerInvariant().Contains("User not found".ToLowerInvariant())))
//{
// throw new UserNotFoundException();
//}
//else if (e.TwitterExceptionInfos.Any(x => x.Message.ToLowerInvariant().Contains("Rate limit exceeded".ToLowerInvariant())))
//{
// throw new RateLimitExceededException();
//}
//else
//{
// throw;
//}
} }
catch (Exception e) catch (Exception e)
{ {
@ -77,49 +90,50 @@ namespace BirdsiteLive.Twitter
} }
// Expand URLs // Expand URLs
var description = user.Description; //var description = user.Description;
foreach (var descriptionUrl in user.Entities?.Description?.Urls?.OrderByDescending(x => x.URL.Length)) //foreach (var descriptionUrl in user.Entities?.Description?.Urls?.OrderByDescending(x => x.URL.Length))
description = description.Replace(descriptionUrl.URL, descriptionUrl.ExpandedURL); // description = description.Replace(descriptionUrl.URL, descriptionUrl.ExpandedURL);
return new TwitterUser return new TwitterUser
{ {
Id = user.Id, Id = long.Parse(res.RootElement.GetProperty("data").GetProperty("id").GetString()),
Acct = username, Acct = res.RootElement.GetProperty("data").GetProperty("username").GetString(),
Name = user.Name, Name = res.RootElement.GetProperty("data").GetProperty("name").GetString(),
Description = description, Description = res.RootElement.GetProperty("data").GetProperty("description").GetString(),
Url = $"https://twitter.com/{username}", Url = res.RootElement.GetProperty("data").GetProperty("url").GetString(),
ProfileImageUrl = user.ProfileImageUrlFullSize.Replace("http://", "https://"), ProfileImageUrl = res.RootElement.GetProperty("data").GetProperty("profile_image_url").GetString(),
ProfileBackgroundImageUrl = user.ProfileBackgroundImageUrlHttps, ProfileBackgroundImageUrl = res.RootElement.GetProperty("data").GetProperty("profile_image_url").GetString(), //for now
ProfileBannerURL = user.ProfileBannerURL, ProfileBannerURL = res.RootElement.GetProperty("data").GetProperty("profile_image_url").GetString(), //for now
Protected = user.Protected Protected = res.RootElement.GetProperty("data").GetProperty("protected").GetBoolean(),
}; };
} }
public bool IsUserApiRateLimited() public bool IsUserApiRateLimited()
{ {
// Retrieve limit from tooling // Retrieve limit from tooling
_twitterAuthenticationInitializer.EnsureAuthenticationIsInitialized(); //_twitterAuthenticationInitializer.EnsureAuthenticationIsInitialized();
ExceptionHandler.SwallowWebExceptions = false; //ExceptionHandler.SwallowWebExceptions = false;
RateLimit.RateLimitTrackerMode = RateLimitTrackerMode.TrackOnly; //RateLimit.RateLimitTrackerMode = RateLimitTrackerMode.TrackOnly;
try //try
{ //{
var queryRateLimits = RateLimit.GetQueryRateLimit("https://api.twitter.com/1.1/users/show.json?screen_name=mastodon"); // var queryRateLimits = RateLimit.GetQueryRateLimit("https://api.twitter.com/1.1/users/show.json?screen_name=mastodon");
if (queryRateLimits != null) // if (queryRateLimits != null)
{ // {
return queryRateLimits.Remaining <= 0; // return queryRateLimits.Remaining <= 0;
} // }
} //}
catch (Exception e) //catch (Exception e)
{ //{
_logger.LogError(e, "Error retrieving rate limits"); // _logger.LogError(e, "Error retrieving rate limits");
} //}
// Fallback //// Fallback
var currentCalls = _statisticsHandler.GetCurrentUserCalls(); //var currentCalls = _statisticsHandler.GetCurrentUserCalls();
var maxCalls = _statisticsHandler.GetStatistics().UserCallsMax; //var maxCalls = _statisticsHandler.GetStatistics().UserCallsMax;
return currentCalls >= maxCalls; //return currentCalls >= maxCalls;
return false;
} }
} }
} }

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Web"> <Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework> <TargetFramework>net6</TargetFramework>
<UserSecretsId>d21486de-a812-47eb-a419-05682bb68856</UserSecretsId> <UserSecretsId>d21486de-a812-47eb-a419-05682bb68856</UserSecretsId>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS> <DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
<Version>0.20.0</Version> <Version>0.20.0</Version>

View file

@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Net.Mime; using System.Net.Mime;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework> <TargetFramework>net6</TargetFramework>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
</PropertyGroup> </PropertyGroup>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework> <TargetFramework>net6</TargetFramework>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
</PropertyGroup> </PropertyGroup>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework> <TargetFramework>net6</TargetFramework>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
</PropertyGroup> </PropertyGroup>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework> <TargetFramework>net6</TargetFramework>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
</PropertyGroup> </PropertyGroup>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework> <TargetFramework>net6</TargetFramework>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
</PropertyGroup> </PropertyGroup>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework> <TargetFramework>net6</TargetFramework>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
</PropertyGroup> </PropertyGroup>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework> <TargetFramework>net6</TargetFramework>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
</PropertyGroup> </PropertyGroup>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework> <TargetFramework>net6</TargetFramework>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
</PropertyGroup> </PropertyGroup>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework> <TargetFramework>net6</TargetFramework>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
</PropertyGroup> </PropertyGroup>