Use Octokit for Github lookups.

This commit is contained in:
clienthax 2021-08-30 14:39:05 +01:00
parent bb1bc8e858
commit bacc0ca3a9
11 changed files with 81 additions and 225 deletions

View File

@ -11,7 +11,9 @@ namespace CompatApiClient
public static class ApiConfig public static class ApiConfig
{ {
public static readonly ProductInfoHeaderValue ProductInfoHeader = new("RPCS3CompatibilityBot", "2.0"); public static readonly string ProductName = "RPCS3CompatibilityBot";
public static readonly string ProductVersion = "2.0";
public static readonly ProductInfoHeaderValue ProductInfoHeader = new(ProductName, ProductVersion);
public static int Version { get; } = 1; public static int Version { get; } = 1;
public static Uri BaseUrl { get; } = new("https://rpcs3.net/compatibility"); public static Uri BaseUrl { get; } = new("https://rpcs3.net/compatibility");
public static string DateInputFormat { get; } = "yyyy-M-d"; public static string DateInputFormat { get; } = "yyyy-M-d";

View File

@ -1,25 +1,15 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Json;
using System.Net.Http.Headers;
using System.Text.Json;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using CompatApiClient; using CompatApiClient;
using CompatApiClient.Compression;
using CompatApiClient.Formatters;
using CompatApiClient.Utils;
using GithubClient.POCOs;
using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Caching.Memory;
namespace GithubClient namespace GithubClient
{ {
public class Client public class Client
{ {
private readonly HttpClient client; private readonly Octokit.GitHubClient client;
private readonly JsonSerializerOptions jsonOptions;
private static readonly TimeSpan PrStatusCacheTime = TimeSpan.FromMinutes(3); private static readonly TimeSpan PrStatusCacheTime = TimeSpan.FromMinutes(3);
private static readonly TimeSpan IssueStatusCacheTime = TimeSpan.FromMinutes(30); private static readonly TimeSpan IssueStatusCacheTime = TimeSpan.FromMinutes(30);
@ -30,40 +20,29 @@ namespace GithubClient
public static int RateLimitRemaining { get; private set; } public static int RateLimitRemaining { get; private set; }
public static DateTime RateLimitResetTime { get; private set; } public static DateTime RateLimitResetTime { get; private set; }
public Client() public Client(string? githubToken)
{ {
client = HttpClientFactory.Create(new CompressionMessageHandler()); client = new Octokit.GitHubClient(new Octokit.ProductHeaderValue(ApiConfig.ProductName, ApiConfig.ProductVersion));
jsonOptions = new() if (!string.IsNullOrEmpty(githubToken))
{ {
PropertyNamingPolicy = SpecialJsonNamingPolicy.SnakeCase, client.Credentials = new Octokit.Credentials(githubToken);
IgnoreNullValues = true, }
IncludeFields = true,
};
} }
public async Task<PrInfo?> GetPrInfoAsync(int pr, CancellationToken cancellationToken) public async Task<Octokit.PullRequest?> GetPrInfoAsync(int pr, CancellationToken cancellationToken)
{ {
if (StatusesCache.TryGetValue(pr, out PrInfo? result)) if (StatusesCache.TryGetValue(pr, out Octokit.PullRequest? result))
{ {
ApiConfig.Log.Debug($"Returned {nameof(PrInfo)} for {pr} from cache"); ApiConfig.Log.Debug($"Returned {nameof(Octokit.PullRequest)} for {pr} from cache");
return result; return result;
} }
try try
{ {
using var message = new HttpRequestMessage(HttpMethod.Get, "https://api.github.com/repos/RPCS3/rpcs3/pulls/" + pr); var request = client.PullRequest.Get("RPCS3", "rpcs3", pr);
message.Headers.UserAgent.Add(ApiConfig.ProductInfoHeader); request.Wait(cancellationToken);
using var response = await client.SendAsync(message, HttpCompletionOption.ResponseContentRead, cancellationToken).ConfigureAwait(false); result = (await request.ConfigureAwait(false));
try UpdateRateLimitStats();
{
await response.Content.LoadIntoBufferAsync().ConfigureAwait(false);
UpdateRateLimitStats(response.Headers);
result = await response.Content.ReadFromJsonAsync<PrInfo>(jsonOptions, cancellationToken).ConfigureAwait(false);
}
catch (Exception e)
{
ConsoleLogger.PrintError(e, response);
}
} }
catch (Exception e) catch (Exception e)
{ {
@ -71,38 +50,29 @@ namespace GithubClient
} }
if (result == null) if (result == null)
{ {
ApiConfig.Log.Debug($"Failed to get {nameof(PrInfo)}, returning empty result"); ApiConfig.Log.Debug($"Failed to get {nameof(Octokit.PullRequest)}, returning empty result");
return new() { Number = pr }; return new(pr);
} }
StatusesCache.Set(pr, result, PrStatusCacheTime); StatusesCache.Set(pr, result, PrStatusCacheTime);
ApiConfig.Log.Debug($"Cached {nameof(PrInfo)} for {pr} for {PrStatusCacheTime}"); ApiConfig.Log.Debug($"Cached {nameof(Octokit.PullRequest)} for {pr} for {PrStatusCacheTime}");
return result; return result;
} }
public async Task<IssueInfo?> GetIssueInfoAsync(int issue, CancellationToken cancellationToken) public async Task<Octokit.Issue?> GetIssueInfoAsync(int issue, CancellationToken cancellationToken)
{ {
if (IssuesCache.TryGetValue(issue, out IssueInfo? result)) if (IssuesCache.TryGetValue(issue, out Octokit.Issue? result))
{ {
ApiConfig.Log.Debug($"Returned {nameof(IssueInfo)} for {issue} from cache"); ApiConfig.Log.Debug($"Returned {nameof(Octokit.Issue)} for {issue} from cache");
return result; return result;
} }
try try
{ {
using var message = new HttpRequestMessage(HttpMethod.Get, "https://api.github.com/repos/RPCS3/rpcs3/issues/" + issue); var request = client.Issue.Get("RPCS3", "rpcs3", issue);
message.Headers.UserAgent.Add(ApiConfig.ProductInfoHeader); request.Wait(cancellationToken);
using var response = await client.SendAsync(message, HttpCompletionOption.ResponseContentRead, cancellationToken).ConfigureAwait(false); result = (await request.ConfigureAwait(false));
try UpdateRateLimitStats();
{
await response.Content.LoadIntoBufferAsync().ConfigureAwait(false);
UpdateRateLimitStats(response.Headers);
result = await response.Content.ReadFromJsonAsync<IssueInfo>(jsonOptions, cancellationToken).ConfigureAwait(false);
}
catch (Exception e)
{
ConsoleLogger.PrintError(e, response);
}
} }
catch (Exception e) catch (Exception e)
{ {
@ -110,22 +80,31 @@ namespace GithubClient
} }
if (result == null) if (result == null)
{ {
ApiConfig.Log.Debug($"Failed to get {nameof(IssueInfo)}, returning empty result"); ApiConfig.Log.Debug($"Failed to get {nameof(Octokit.Issue)}, returning empty result");
return new() { Number = issue }; return new() { };
} }
IssuesCache.Set(issue, result, IssueStatusCacheTime); IssuesCache.Set(issue, result, IssueStatusCacheTime);
ApiConfig.Log.Debug($"Cached {nameof(IssueInfo)} for {issue} for {IssueStatusCacheTime}"); ApiConfig.Log.Debug($"Cached {nameof(Octokit.Issue)} for {issue} for {IssueStatusCacheTime}");
return result; return result;
} }
public Task<List<PrInfo>?> GetOpenPrsAsync(CancellationToken cancellationToken) => GetPrsWithStatusAsync("open", cancellationToken); public Task<IReadOnlyList<Octokit.PullRequest>?> GetOpenPrsAsync(CancellationToken cancellationToken) => GetPrsWithStatusAsync(new Octokit.PullRequestRequest
public Task<List<PrInfo>?> GetClosedPrsAsync(CancellationToken cancellationToken) => GetPrsWithStatusAsync("closed&sort=updated&direction=desc", cancellationToken);
private async Task<List<PrInfo>?> GetPrsWithStatusAsync(string status, CancellationToken cancellationToken)
{ {
var requestUri = "https://api.github.com/repos/RPCS3/rpcs3/pulls?state=" + status; State = Octokit.ItemStateFilter.Open
if (StatusesCache.TryGetValue(requestUri, out List<PrInfo>? result)) }, cancellationToken);
public Task<IReadOnlyList<Octokit.PullRequest>?> GetClosedPrsAsync(CancellationToken cancellationToken) => GetPrsWithStatusAsync(new Octokit.PullRequestRequest
{
State = Octokit.ItemStateFilter.Closed,
SortProperty = Octokit.PullRequestSort.Updated,
SortDirection = Octokit.SortDirection.Descending
}, cancellationToken);
private async Task<IReadOnlyList<Octokit.PullRequest>?> GetPrsWithStatusAsync(Octokit.PullRequestRequest filter, CancellationToken cancellationToken)
{
var statusURI = "https://api.github.com/repos/RPCS3/rpcs3/pulls?state=" + filter.ToString();
if (StatusesCache.TryGetValue(statusURI, out IReadOnlyList<Octokit.PullRequest>? result))
{ {
ApiConfig.Log.Debug("Returned list of opened PRs from cache"); ApiConfig.Log.Debug("Returned list of opened PRs from cache");
return result; return result;
@ -133,19 +112,11 @@ namespace GithubClient
try try
{ {
using var message = new HttpRequestMessage(HttpMethod.Get, requestUri); var request = client.PullRequest.GetAllForRepository("RPCS3", "rpcs3", filter);
message.Headers.UserAgent.Add(ApiConfig.ProductInfoHeader); request.Wait(cancellationToken);
using var response = await client.SendAsync(message, HttpCompletionOption.ResponseContentRead, cancellationToken).ConfigureAwait(false);
try result = (await request.ConfigureAwait(false));
{ UpdateRateLimitStats();
await response.Content.LoadIntoBufferAsync().ConfigureAwait(false);
UpdateRateLimitStats(response.Headers);
result = await response.Content.ReadFromJsonAsync<List<PrInfo>>(jsonOptions, cancellationToken).ConfigureAwait(false);
}
catch (Exception e)
{
ConsoleLogger.PrintError(e, response);
}
} }
catch (Exception e) catch (Exception e)
{ {
@ -153,7 +124,7 @@ namespace GithubClient
} }
if (result != null) if (result != null)
{ {
StatusesCache.Set(requestUri, result, PrStatusCacheTime); StatusesCache.Set(statusURI, result, PrStatusCacheTime);
foreach (var prInfo in result) foreach (var prInfo in result)
StatusesCache.Set(prInfo.Number, prInfo, PrStatusCacheTime); StatusesCache.Set(prInfo.Number, prInfo, PrStatusCacheTime);
ApiConfig.Log.Debug($"Cached list of open PRs for {PrStatusCacheTime}"); ApiConfig.Log.Debug($"Cached list of open PRs for {PrStatusCacheTime}");
@ -161,62 +132,22 @@ namespace GithubClient
return result; return result;
} }
public async Task<List<StatusInfo>?> GetStatusesAsync(string statusesUrl, CancellationToken cancellationToken) private void UpdateRateLimitStats()
{ {
if (StatusesCache.TryGetValue(statusesUrl, out List<StatusInfo>? result)) var apiInfo = client.GetLastApiInfo();
if (apiInfo == null)
{ {
ApiConfig.Log.Debug($"Returned cached item for {statusesUrl}"); return;
return result;
} }
try RateLimit = apiInfo.RateLimit.Limit;
{ RateLimitRemaining = apiInfo.RateLimit.Remaining;
using var message = new HttpRequestMessage(HttpMethod.Get, statusesUrl); RateLimitResetTime = DateTimeOffset.FromUnixTimeSeconds(apiInfo.RateLimit.ResetAsUtcEpochSeconds).UtcDateTime;
message.Headers.UserAgent.Add(ApiConfig.ProductInfoHeader);
using var response = await client.SendAsync(message, HttpCompletionOption.ResponseContentRead, cancellationToken).ConfigureAwait(false);
try
{
await response.Content.LoadIntoBufferAsync().ConfigureAwait(false);
UpdateRateLimitStats(response.Headers);
result = await response.Content.ReadFromJsonAsync<List<StatusInfo>>(jsonOptions, cancellationToken).ConfigureAwait(false);
}
catch (Exception e)
{
ConsoleLogger.PrintError(e, response);
}
}
catch (Exception e)
{
ApiConfig.Log.Error(e);
}
if (result != null)
{
StatusesCache.Set(statusesUrl, result, PrStatusCacheTime);
ApiConfig.Log.Debug($"Cached item for {statusesUrl} for {PrStatusCacheTime}");
}
return result;
}
private static void UpdateRateLimitStats(HttpResponseHeaders headers)
{
if (headers.TryGetValues("X-RateLimit-Limit", out var rateLimitValues)
&& rateLimitValues.FirstOrDefault() is string limitValue
&& int.TryParse(limitValue, out var limit)
&& limit > 0)
RateLimit = limit;
if (headers.TryGetValues("X-RateLimit-Remaining", out var rateLimitRemainingValues)
&& rateLimitRemainingValues.FirstOrDefault() is string remainingValue
&& int.TryParse(remainingValue, out var remaining)
&& remaining > 0)
RateLimitRemaining = remaining;
if (headers.TryGetValues("X-RateLimit-Reset", out var rateLimitResetValues)
&& rateLimitResetValues.FirstOrDefault() is string resetValue
&& long.TryParse(resetValue, out var resetSeconds)
&& resetSeconds > 0)
RateLimitResetTime = DateTimeOffset.FromUnixTimeSeconds(resetSeconds).UtcDateTime;
if (RateLimitRemaining < 10) if (RateLimitRemaining < 10)
ApiConfig.Log.Warn($"Github rate limit is low: {RateLimitRemaining} out of {RateLimit}, will be reset on {RateLimitResetTime:u}"); ApiConfig.Log.Warn($"Github rate limit is low: {RateLimitRemaining} out of {RateLimit}, will be reset on {RateLimitResetTime:u}");
} }
} }
} }

View File

@ -8,6 +8,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="5.0.0" /> <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="5.0.0" />
<PackageReference Include="Octokit" Version="0.50.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -1,64 +0,0 @@
using System;
using System.Diagnostics;
namespace GithubClient.POCOs
{
[DebuggerDisplay("{Body}", Name = "#{Number}")]
public sealed class PrInfo
{
public string? HtmlUrl;
public int Number;
public string? State;
public string? Title;
public GithubUser? User;
public string? Body;
public DateTime CreatedAt;
public DateTime? UpdatedAt;
public DateTime? ClosedAt;
public DateTime? MergedAt;
public string? MergeCommitSha;
public string? StatusesUrl;
public RefInfo? Head;
public RefInfo? Base;
public int Additions;
public int Deletions;
public int ChangedFiles;
public string? Message;
}
public sealed class IssueInfo
{
public string? HtmlUrl;
public int Number;
public string? State;
public string? Title;
public GithubUser? User;
public DateTime CreatedAt;
public DateTime? UpdatedAt;
public DateTime? ClosedAt;
public DateTime? MergedAt;
public string? Body;
public PullRequestReference? PullRequest;
}
public sealed class GithubUser
{
public string? Login;
}
public sealed class PullRequestReference
{
public string? Url;
public string? HtmlUrl;
public string? DiffUrl;
public string? PatchUrl;
}
public sealed class RefInfo
{
public string? Label;
public string? Ref;
public GithubUser? User;
public string? Sha;
}
}

View File

@ -1,14 +0,0 @@
using System;
namespace GithubClient.POCOs
{
public sealed class StatusInfo
{
public string? State; // success
public string? Description;
public string? TargetUrl;
public string? Context; // continuous-integration/appveyor/pr
public DateTime? CreatedAt;
public DateTime? UpdatedAt;
}
}

View File

@ -67,6 +67,7 @@ namespace CompatBot.Commands
.Append(string.IsNullOrEmpty(Config.AzureDevOpsToken) ? "❌" : "✅").AppendLine(" Azure DevOps") .Append(string.IsNullOrEmpty(Config.AzureDevOpsToken) ? "❌" : "✅").AppendLine(" Azure DevOps")
.Append(string.IsNullOrEmpty(Config.AzureComputerVisionKey) ? "❌" : "✅").AppendLine(" Computer Vision") .Append(string.IsNullOrEmpty(Config.AzureComputerVisionKey) ? "❌" : "✅").AppendLine(" Computer Vision")
.Append(string.IsNullOrEmpty(Config.AzureAppInsightsKey) ? "❌" : "✅").AppendLine(" AppInsights") .Append(string.IsNullOrEmpty(Config.AzureAppInsightsKey) ? "❌" : "✅").AppendLine(" AppInsights")
.Append(string.IsNullOrEmpty(Config.GithubToken) ? "❌" : "✅").AppendLine(" Github")
.ToString() .ToString()
.Trim(); .Trim();

View File

@ -35,7 +35,7 @@ namespace CompatBot.Commands
internal sealed class CompatList : BaseCommandModuleCustom internal sealed class CompatList : BaseCommandModuleCustom
{ {
private static readonly Client Client = new(); private static readonly Client Client = new();
private static readonly GithubClient.Client GithubClient = new(); private static readonly GithubClient.Client GithubClient = new(Config.GithubToken);
private static readonly SemaphoreSlim UpdateCheck = new(1, 1); private static readonly SemaphoreSlim UpdateCheck = new(1, 1);
private static string? lastUpdateInfo, lastFullBuildNumber; private static string? lastUpdateInfo, lastFullBuildNumber;
private const string Rpcs3UpdateStateKey = "Rpcs3UpdateState"; private const string Rpcs3UpdateStateKey = "Rpcs3UpdateState";
@ -338,7 +338,7 @@ namespace CompatBot.Commands
var failedBuilds = await Config.GetAzureDevOpsClient().GetMasterBuildsAsync( var failedBuilds = await Config.GetAzureDevOpsClient().GetMasterBuildsAsync(
oldestPrCommit.MergeCommitSha, oldestPrCommit.MergeCommitSha,
newestPrCommit.MergeCommitSha, newestPrCommit.MergeCommitSha,
oldestPrCommit.MergedAt, oldestPrCommit.MergedAt?.DateTime,
cancellationToken cancellationToken
).ConfigureAwait(false); ).ConfigureAwait(false);
foreach (var mergedPr in mergedPrs) foreach (var mergedPr in mergedPrs)

View File

@ -21,7 +21,7 @@ namespace CompatBot.Commands
[Description("Commands to list opened pull requests information")] [Description("Commands to list opened pull requests information")]
internal sealed class Pr: BaseCommandModuleCustom internal sealed class Pr: BaseCommandModuleCustom
{ {
private static readonly GithubClient.Client GithubClient = new(); private static readonly GithubClient.Client GithubClient = new(Config.GithubToken);
private static readonly CompatApiClient.Client CompatApiClient = new(); private static readonly CompatApiClient.Client CompatApiClient = new();
[GroupCommand] [GroupCommand]
@ -114,7 +114,7 @@ namespace CompatBot.Commands
var prInfo = await GithubClient.GetPrInfoAsync(pr, Config.Cts.Token).ConfigureAwait(false); var prInfo = await GithubClient.GetPrInfoAsync(pr, Config.Cts.Token).ConfigureAwait(false);
if (prInfo is null or {Number: 0}) if (prInfo is null or {Number: 0})
{ {
await message.ReactWithAsync(Config.Reactions.Failure, prInfo?.Message ?? "PR not found").ConfigureAwait(false); await message.ReactWithAsync(Config.Reactions.Failure, prInfo?.Title ?? "PR not found").ConfigureAwait(false);
return; return;
} }
@ -134,7 +134,7 @@ namespace CompatBot.Commands
{ {
windowsDownloadText = "⏳ Pending..."; windowsDownloadText = "⏳ Pending...";
linuxDownloadText = "⏳ Pending..."; linuxDownloadText = "⏳ Pending...";
var latestBuild = await CirrusCi.GetPrBuildInfoAsync(commit, prInfo.MergedAt, pr, Config.Cts.Token).ConfigureAwait(false); var latestBuild = await CirrusCi.GetPrBuildInfoAsync(commit, prInfo.MergedAt?.DateTime, pr, Config.Cts.Token).ConfigureAwait(false);
if (latestBuild == null) if (latestBuild == null)
{ {
if (state == "Open") if (state == "Open")

View File

@ -82,6 +82,7 @@ namespace CompatBot
public static string AzureComputerVisionEndpoint => config.GetValue(nameof(AzureComputerVisionEndpoint), "https://westeurope.api.cognitive.microsoft.com/"); public static string AzureComputerVisionEndpoint => config.GetValue(nameof(AzureComputerVisionEndpoint), "https://westeurope.api.cognitive.microsoft.com/");
public static Guid AzureDevOpsProjectId => config.GetValue(nameof(AzureDevOpsProjectId), new Guid("3598951b-4d39-4fad-ad3b-ff2386a649de")); public static Guid AzureDevOpsProjectId => config.GetValue(nameof(AzureDevOpsProjectId), new Guid("3598951b-4d39-4fad-ad3b-ff2386a649de"));
public static string AzureAppInsightsKey => config.GetValue(nameof(AzureAppInsightsKey), ""); public static string AzureAppInsightsKey => config.GetValue(nameof(AzureAppInsightsKey), "");
public static string GithubToken => config.GetValue(nameof(GithubToken), "");
public static string PreferredFontFamily => config.GetValue(nameof(PreferredFontFamily), ""); public static string PreferredFontFamily => config.GetValue(nameof(PreferredFontFamily), "");
public static string LogPath => config.GetValue(nameof(LogPath), "./logs/"); // paths are relative to the working directory public static string LogPath => config.GetValue(nameof(LogPath), "./logs/"); // paths are relative to the working directory
public static string IrdCachePath => config.GetValue(nameof(IrdCachePath), "./ird/"); public static string IrdCachePath => config.GetValue(nameof(IrdCachePath), "./ird/");

View File

@ -1,11 +1,10 @@
using DSharpPlus.Entities; using DSharpPlus.Entities;
using GithubClient.POCOs;
namespace CompatBot.Utils.ResultFormatters namespace CompatBot.Utils.ResultFormatters
{ {
internal static class PrInfoFormatter internal static class PrInfoFormatter
{ {
public static DiscordEmbedBuilder AsEmbed(this PrInfo prInfo) public static DiscordEmbedBuilder AsEmbed(this Octokit.PullRequest prInfo)
{ {
var state = prInfo.GetState(); var state = prInfo.GetState();
var stateLabel = state.state == null ? null : $"[{state.state}] "; var stateLabel = state.state == null ? null : $"[{state.state}] ";
@ -13,7 +12,7 @@ namespace CompatBot.Utils.ResultFormatters
return new DiscordEmbedBuilder {Title = title, Url = prInfo.HtmlUrl, Description = prInfo.Title, Color = state.color}; return new DiscordEmbedBuilder {Title = title, Url = prInfo.HtmlUrl, Description = prInfo.Title, Color = state.color};
} }
public static DiscordEmbedBuilder AsEmbed(this IssueInfo issueInfo) public static DiscordEmbedBuilder AsEmbed(this Octokit.Issue issueInfo)
{ {
var state = issueInfo.GetState(); var state = issueInfo.GetState();
var stateLabel = state.state == null ? null : $"[{state.state}] "; var stateLabel = state.state == null ? null : $"[{state.state}] ";
@ -21,12 +20,12 @@ namespace CompatBot.Utils.ResultFormatters
return new DiscordEmbedBuilder {Title = title, Url = issueInfo.HtmlUrl, Description = issueInfo.Title, Color = state.color}; return new DiscordEmbedBuilder {Title = title, Url = issueInfo.HtmlUrl, Description = issueInfo.Title, Color = state.color};
} }
public static (string? state, DiscordColor color) GetState(this PrInfo prInfo) public static (string? state, DiscordColor color) GetState(this Octokit.PullRequest prInfo)
{ {
if (prInfo.State == "open") if (prInfo.State == Octokit.ItemState.Open)
return ("Open", Config.Colors.PrOpen); return ("Open", Config.Colors.PrOpen);
if (prInfo.State == "closed") if (prInfo.State == Octokit.ItemState.Closed)
{ {
if (prInfo.MergedAt.HasValue) if (prInfo.MergedAt.HasValue)
return ("Merged", Config.Colors.PrMerged); return ("Merged", Config.Colors.PrMerged);
@ -37,12 +36,12 @@ namespace CompatBot.Utils.ResultFormatters
return (null, Config.Colors.DownloadLinks); return (null, Config.Colors.DownloadLinks);
} }
public static (string? state, DiscordColor color) GetState(this IssueInfo issueInfo) public static (string? state, DiscordColor color) GetState(this Octokit.Issue issueInfo)
{ {
if (issueInfo.State == "open") if (issueInfo.State == Octokit.ItemState.Open)
return ("Open", Config.Colors.PrOpen); return ("Open", Config.Colors.PrOpen);
if (issueInfo.State == "closed") if (issueInfo.State == Octokit.ItemState.Closed)
return ("Closed", Config.Colors.PrClosed); return ("Closed", Config.Colors.PrClosed);
return (null, Config.Colors.DownloadLinks); return (null, Config.Colors.DownloadLinks);

View File

@ -10,15 +10,14 @@ using CompatBot.EventHandlers;
using CompatBot.Utils.Extensions; using CompatBot.Utils.Extensions;
using DSharpPlus; using DSharpPlus;
using DSharpPlus.Entities; using DSharpPlus.Entities;
using GithubClient.POCOs;
namespace CompatBot.Utils.ResultFormatters namespace CompatBot.Utils.ResultFormatters
{ {
internal static class UpdateInfoFormatter internal static class UpdateInfoFormatter
{ {
private static readonly GithubClient.Client GithubClient = new(); private static readonly GithubClient.Client GithubClient = new(Config.GithubToken);
public static async Task<DiscordEmbedBuilder> AsEmbedAsync(this UpdateInfo? info, DiscordClient client, bool includePrBody = false, DiscordEmbedBuilder? builder = null, PrInfo? currentPrInfo = null) public static async Task<DiscordEmbedBuilder> AsEmbedAsync(this UpdateInfo? info, DiscordClient client, bool includePrBody = false, DiscordEmbedBuilder? builder = null, Octokit.PullRequest? currentPrInfo = null)
{ {
if ((info?.LatestBuild?.Windows?.Download ?? info?.LatestBuild?.Linux?.Download) is null) if ((info?.LatestBuild?.Windows?.Download ?? info?.LatestBuild?.Linux?.Download) is null)
return builder ?? new DiscordEmbedBuilder {Title = "Error", Description = "Error communicating with the update API. Try again later.", Color = Config.Colors.Maintenance}; return builder ?? new DiscordEmbedBuilder {Title = "Error", Description = "Error communicating with the update API. Try again later.", Color = Config.Colors.Maintenance};
@ -28,7 +27,7 @@ namespace CompatBot.Utils.ResultFormatters
var latestPr = latestBuild?.Pr; var latestPr = latestBuild?.Pr;
var currentPr = info.CurrentBuild?.Pr; var currentPr = info.CurrentBuild?.Pr;
string? url = null; string? url = null;
PrInfo? latestPrInfo = null; Octokit.PullRequest? latestPrInfo = null;
string prDesc = ""; string prDesc = "";
if (!justAppend) if (!justAppend)
@ -134,14 +133,14 @@ namespace CompatBot.Utils.ResultFormatters
DateTime? latestBuildTimestamp = null, currentBuildTimestamp = null; DateTime? latestBuildTimestamp = null, currentBuildTimestamp = null;
if (Config.GetAzureDevOpsClient() is {} azureClient) if (Config.GetAzureDevOpsClient() is {} azureClient)
{ {
var currentAppveyorBuild = await azureClient.GetMasterBuildInfoAsync(currentCommit, currentPrInfo?.MergedAt, Config.Cts.Token).ConfigureAwait(false); var currentAppveyorBuild = await azureClient.GetMasterBuildInfoAsync(currentCommit, currentPrInfo?.MergedAt?.DateTime, Config.Cts.Token).ConfigureAwait(false);
var latestAppveyorBuild = await azureClient.GetMasterBuildInfoAsync(latestCommit, latestPrInfo?.MergedAt, Config.Cts.Token).ConfigureAwait(false); var latestAppveyorBuild = await azureClient.GetMasterBuildInfoAsync(latestCommit, latestPrInfo?.MergedAt?.DateTime, Config.Cts.Token).ConfigureAwait(false);
latestBuildTimestamp = latestAppveyorBuild?.FinishTime; latestBuildTimestamp = latestAppveyorBuild?.FinishTime;
currentBuildTimestamp = currentAppveyorBuild?.FinishTime; currentBuildTimestamp = currentAppveyorBuild?.FinishTime;
if (!latestBuildTimestamp.HasValue) if (!latestBuildTimestamp.HasValue)
{ {
buildTimestampKind = "Merged"; buildTimestampKind = "Merged";
latestBuildTimestamp = currentPrInfo?.MergedAt; latestBuildTimestamp = currentPrInfo?.MergedAt?.DateTime;
} }
} }