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>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<TargetFramework>net6</TargetFramework>
</PropertyGroup>
<ItemGroup>

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -2,129 +2,109 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.Json;
using BirdsiteLive.Twitter.Models;
using Tweetinvi.Models;
using Tweetinvi.Models.Entities;
namespace BirdsiteLive.Twitter.Extractors
{
public interface ITweetExtractor
{
ExtractedTweet Extract(ITweet tweet);
ExtractedTweet Extract(JsonDocument tweet);
}
public class TweetExtractor : ITweetExtractor
{
public ExtractedTweet Extract(ITweet tweet)
public ExtractedTweet Extract(JsonDocument tweet)
{
var extractedTweet = new ExtractedTweet
{
Id = tweet.Id,
InReplyToStatusId = tweet.InReplyToStatusId,
InReplyToAccount = tweet.InReplyToScreenName,
Id = tweet.RootElement.GetProperty("data").GetProperty("id").GetInt64(),
InReplyToStatusId = tweet.RootElement.GetProperty("data").GetProperty("in_reply_to_status_id").GetInt64(),
InReplyToAccount = tweet.RootElement.GetProperty("data").GetProperty("in_reply_to_status_id").GetString(),
MessageContent = ExtractMessage(tweet),
Media = ExtractMedia(tweet),
CreatedAt = tweet.CreatedAt.ToUniversalTime(),
IsReply = tweet.InReplyToUserId != null,
IsThread = tweet.InReplyToUserId != null && tweet.InReplyToUserId == tweet.CreatedBy.Id,
IsRetweet = tweet.IsRetweet || tweet.QuotedStatusId != null,
CreatedAt = tweet.RootElement.GetProperty("data").GetProperty("in_reply_to_status_id").GetDateTime(),
IsReply = false,
IsThread = false,
IsRetweet = false,
RetweetUrl = ExtractRetweetUrl(tweet)
};
return extractedTweet;
}
private string ExtractRetweetUrl(ITweet tweet)
private string ExtractRetweetUrl(JsonDocument tweet)
{
if (tweet.IsRetweet)
{
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}";
}
}
var retweetId = "123";
return $"https://t.co/{retweetId}";
return null;
}
public string ExtractMessage(ITweet tweet)
private string ExtractMessage(JsonDocument tweet)
{
var message = tweet.FullText;
var tweetUrls = tweet.Media.Select(x => x.URL).Distinct();
return "hello world";
//var message = tweet.FullText;
//var tweetUrls = tweet.Media.Select(x => x.URL).Distinct();
if (tweet.IsRetweet && message.StartsWith("RT") && tweet.RetweetedTweet != null)
{
message = tweet.RetweetedTweet.FullText;
tweetUrls = tweet.RetweetedTweet.Media.Select(x => x.URL).Distinct();
}
//if (tweet.IsRetweet && message.StartsWith("RT") && tweet.RetweetedTweet != null)
//{
// message = tweet.RetweetedTweet.FullText;
// 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();
}
//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}}]");
}
//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);
//// Expand URLs
//foreach (var url in tweet.Urls.OrderByDescending(x => x.URL.Length))
// message = message.Replace(url.URL, url.ExpandedURL);
return message;
//return message;
}
public ExtractedMedia[] ExtractMedia(ITweet tweet)
private ExtractedMedia[] ExtractMedia(JsonDocument tweet)
{
var media = tweet.Media;
if (tweet.IsRetweet && tweet.RetweetedTweet != null)
media = tweet.RetweetedTweet.Media;
//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 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);
}
// var att = new ExtractedMedia
// {
// MediaType = mediaType,
// Url = mediaUrl
// };
// result.Add(att);
//}
return result.ToArray();
//return result.ToArray();
return Array.Empty<ExtractedMedia>();
}
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)
private string GetMediaType(string mediaType, string mediaUrl)
{
switch (mediaType)
{

View file

@ -3,13 +3,16 @@ using System.Threading;
using System.Threading.Tasks;
using BirdsiteLive.Common.Settings;
using Microsoft.Extensions.Logging;
using Tweetinvi;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text.Json;
namespace BirdsiteLive.Twitter.Tools
{
public interface ITwitterAuthenticationInitializer
{
void EnsureAuthenticationIsInitialized();
String Token { get; }
Task EnsureAuthenticationIsInitialized();
}
public class TwitterAuthenticationInitializer : ITwitterAuthenticationInitializer
@ -17,7 +20,11 @@ namespace BirdsiteLive.Twitter.Tools
private readonly TwitterSettings _settings;
private readonly ILogger<TwitterAuthenticationInitializer> _logger;
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
public TwitterAuthenticationInitializer(TwitterSettings settings, ILogger<TwitterAuthenticationInitializer> logger)
@ -27,36 +34,42 @@ namespace BirdsiteLive.Twitter.Tools
}
#endregion
public void EnsureAuthenticationIsInitialized()
public async Task EnsureAuthenticationIsInitialized()
{
if (_initialized) return;
_semaphoregate.Wait();
try
{
if (_initialized) return;
InitTwitterCredentials();
}
finally
{
_semaphoregate.Release();
}
await InitTwitterCredentials();
}
private void InitTwitterCredentials()
private async Task InitTwitterCredentials()
{
for (;;)
{
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;
return;
}
catch (Exception e)
{
_logger.LogError(e, "Twitter Authentication Failed");
Thread.Sleep(250);
await Task.Delay(3600*1000);
}
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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