mirror of
https://github.com/RPCS3/discord-bot.git
synced 2026-01-31 01:25:22 +01:00
add arm build downloads
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Json;
|
||||
using System.Text.Json;
|
||||
@@ -16,6 +17,7 @@ namespace CompatApiClient;
|
||||
public class Client: IDisposable
|
||||
{
|
||||
private static readonly MemoryCache ResponseCache = new(new MemoryCacheOptions { ExpirationScanFrequency = TimeSpan.FromHours(1) });
|
||||
private static readonly string[] BuildArchList = [ArchType.X64, ArchType.Arm];
|
||||
|
||||
private readonly HttpClient client = HttpClientFactory.Create(new CompressionMessageHandler());
|
||||
private readonly JsonSerializerOptions jsonOptions = new()
|
||||
@@ -100,33 +102,46 @@ public class Client: IDisposable
|
||||
}
|
||||
|
||||
// https://github.com/AniLeo/rpcs3-compatibility/wiki/API:-Update
|
||||
public async ValueTask<UpdateInfo?> GetUpdateAsync(CancellationToken cancellationToken, string? commit = null)
|
||||
public async ValueTask<UpdateInfo> GetUpdateAsync(CancellationToken cancellationToken, string? commit = null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(commit))
|
||||
if (commit is not {Length: >6})
|
||||
commit = "somecommit";
|
||||
var tries = 3;
|
||||
do
|
||||
var result = new UpdateInfo();
|
||||
foreach (var arch in BuildArchList)
|
||||
{
|
||||
try
|
||||
var tries = 3;
|
||||
do
|
||||
{
|
||||
using var message = new HttpRequestMessage(HttpMethod.Get, "https://update.rpcs3.net/?api=v1&c=" + commit);
|
||||
using var response = await client.SendAsync(message, HttpCompletionOption.ResponseContentRead, cancellationToken).ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
return await response.Content.ReadFromJsonAsync<UpdateInfo>(jsonOptions, cancellationToken).ConfigureAwait(false);
|
||||
using var message = new HttpRequestMessage(
|
||||
HttpMethod.Get,
|
||||
$"https://update.rpcs3.net/?api=v3&os_arch={arch}&os_type=all&c={commit}"
|
||||
);
|
||||
using var response = await client
|
||||
.SendAsync(message, HttpCompletionOption.ResponseContentRead, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
var info = await response.Content
|
||||
.ReadFromJsonAsync<UpdateCheckResult>(jsonOptions, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
if (info is not null)
|
||||
result[arch] = info;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ConsoleLogger.PrintError(e, response, false);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ConsoleLogger.PrintError(e, response, false);
|
||||
ApiConfig.Log.Warn(e);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ApiConfig.Log.Warn(e);
|
||||
}
|
||||
tries++;
|
||||
} while (tries < 3);
|
||||
return null;
|
||||
tries++;
|
||||
} while (tries < 3);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
|
||||
47
Clients/CompatApiClient/POCOs/UpdateCheckResult.cs
Normal file
47
Clients/CompatApiClient/POCOs/UpdateCheckResult.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
namespace CompatApiClient.POCOs;
|
||||
|
||||
public class UpdateCheckResult
|
||||
{
|
||||
public StatusCode ReturnCode;
|
||||
public BuildInfo LatestBuild = null!;
|
||||
public BuildInfo? CurrentBuild;
|
||||
public VersionInfo[]? Changelog;
|
||||
}
|
||||
|
||||
public class BuildInfo
|
||||
{
|
||||
public int? Pr;
|
||||
public string Datetime = null!;
|
||||
public string Version = null!;
|
||||
public BuildLink? Windows;
|
||||
public BuildLink? Linux;
|
||||
public BuildLink? Mac;
|
||||
}
|
||||
|
||||
public class BuildLink
|
||||
{
|
||||
public string Download = null!;
|
||||
public int? Size;
|
||||
public string? Checksum;
|
||||
}
|
||||
|
||||
public class VersionInfo
|
||||
{
|
||||
public string Verison = null!;
|
||||
public string? Title;
|
||||
}
|
||||
|
||||
public enum StatusCode
|
||||
{
|
||||
IllegalSearch = -3,
|
||||
Maintenance = -2,
|
||||
UnknownBuild = -1,
|
||||
NoUpdates = 0,
|
||||
UpdatesAvailable = 1,
|
||||
}
|
||||
|
||||
public static class ArchType
|
||||
{
|
||||
public const string X64 = "x64";
|
||||
public const string Arm = "arm64";
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
namespace CompatApiClient.POCOs;
|
||||
#nullable disable
|
||||
|
||||
public class UpdateInfo
|
||||
{
|
||||
public int ReturnCode;
|
||||
public BuildInfo LatestBuild;
|
||||
public BuildInfo CurrentBuild;
|
||||
}
|
||||
|
||||
public class BuildInfo
|
||||
{
|
||||
public int? Pr;
|
||||
public string Datetime;
|
||||
public BuildLink Windows;
|
||||
public BuildLink Linux;
|
||||
public BuildLink Mac;
|
||||
}
|
||||
|
||||
public class BuildLink
|
||||
{
|
||||
public string Download;
|
||||
public int? Size;
|
||||
public string Checksum;
|
||||
}
|
||||
|
||||
#nullable restore
|
||||
96
Clients/CompatApiClient/UpdateInfo.cs
Normal file
96
Clients/CompatApiClient/UpdateInfo.cs
Normal file
@@ -0,0 +1,96 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using CompatApiClient.POCOs;
|
||||
|
||||
namespace CompatApiClient;
|
||||
|
||||
public class UpdateInfo
|
||||
{
|
||||
public UpdateCheckResult? X64;
|
||||
public UpdateCheckResult? Arm;
|
||||
|
||||
public UpdateCheckResult? this[string key]
|
||||
{
|
||||
get => key switch
|
||||
{
|
||||
ArchType.X64 => X64,
|
||||
ArchType.Arm => Arm,
|
||||
_ => throw new KeyNotFoundException($"Unknown {nameof(ArchType)} '{key}'")
|
||||
};
|
||||
set
|
||||
{
|
||||
if (key is ArchType.X64)
|
||||
X64 = value;
|
||||
else if (key is ArchType.Arm)
|
||||
Arm = value;
|
||||
else
|
||||
throw new KeyNotFoundException($"Unknown {nameof(ArchType)} '{key}'");
|
||||
}
|
||||
}
|
||||
|
||||
public void SetCurrentAsLatest()
|
||||
{
|
||||
if (this is {X64.CurrentBuild: not null, Arm.CurrentBuild: not null})
|
||||
{
|
||||
X64.LatestBuild = X64.CurrentBuild;
|
||||
X64.CurrentBuild = null;
|
||||
Arm.LatestBuild = Arm.CurrentBuild;
|
||||
Arm.CurrentBuild = null;
|
||||
}
|
||||
else if (X64?.CurrentBuild is not null)
|
||||
{
|
||||
X64.LatestBuild = X64.CurrentBuild;
|
||||
X64.CurrentBuild = null;
|
||||
Arm = null;
|
||||
}
|
||||
else if (Arm?.CurrentBuild is not null)
|
||||
{
|
||||
Arm.LatestBuild = Arm.CurrentBuild;
|
||||
Arm.CurrentBuild = null;
|
||||
X64 = null;
|
||||
}
|
||||
}
|
||||
|
||||
public StatusCode ReturnCode => (X64?.ReturnCode, Arm?.ReturnCode) switch
|
||||
{
|
||||
({ } v1, { } v2) when v1 == v2 => v1,
|
||||
({ } v1 and >= StatusCode.UnknownBuild, { } v2 and >= StatusCode.UnknownBuild) => (StatusCode)Math.Max((int)v1,
|
||||
(int)v2),
|
||||
({ }, { }) => StatusCode.Maintenance,
|
||||
({ } v, null) => v,
|
||||
(null, { } v) => v,
|
||||
_ => StatusCode.Maintenance,
|
||||
};
|
||||
|
||||
public DateTime? LatestDatetime => ((X64?.LatestBuild.Datetime, Arm?.LatestBuild.Datetime) switch
|
||||
{
|
||||
({ Length: > 0 } d1, { Length: > 0 } d2) => StringComparer.Ordinal.Compare(d1, d1) >= 0 ? d1 : d2,
|
||||
({ Length: > 0 } d, _) => d,
|
||||
(_, { Length: > 0 } d) => d,
|
||||
_ => null,
|
||||
}) switch
|
||||
{
|
||||
{ Length: > 0 } v when DateTime.TryParse(v, out var result) => result,
|
||||
_ => null,
|
||||
};
|
||||
|
||||
public DateTime? CurrentDatetime => ((X64?.CurrentBuild?.Datetime, Arm?.CurrentBuild?.Datetime) switch
|
||||
{
|
||||
({ Length: > 0 } d1, { Length: > 0 } d2) => StringComparer.Ordinal.Compare(d1, d1) >= 0 ? d1 : d2,
|
||||
({ Length: > 0 } d, _) => d,
|
||||
(_, { Length: > 0 } d) => d,
|
||||
_ => null,
|
||||
}) switch
|
||||
{
|
||||
{ Length: > 0 } v when DateTime.TryParse(v, out var result) => result,
|
||||
_ => null,
|
||||
};
|
||||
|
||||
public int? LatestPr => (X64?.LatestBuild.Pr, Arm?.LatestBuild.Pr) switch
|
||||
{
|
||||
//(int pr1, int pr2) when pr1 != pr2 => throw new InvalidDataException($"Expected the same PR for both {nameof(ArchType)}, but got {pr1} and {pr2}"),
|
||||
(int pr, _) => pr,
|
||||
(_, int pr) => pr,
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
@@ -90,7 +91,6 @@ public partial class Client
|
||||
ExcludePullRequests = false,
|
||||
Event = "pull_request",
|
||||
HeadSha = commit,
|
||||
//Branch = $"refs/pull/{pr}/merge",
|
||||
};
|
||||
var runsList = await client.Actions.Workflows.Runs.ListByWorkflow(OwnerId, RepoId, wfId, wfrRequest).ConfigureAwait(false);
|
||||
var builds = runsList.WorkflowRuns
|
||||
@@ -299,4 +299,72 @@ public partial class Client
|
||||
BuildInfoCache.Set(cacheKey, result, TimeSpan.FromDays(1));
|
||||
return result;
|
||||
}
|
||||
|
||||
public async ValueTask<List<BuildInfo>?> GetMasterBuildsAsync(string? oldestMergeCommit, string? newestMergeCommit, DateTime? oldestTimestamp, CancellationToken cancellationToken)
|
||||
{
|
||||
if (oldestMergeCommit is not {Length: >=6} || newestMergeCommit is not {Length: >=6})
|
||||
return null;
|
||||
|
||||
if (await GetWorkflowIdAsync().ConfigureAwait(false) is not long wfId)
|
||||
return null;
|
||||
|
||||
oldestMergeCommit = oldestMergeCommit.ToLower();
|
||||
newestMergeCommit = newestMergeCommit.ToLower();
|
||||
var wfrRequest = new WorkflowRunsRequest
|
||||
{
|
||||
ExcludePullRequests = true,
|
||||
Event = "push",
|
||||
Created = $"{oldestTimestamp:yyyy-MM-dd}..*",
|
||||
Status = CheckRunStatusFilter.Completed,
|
||||
Branch = "master",
|
||||
};
|
||||
var runsList = await client.Actions.Workflows.Runs.ListByWorkflow(OwnerId, RepoId, wfId, wfrRequest).ConfigureAwait(false);
|
||||
var builds = runsList.WorkflowRuns
|
||||
.OrderByDescending(r => r.CreatedAt)
|
||||
.SkipWhile(b => !newestMergeCommit.Equals(b.HeadSha, StringComparison.OrdinalIgnoreCase))
|
||||
.Skip(1)
|
||||
.TakeWhile(b => !oldestMergeCommit.Equals(b.HeadSha, StringComparison.OrdinalIgnoreCase))
|
||||
.ToList();
|
||||
return await builds
|
||||
.ToAsyncEnumerable()
|
||||
.SelectAwait(async b => await GetArtifactsInfoAsync(b.HeadSha, b, cancellationToken).ConfigureAwait(false))
|
||||
.ToListAsync(cancellationToken: cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async ValueTask<BuildInfo?> GetMasterBuildInfoAsync(string? commit, DateTime? oldestTimestamp, CancellationToken cancellationToken)
|
||||
{
|
||||
if (commit is not {Length: >=6})
|
||||
return null;
|
||||
|
||||
if (await GetWorkflowIdAsync().ConfigureAwait(false) is not long wfId)
|
||||
return null;
|
||||
|
||||
commit = commit.ToLower();
|
||||
if (BuildInfoCache.TryGetValue(commit, out BuildInfo? result) && result is not null)
|
||||
return result;
|
||||
|
||||
var wfrRequest = new WorkflowRunsRequest
|
||||
{
|
||||
ExcludePullRequests = true,
|
||||
Event = "push",
|
||||
Created = $"{oldestTimestamp:yyyy-MM-dd}..*",
|
||||
Status = CheckRunStatusFilter.Completed,
|
||||
Branch = "master",
|
||||
};
|
||||
var runsList = await client.Actions.Workflows.Runs.ListByWorkflow(OwnerId, RepoId, wfId, wfrRequest).ConfigureAwait(false);
|
||||
|
||||
var builds = runsList.WorkflowRuns
|
||||
.Where(b => commit.Equals(b.HeadSha, StringComparison.OrdinalIgnoreCase))
|
||||
.OrderByDescending(b => b.CreatedAt)
|
||||
.ToList();
|
||||
if (builds.FirstOrDefault() is not {} latestBuild)
|
||||
return null;
|
||||
|
||||
result = await GetArtifactsInfoAsync(commit, latestBuild, cancellationToken).ConfigureAwait(false);
|
||||
if (result is { Status: WorkflowRunStatus.Completed, Result: WorkflowRunConclusion.Success })
|
||||
BuildInfoCache.Set(commit, result, TimeSpan.FromHours(1));
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -8,6 +8,7 @@
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="9.0.3" />
|
||||
<PackageReference Include="Octokit" Version="14.0.0" />
|
||||
<PackageReference Include="SharpCompress" Version="0.39.0" />
|
||||
<PackageReference Include="System.Linq.Async" Version="6.0.1" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\CompatApiClient\CompatApiClient.csproj" />
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Text.Json;
|
||||
using CompatApiClient;
|
||||
using CompatApiClient.POCOs;
|
||||
using CompatBot.Database;
|
||||
using CompatBot.EventHandlers;
|
||||
@@ -7,6 +8,7 @@ using CompatBot.Utils.ResultFormatters;
|
||||
using DSharpPlus.Commands.Processors.TextCommands;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.TeamFoundation.Build.WebApi;
|
||||
using Octokit;
|
||||
|
||||
namespace CompatBot.Commands;
|
||||
|
||||
@@ -19,12 +21,12 @@ internal static partial class CompatList
|
||||
[Description("Link to the latest RPCS3 build")]
|
||||
public static ValueTask Latest(SlashCommandContext ctx) => CheckForRpcs3UpdatesAsync(ctx, respond: true);
|
||||
|
||||
/*
|
||||
#if DEBUG
|
||||
[Command("since")]
|
||||
[Description("Show additional info about changes since specified update")]
|
||||
public static ValueTask Since(TextCommandContext ctx, [Description("Commit hash of the update, such as `46abe0f31`")] string commit)
|
||||
public static ValueTask Since(SlashCommandContext ctx, [Description("Commit hash of the update, such as `46abe0f31`")] string commit)
|
||||
=> CheckForRpcs3UpdatesAsync(ctx, respond: true, sinceCommit: commit);
|
||||
*/
|
||||
#endif
|
||||
|
||||
[Command("clear"), RequiresBotModRole]
|
||||
[Description("Clear the update info cache and post the latest RPCS3 build announcement")]
|
||||
@@ -69,13 +71,13 @@ internal static partial class CompatList
|
||||
|
||||
var updateAnnouncementRestore = emptyBotMsg is not null;
|
||||
var info = await Client.GetUpdateAsync(Config.Cts.Token, sinceCommit).ConfigureAwait(false);
|
||||
if (info?.ReturnCode != 1 && sinceCommit != null)
|
||||
if (info.ReturnCode != StatusCode.UpdatesAvailable && sinceCommit is not null)
|
||||
info = await Client.GetUpdateAsync(Config.Cts.Token).ConfigureAwait(false);
|
||||
|
||||
if (updateAnnouncementRestore && info?.CurrentBuild != null)
|
||||
info.LatestBuild = info.CurrentBuild;
|
||||
if (updateAnnouncementRestore)
|
||||
info.SetCurrentAsLatest();
|
||||
var embed = await info.AsEmbedAsync(discordClient, !respond).ConfigureAwait(false);
|
||||
if (info is null || embed.Color!.Value.Value == Config.Colors.Maintenance.Value)
|
||||
if (info.ReturnCode < StatusCode.UnknownBuild || embed.Color?.Value == Config.Colors.Maintenance.Value)
|
||||
{
|
||||
if (updateAnnouncementRestore)
|
||||
{
|
||||
@@ -87,10 +89,8 @@ internal static partial class CompatList
|
||||
}
|
||||
else if (!updateAnnouncementRestore)
|
||||
{
|
||||
if (cachedUpdateInfo?.LatestBuild?.Datetime is string previousBuildTimeStr
|
||||
&& info.LatestBuild?.Datetime is string newBuildTimeStr
|
||||
&& DateTime.TryParse(previousBuildTimeStr, out var previousBuildTime)
|
||||
&& DateTime.TryParse(newBuildTimeStr, out var newBuildTime)
|
||||
if (cachedUpdateInfo?.LatestDatetime is DateTime previousBuildTime
|
||||
&& info.LatestDatetime is DateTime newBuildTime
|
||||
&& newBuildTime > previousBuildTime)
|
||||
cachedUpdateInfo = info;
|
||||
}
|
||||
@@ -113,7 +113,7 @@ internal static partial class CompatList
|
||||
return;
|
||||
}
|
||||
|
||||
var latestUpdatePr = info?.LatestBuild?.Pr?.ToString();
|
||||
var latestUpdatePr = info.LatestPr?.ToString();
|
||||
var match = (
|
||||
from field in embed.Fields
|
||||
let m = UpdateVersionRegex().Match(field.Value)
|
||||
@@ -122,15 +122,15 @@ internal static partial class CompatList
|
||||
).FirstOrDefault();
|
||||
var latestUpdateBuild = match?.Groups["build"].Value;
|
||||
|
||||
if (string.IsNullOrEmpty(latestUpdatePr)
|
||||
if (latestUpdatePr is not {Length: >0}
|
||||
|| lastUpdateInfo == latestUpdatePr
|
||||
|| !await UpdateCheck.WaitAsync(0).ConfigureAwait(false))
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
if (!string.IsNullOrEmpty(lastFullBuildNumber)
|
||||
&& !string.IsNullOrEmpty(latestUpdateBuild)
|
||||
if (lastFullBuildNumber is {Length: >0}
|
||||
&& latestUpdateBuild is {Length: >0}
|
||||
&& int.TryParse(lastFullBuildNumber, out var lastSaveBuild)
|
||||
&& int.TryParse(latestUpdateBuild, out var latestBuild)
|
||||
&& latestBuild <= lastSaveBuild)
|
||||
@@ -147,7 +147,7 @@ internal static partial class CompatList
|
||||
return;
|
||||
}
|
||||
|
||||
if (embed.Color!.Value.Value == Config.Colors.Maintenance.Value)
|
||||
if (embed.Color?.Value == Config.Colors.Maintenance.Value)
|
||||
return;
|
||||
|
||||
await CheckMissedBuildsBetweenAsync(discordClient, compatChannel, lastUpdateInfo, latestUpdatePr, Config.Cts.Token).ConfigureAwait(false);
|
||||
@@ -157,18 +157,14 @@ internal static partial class CompatList
|
||||
lastFullBuildNumber = latestUpdateBuild;
|
||||
await using (var wdb = await BotDb.OpenWriteAsync().ConfigureAwait(false))
|
||||
{
|
||||
var currentState = await wdb.BotState.FirstOrDefaultAsync(k => k.Key == Rpcs3UpdateStateKey)
|
||||
.ConfigureAwait(false);
|
||||
var currentState = await wdb.BotState.FirstOrDefaultAsync(k => k.Key == Rpcs3UpdateStateKey).ConfigureAwait(false);
|
||||
if (currentState == null)
|
||||
await wdb.BotState.AddAsync(new() { Key = Rpcs3UpdateStateKey, Value = latestUpdatePr })
|
||||
.ConfigureAwait(false);
|
||||
await wdb.BotState.AddAsync(new() { Key = Rpcs3UpdateStateKey, Value = latestUpdatePr }).ConfigureAwait(false);
|
||||
else
|
||||
currentState.Value = latestUpdatePr;
|
||||
var savedLastBuild = await wdb.BotState.FirstOrDefaultAsync(k => k.Key == Rpcs3UpdateBuildKey)
|
||||
.ConfigureAwait(false);
|
||||
var savedLastBuild = await wdb.BotState.FirstOrDefaultAsync(k => k.Key == Rpcs3UpdateBuildKey).ConfigureAwait(false);
|
||||
if (savedLastBuild == null)
|
||||
await wdb.BotState.AddAsync(new() { Key = Rpcs3UpdateBuildKey, Value = latestUpdateBuild })
|
||||
.ConfigureAwait(false);
|
||||
await wdb.BotState.AddAsync(new() { Key = Rpcs3UpdateBuildKey, Value = latestUpdateBuild }).ConfigureAwait(false);
|
||||
else
|
||||
savedLastBuild.Value = latestUpdateBuild;
|
||||
await wdb.SaveChangesAsync(Config.Cts.Token).ConfigureAwait(false);
|
||||
@@ -194,7 +190,7 @@ internal static partial class CompatList
|
||||
var mergedPrs = await GithubClient.GetClosedPrsAsync(cancellationToken).ConfigureAwait(false); // this will cache 30 latest PRs
|
||||
var newestPrCommit = await GithubClient.GetPrInfoAsync(newestPr, cancellationToken).ConfigureAwait(false);
|
||||
var oldestPrCommit = await GithubClient.GetPrInfoAsync(oldestPr, cancellationToken).ConfigureAwait(false);
|
||||
if (newestPrCommit?.MergedAt == null || oldestPrCommit?.MergedAt == null)
|
||||
if (newestPrCommit?.MergedAt is null || oldestPrCommit?.MergedAt is null)
|
||||
return;
|
||||
|
||||
mergedPrs = mergedPrs?.Where(pri => pri.MergedAt.HasValue)
|
||||
@@ -203,10 +199,16 @@ internal static partial class CompatList
|
||||
.Skip(1)
|
||||
.TakeWhile(pri => pri.Number != newestPr)
|
||||
.ToList();
|
||||
if (mergedPrs is null or {Count: 0})
|
||||
if (mergedPrs is not {Count: >0})
|
||||
return;
|
||||
|
||||
var failedBuilds = await Config.GetAzureDevOpsClient().GetMasterBuildsAsync(
|
||||
var failedAzureBuilds = await Config.GetAzureDevOpsClient().GetMasterBuildsAsync(
|
||||
oldestPrCommit.MergeCommitSha,
|
||||
newestPrCommit.MergeCommitSha,
|
||||
oldestPrCommit.MergedAt?.DateTime,
|
||||
cancellationToken
|
||||
).ConfigureAwait(false);
|
||||
var failedGhBuilds = await GithubClient.GetMasterBuildsAsync(
|
||||
oldestPrCommit.MergeCommitSha,
|
||||
newestPrCommit.MergeCommitSha,
|
||||
oldestPrCommit.MergedAt?.DateTime,
|
||||
@@ -214,31 +216,62 @@ internal static partial class CompatList
|
||||
).ConfigureAwait(false);
|
||||
foreach (var mergedPr in mergedPrs)
|
||||
{
|
||||
var updateInfo = await Client.GetUpdateAsync(cancellationToken, mergedPr.MergeCommitSha).ConfigureAwait(false)
|
||||
?? new UpdateInfo {ReturnCode = -1};
|
||||
if (updateInfo.ReturnCode is 0 or 1) // latest or known build
|
||||
var updateInfo = await Client.GetUpdateAsync(cancellationToken, mergedPr.MergeCommitSha).ConfigureAwait(false);
|
||||
if (updateInfo.ReturnCode >= StatusCode.NoUpdates) // latest or known build
|
||||
{
|
||||
updateInfo.LatestBuild = updateInfo.CurrentBuild;
|
||||
updateInfo.CurrentBuild = null;
|
||||
if (updateInfo is { X64.CurrentBuild: not null, Arm.CurrentBuild: not null })
|
||||
{
|
||||
updateInfo.X64.LatestBuild = updateInfo.X64.CurrentBuild;
|
||||
updateInfo.X64.CurrentBuild = null;
|
||||
updateInfo.Arm.LatestBuild = updateInfo.Arm.CurrentBuild;
|
||||
updateInfo.Arm.CurrentBuild = null;
|
||||
}
|
||||
else if (updateInfo.X64?.CurrentBuild is not null)
|
||||
{
|
||||
updateInfo.X64.LatestBuild = updateInfo.X64.CurrentBuild;
|
||||
updateInfo.X64.CurrentBuild = null;
|
||||
updateInfo.Arm = null;
|
||||
}
|
||||
else if (updateInfo.Arm?.CurrentBuild is not null)
|
||||
{
|
||||
updateInfo.Arm.LatestBuild = updateInfo.Arm.CurrentBuild;
|
||||
updateInfo.Arm.CurrentBuild = null;
|
||||
updateInfo.X64 = null;
|
||||
}
|
||||
var embed = await updateInfo.AsEmbedAsync(discordClient, true).ConfigureAwait(false);
|
||||
await compatChannel.SendMessageAsync(embed: embed.Build()).ConfigureAwait(false);
|
||||
}
|
||||
else if (updateInfo.ReturnCode == -1) // unknown build
|
||||
else if (updateInfo.ReturnCode is StatusCode.UnknownBuild)
|
||||
{
|
||||
var masterBuildInfo = failedBuilds?.FirstOrDefault(b => b.Commit?.Equals(mergedPr.MergeCommitSha, StringComparison.InvariantCultureIgnoreCase) is true);
|
||||
var buildTime = masterBuildInfo?.FinishTime;
|
||||
if (masterBuildInfo != null)
|
||||
var masterBuildInfoAzure = failedAzureBuilds?.FirstOrDefault(b => b.Commit?.Equals(mergedPr.MergeCommitSha, StringComparison.OrdinalIgnoreCase) is true);
|
||||
var masterBuildInfoGh = failedGhBuilds?.FirstOrDefault(b => b.Commit?.Equals(mergedPr.MergeCommitSha, StringComparison.OrdinalIgnoreCase) is true);
|
||||
var buildTime = masterBuildInfoGh?.FinishTime ?? masterBuildInfoAzure?.FinishTime;
|
||||
if (masterBuildInfoAzure is not null || masterBuildInfoGh is not null)
|
||||
{
|
||||
updateInfo = new()
|
||||
{
|
||||
ReturnCode = 1,
|
||||
LatestBuild = new()
|
||||
X64 = new()
|
||||
{
|
||||
Datetime = buildTime?.ToString("yyyy-MM-dd HH:mm:ss"),
|
||||
Pr = mergedPr.Number,
|
||||
Windows = new() {Download = masterBuildInfo.WindowsBuildDownloadLink ?? ""},
|
||||
Linux = new() { Download = masterBuildInfo.LinuxBuildDownloadLink ?? "" },
|
||||
Mac = new() { Download = masterBuildInfo.MacBuildDownloadLink ?? "" },
|
||||
ReturnCode = StatusCode.UpdatesAvailable,
|
||||
LatestBuild = new()
|
||||
{
|
||||
Datetime = buildTime!.Value.ToString("yyyy-MM-dd HH:mm:ss"),
|
||||
Pr = mergedPr.Number,
|
||||
Windows = new() { Download = masterBuildInfoAzure?.WindowsBuildDownloadLink ?? masterBuildInfoGh?.WindowsBuildDownloadLink ?? "" },
|
||||
Linux = new() { Download = masterBuildInfoAzure?.LinuxBuildDownloadLink ?? masterBuildInfoGh?.LinuxBuildDownloadLink ?? "" },
|
||||
Mac = new() { Download = masterBuildInfoAzure?.MacBuildDownloadLink ?? masterBuildInfoGh?.MacBuildDownloadLink ?? "" },
|
||||
},
|
||||
},
|
||||
Arm = new()
|
||||
{
|
||||
ReturnCode = StatusCode.UpdatesAvailable,
|
||||
LatestBuild = new()
|
||||
{
|
||||
Datetime = buildTime!.Value.ToString("yyyy-MM-dd HH:mm:ss"),
|
||||
Pr = mergedPr.Number,
|
||||
Linux = new() { Download = masterBuildInfoAzure?.LinuxArmBuildDownloadLink ?? masterBuildInfoGh?.LinuxArmBuildDownloadLink ?? "" },
|
||||
Mac = new() { Download = masterBuildInfoAzure?.MacArmBuildDownloadLink ?? masterBuildInfoGh?.MacArmBuildDownloadLink ?? "" },
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -246,20 +279,27 @@ internal static partial class CompatList
|
||||
{
|
||||
updateInfo = new()
|
||||
{
|
||||
ReturnCode = 1,
|
||||
LatestBuild = new()
|
||||
X64 = new()
|
||||
{
|
||||
Pr = mergedPr.Number,
|
||||
Windows = new() {Download = ""},
|
||||
Linux = new() { Download = "" },
|
||||
Mac = new() { Download = "" },
|
||||
ReturnCode = StatusCode.UpdatesAvailable,
|
||||
LatestBuild = new()
|
||||
{
|
||||
Pr = mergedPr.Number,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
var embed = await updateInfo.AsEmbedAsync(discordClient, true).ConfigureAwait(false);
|
||||
embed.Color = Config.Colors.PrClosed;
|
||||
embed.ClearFields();
|
||||
var reason = masterBuildInfo?.Result switch
|
||||
var reason = masterBuildInfoGh?.Result switch
|
||||
{
|
||||
WorkflowRunConclusion.Success => "Built",
|
||||
WorkflowRunConclusion.Failure => "Failed to build",
|
||||
WorkflowRunConclusion.Cancelled => "Cancelled",
|
||||
WorkflowRunConclusion.TimedOut => "Timed out",
|
||||
_ => null,
|
||||
} ?? masterBuildInfoAzure?.Result switch
|
||||
{
|
||||
BuildResult.Succeeded => "Built",
|
||||
BuildResult.PartiallySucceeded => "Built",
|
||||
@@ -267,7 +307,7 @@ internal static partial class CompatList
|
||||
BuildResult.Canceled => "Cancelled",
|
||||
_ => null,
|
||||
};
|
||||
if (buildTime.HasValue && reason != null)
|
||||
if (buildTime.HasValue && reason is not null)
|
||||
embed.WithFooter($"{reason} on {buildTime:u} ({(DateTime.UtcNow - buildTime.Value).AsTimeDeltaDescription()} ago)");
|
||||
else
|
||||
embed.WithFooter(reason ?? "Never built");
|
||||
|
||||
@@ -33,9 +33,11 @@ internal static partial class CompatList
|
||||
|
||||
static CompatList()
|
||||
{
|
||||
using var db = BotDb.OpenRead();
|
||||
lastUpdateInfo = db.BotState.FirstOrDefault(k => k.Key == Rpcs3UpdateStateKey)?.Value;
|
||||
lastFullBuildNumber = db.BotState.FirstOrDefault(k => k.Key == Rpcs3UpdateBuildKey)?.Value;
|
||||
using (var db = BotDb.OpenRead())
|
||||
{
|
||||
lastUpdateInfo = db.BotState.FirstOrDefault(k => k.Key == Rpcs3UpdateStateKey)?.Value;
|
||||
lastFullBuildNumber = db.BotState.FirstOrDefault(k => k.Key == Rpcs3UpdateBuildKey)?.Value;
|
||||
}
|
||||
//lastUpdateInfo = "8022";
|
||||
if (lastUpdateInfo is {Length: >0} strPr && int.TryParse(strPr, out var pr))
|
||||
{
|
||||
@@ -43,11 +45,10 @@ internal static partial class CompatList
|
||||
{
|
||||
var prInfo = GithubClient.GetPrInfoAsync(pr, Config.Cts.Token).ConfigureAwait(false).GetAwaiter().GetResult();
|
||||
cachedUpdateInfo = Client.GetUpdateAsync(Config.Cts.Token, prInfo?.MergeCommitSha).ConfigureAwait(false).GetAwaiter().GetResult();
|
||||
if (cachedUpdateInfo?.CurrentBuild == null)
|
||||
if ((cachedUpdateInfo.X64 ?? cachedUpdateInfo.Arm)?.CurrentBuild is null)
|
||||
return;
|
||||
|
||||
cachedUpdateInfo.LatestBuild = cachedUpdateInfo.CurrentBuild;
|
||||
cachedUpdateInfo.CurrentBuild = null;
|
||||
|
||||
cachedUpdateInfo.SetCurrentAsLatest();
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
@@ -135,13 +135,13 @@ internal sealed partial class ContentFilters
|
||||
if (explanation is { Length: > 0 } &&
|
||||
!await wdb.Explanation.AnyAsync(e => e.Keyword == explanation).ConfigureAwait(false))
|
||||
{
|
||||
await ctx.RespondAsync($"❌ Unknown explanation term: {explanation}", ephemeral: ephemeral)
|
||||
.ConfigureAwait(false);
|
||||
await ctx.RespondAsync($"❌ Unknown explanation term: {explanation}", ephemeral: ephemeral).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
var isNewFilter = true;
|
||||
var filter = await wdb.Piracystring.FirstOrDefaultAsync(ps => ps.String == trigger && ps.Disabled)
|
||||
var filter = await wdb.Piracystring
|
||||
.FirstOrDefaultAsync(ps => ps.String == trigger && ps.Disabled)
|
||||
.ConfigureAwait(false);
|
||||
if (filter is null)
|
||||
filter = new() { String = trigger };
|
||||
@@ -184,8 +184,7 @@ internal sealed partial class ContentFilters
|
||||
$"{member?.GetMentionWithNickname()} added a new content filter: `{filter.String.Sanitize()}`";
|
||||
if (!string.IsNullOrEmpty(filter.ValidatingRegex))
|
||||
reportMsg += $"\nValidation: `{filter.ValidatingRegex}`";
|
||||
await ctx.Client.ReportAsync("🆕 Content filter created", reportMsg, null, ReportSeverity.Low)
|
||||
.ConfigureAwait(false);
|
||||
await ctx.Client.ReportAsync("🆕 Content filter created", reportMsg, null, ReportSeverity.Low).ConfigureAwait(false);
|
||||
}
|
||||
ContentFilter.RebuildMatcher();
|
||||
}
|
||||
@@ -327,8 +326,7 @@ internal sealed partial class ContentFilters
|
||||
if (explanation is { Length: > 0 } &&
|
||||
!await wdb.Explanation.AnyAsync(e => e.Keyword == explanation).ConfigureAwait(false))
|
||||
{
|
||||
await ctx.RespondAsync($"❌ Unknown explanation term: {explanation}", ephemeral: ephemeral)
|
||||
.ConfigureAwait(false);
|
||||
await ctx.RespondAsync($"❌ Unknown explanation term: {explanation}", ephemeral: ephemeral).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -249,8 +249,7 @@ internal static partial class Misc
|
||||
var count = await db.Thumbnail.CountAsync().ConfigureAwait(false);
|
||||
if (count is 0)
|
||||
{
|
||||
await ctx.RespondAsync("Sorry, I have no information about a single game yet", ephemeral: true)
|
||||
.ConfigureAwait(false);
|
||||
await ctx.RespondAsync("Sorry, I have no information about a single game yet", ephemeral: true).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -265,8 +264,7 @@ internal static partial class Misc
|
||||
return;
|
||||
}
|
||||
|
||||
var result = await ProductCodeLookup.LookupProductCodeAndFormatAsync(ctx.Client, [productCode.ProductCode])
|
||||
.ConfigureAwait(false);
|
||||
var result = await ProductCodeLookup.LookupProductCodeAndFormatAsync(ctx.Client, [productCode.ProductCode]).ConfigureAwait(false);
|
||||
await ctx.RespondAsync(result[0].builder, ephemeral: ephemeral).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -365,21 +365,21 @@ internal sealed class Pr
|
||||
var mergeTime = prInfo.MergedAt.GetValueOrDefault();
|
||||
var now = DateTime.UtcNow;
|
||||
var updateInfo = await CompatApiClient.GetUpdateAsync(Config.Cts.Token, linkOld ? prInfo.MergeCommitSha : null).ConfigureAwait(false);
|
||||
if (updateInfo is not null)
|
||||
if (updateInfo.LatestDatetime is DateTime masterBuildTime && masterBuildTime.Ticks >= mergeTime.Ticks)
|
||||
embed = await updateInfo.AsEmbedAsync(client, false, embed, prInfo, linkOld).ConfigureAwait(false);
|
||||
else
|
||||
{
|
||||
if (DateTime.TryParse(updateInfo.LatestBuild?.Datetime, out var masterBuildTime) && masterBuildTime.Ticks >= mergeTime.Ticks)
|
||||
embed = await updateInfo.AsEmbedAsync(client, false, embed, prInfo, linkOld).ConfigureAwait(false);
|
||||
else
|
||||
{
|
||||
var waitTime = TimeSpan.FromMinutes(5);
|
||||
var avgBuildTime = (await GithubClient.GetPipelineDurationAsync(Config.Cts.Token).ConfigureAwait(false)).Mean;
|
||||
if (now < mergeTime + avgBuildTime)
|
||||
waitTime = mergeTime + avgBuildTime - now;
|
||||
embed.AddField("Latest master build", $"""
|
||||
This pull request has been merged, and will be part of `master` very soon.
|
||||
Please check again in {waitTime.AsTimeDeltaDescription()}.
|
||||
""");
|
||||
}
|
||||
var waitTime = TimeSpan.FromMinutes(5);
|
||||
var avgBuildTime = (await GithubClient.GetPipelineDurationAsync(Config.Cts.Token).ConfigureAwait(false)).Mean;
|
||||
if (now < mergeTime + avgBuildTime)
|
||||
waitTime = mergeTime + avgBuildTime - now;
|
||||
embed.AddField(
|
||||
"Latest master build",
|
||||
$"""
|
||||
This pull request has been merged, and will be part of `master` very soon.
|
||||
Please check again in {waitTime.AsTimeDeltaDescription()}.
|
||||
"""
|
||||
);
|
||||
}
|
||||
}
|
||||
return result.AddEmbed(embed);
|
||||
|
||||
@@ -112,8 +112,7 @@ public static class LogParsingHandler
|
||||
$">>>>>>> {message.Id % 100} Parsing log '{source.FileName}' from {message.Author.Username}#{message.Author.Discriminator} ({message.Author.Id}) using {source.GetType().Name} ({source.SourceFileSize} bytes)…");
|
||||
var analyzingProgressEmbed = GetAnalyzingMsgEmbed(client);
|
||||
var msgBuilder = new DiscordMessageBuilder()
|
||||
.AddEmbed(await analyzingProgressEmbed.AddAuthorAsync(client, message, source)
|
||||
.ConfigureAwait(false))
|
||||
.AddEmbed(await analyzingProgressEmbed.AddAuthorAsync(client, message, source).ConfigureAwait(false))
|
||||
.WithReply(message.Id);
|
||||
botMsg = await channel.SendMessageAsync(msgBuilder).ConfigureAwait(false);
|
||||
parsedLog = true;
|
||||
@@ -130,8 +129,7 @@ public static class LogParsingHandler
|
||||
source,
|
||||
async () => botMsg = await botMsg.UpdateOrCreateMessageAsync(
|
||||
channel,
|
||||
embed: await analyzingProgressEmbed.AddAuthorAsync(client, message, source)
|
||||
.ConfigureAwait(false)
|
||||
embed: await analyzingProgressEmbed.AddAuthorAsync(client, message, source).ConfigureAwait(false)
|
||||
).ConfigureAwait(false),
|
||||
combinedTokenSource.Token
|
||||
).ConfigureAwait(false);
|
||||
@@ -175,15 +173,12 @@ public static class LogParsingHandler
|
||||
}
|
||||
var yarr = client.GetEmoji(":piratethink:", "☠");
|
||||
result.ReadBytes = 0;
|
||||
if (await message.Author.IsWhitelistedAsync(client, channel.Guild)
|
||||
.ConfigureAwait(false))
|
||||
if (await message.Author.IsWhitelistedAsync(client, channel.Guild).ConfigureAwait(false))
|
||||
{
|
||||
var piracyWarning = await result.AsEmbedAsync(client, message, source)
|
||||
.ConfigureAwait(false);
|
||||
var piracyWarning = await result.AsEmbedAsync(client, message, source).ConfigureAwait(false);
|
||||
piracyWarning = piracyWarning.WithDescription(
|
||||
"Please remove the log and issue warning to the original author of the log");
|
||||
botMsg = await botMsg.UpdateOrCreateMessageAsync(channel, embed: piracyWarning)
|
||||
.ConfigureAwait(false);
|
||||
botMsg = await botMsg.UpdateOrCreateMessageAsync(channel, embed: piracyWarning).ConfigureAwait(false);
|
||||
var matchedOn = ContentFilter.GetMatchedScope(result.SelectedFilter,
|
||||
result.SelectedFilterContext);
|
||||
await client.ReportAsync(yarr + " Pirated Release (whitelisted by role)", message,
|
||||
@@ -269,19 +264,16 @@ public static class LogParsingHandler
|
||||
if (!force
|
||||
&& string.IsNullOrEmpty(message.Content)
|
||||
&& !isSpamChannel
|
||||
&& !await message.Author.IsSmartlistedAsync(client, message.Channel.Guild)
|
||||
.ConfigureAwait(false))
|
||||
&& !await message.Author.IsSmartlistedAsync(client, message.Channel.Guild).ConfigureAwait(false))
|
||||
{
|
||||
var threshold = DateTime.UtcNow.AddMinutes(-15);
|
||||
var previousMessages = await channel.GetMessagesBeforeCachedAsync(message.Id)
|
||||
.ConfigureAwait(false);
|
||||
var previousMessages = await channel.GetMessagesBeforeCachedAsync(message.Id).ConfigureAwait(false);
|
||||
previousMessages = previousMessages.TakeWhile((msg, num) =>
|
||||
num < 15 || msg.Timestamp.UtcDateTime > threshold).ToList();
|
||||
if (!previousMessages.Any(m =>
|
||||
m.Author == message.Author && !string.IsNullOrEmpty(m.Content)))
|
||||
{
|
||||
var botSpamChannel = await client.GetChannelAsync(Config.BotSpamId)
|
||||
.ConfigureAwait(false);
|
||||
var botSpamChannel = await client.GetChannelAsync(Config.BotSpamId).ConfigureAwait(false);
|
||||
if (isHelpChannel)
|
||||
await botMsg.UpdateOrCreateMessageAsync(
|
||||
channel,
|
||||
|
||||
@@ -411,9 +411,10 @@ internal static partial class LogParserResult
|
||||
|
||||
var updateInfo = await CheckForUpdateAsync(items).ConfigureAwait(false);
|
||||
var buildBranch = items["build_branch"]?.ToLowerInvariant();
|
||||
if (updateInfo != null
|
||||
if (updateInfo is not null
|
||||
&& (buildBranch is "master" or "head" or "spu_perf"
|
||||
|| string.IsNullOrEmpty(buildBranch) && updateInfo.CurrentBuild != null))
|
||||
|| buildBranch is not {Length: >0}
|
||||
&& (updateInfo.X64?.CurrentBuild is not null || updateInfo.Arm?.CurrentBuild is not null)))
|
||||
{
|
||||
string prefix = "⚠️";
|
||||
string timeDeltaStr;
|
||||
|
||||
@@ -778,10 +778,15 @@ internal static partial class LogParserResult
|
||||
if (string.IsNullOrEmpty(currentBuildCommit))
|
||||
currentBuildCommit = null;
|
||||
var updateInfo = await CompatClient.GetUpdateAsync(Config.Cts.Token, currentBuildCommit).ConfigureAwait(false);
|
||||
if (updateInfo?.ReturnCode != 1 && currentBuildCommit != null)
|
||||
if (updateInfo.ReturnCode != StatusCode.UpdatesAvailable && currentBuildCommit is not null)
|
||||
updateInfo = await CompatClient.GetUpdateAsync(Config.Cts.Token).ConfigureAwait(false);
|
||||
var link = updateInfo?.LatestBuild?.Windows?.Download ?? updateInfo?.LatestBuild?.Linux?.Download ?? updateInfo?.LatestBuild?.Mac?.Download;
|
||||
if (string.IsNullOrEmpty(link))
|
||||
var link = updateInfo.X64?.LatestBuild.Windows?.Download
|
||||
?? updateInfo.X64?.LatestBuild.Linux?.Download
|
||||
?? updateInfo.X64?.LatestBuild.Mac?.Download
|
||||
??updateInfo.Arm?.LatestBuild.Windows?.Download
|
||||
?? updateInfo.Arm?.LatestBuild.Linux?.Download
|
||||
?? updateInfo.Arm?.LatestBuild.Mac?.Download;
|
||||
if (updateInfo.ReturnCode is not StatusCode.UpdatesAvailable || link is null)
|
||||
return null;
|
||||
|
||||
var latestBuildInfo = BuildInfoInUpdate().Match(link.ToLowerInvariant());
|
||||
@@ -791,7 +796,7 @@ internal static partial class LogParserResult
|
||||
return null;
|
||||
}
|
||||
|
||||
private static bool VersionIsTooOld(NameValueCollection items, Match update, UpdateInfo? updateInfo)
|
||||
private static bool VersionIsTooOld(NameValueCollection items, Match update, UpdateInfo updateInfo)
|
||||
{
|
||||
if (updateInfo.GetUpdateDelta() is TimeSpan updateTimeDelta
|
||||
&& updateTimeDelta < Config.BuildTimeDifferenceForOutdatedBuildsInDays)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.IO;
|
||||
using System.Text.RegularExpressions;
|
||||
using CompatApiClient;
|
||||
using CompatApiClient.POCOs;
|
||||
using CompatApiClient.Utils;
|
||||
using CompatBot.EventHandlers;
|
||||
@@ -13,18 +14,23 @@ internal static class UpdateInfoFormatter
|
||||
|
||||
public static async Task<DiscordEmbedBuilder> AsEmbedAsync(this UpdateInfo? info, DiscordClient client, bool includePrBody = false, DiscordEmbedBuilder? builder = null, Octokit.PullRequest? currentPrInfo = null, bool useCurrent = false)
|
||||
{
|
||||
if ((info?.LatestBuild?.Windows?.Download ?? info?.LatestBuild?.Linux?.Download ?? info?.LatestBuild?.Mac?.Download) is null)
|
||||
return builder ?? new DiscordEmbedBuilder {Title = "Error", Description = "Error communicating with the update API. Try again later.", Color = Config.Colors.Maintenance};
|
||||
if ( info is not {ReturnCode: >=StatusCode.UnknownBuild})
|
||||
return builder ?? new DiscordEmbedBuilder
|
||||
{
|
||||
Title = "Error",
|
||||
Description = "Error communicating with the update API. Try again later.",
|
||||
Color = Config.Colors.Maintenance,
|
||||
};
|
||||
|
||||
var justAppend = builder != null;
|
||||
var latestBuild = info!.LatestBuild;
|
||||
var currentBuild = info.CurrentBuild;
|
||||
var justAppend = builder is not null;
|
||||
var latestBuild = info.X64?.LatestBuild ?? info.Arm?.LatestBuild;
|
||||
var currentBuild = info.X64?.CurrentBuild ?? info.Arm?.CurrentBuild;
|
||||
var latestPr = latestBuild?.Pr;
|
||||
var currentPr = currentBuild?.Pr;
|
||||
string? url = null;
|
||||
Octokit.PullRequest? latestPrInfo = null;
|
||||
|
||||
string prDesc = "";
|
||||
var prDesc = "";
|
||||
if (!justAppend)
|
||||
{
|
||||
if (latestPr > 0)
|
||||
@@ -46,7 +52,7 @@ internal static class UpdateInfoFormatter
|
||||
if (includePrBody && latestPrInfo?.Body is { Length: >0 } prInfoBody)
|
||||
desc = $"**{desc?.TrimEnd()}**\n\n{prInfoBody}";
|
||||
desc = desc?.Trim();
|
||||
if (!string.IsNullOrEmpty(desc))
|
||||
if (desc is {Length: >0})
|
||||
{
|
||||
if (GithubLinksHandler.IssueMention().Matches(desc) is { Count: >0 } issueMatches)
|
||||
{
|
||||
@@ -63,14 +69,14 @@ internal static class UpdateInfoFormatter
|
||||
{
|
||||
num = m.Groups["another_number"].Value;
|
||||
name = "#" + num;
|
||||
if (!string.IsNullOrEmpty(m.Groups["comment_id"].Value))
|
||||
if (m.Groups["comment_id"].Value is {Length: >0})
|
||||
name += " comment";
|
||||
}
|
||||
if (string.IsNullOrEmpty(num))
|
||||
if (num is not {Length: >0})
|
||||
continue;
|
||||
|
||||
var commentLink = "";
|
||||
if (!string.IsNullOrEmpty(m.Groups["comment_id"].Value))
|
||||
if (m.Groups["comment_id"].Value is {Length: >0})
|
||||
commentLink = "#issuecomment-" + m.Groups["comment_id"].Value;
|
||||
var newLink = $"[{name}](https://github.com/RPCS3/rpcs3/issues/{num}{commentLink})";
|
||||
desc = desc.Replace(str, newLink);
|
||||
@@ -85,7 +91,7 @@ internal static class UpdateInfoFormatter
|
||||
if (m.Groups["commit_mention"].Value is { Length: >0 } lnk && uniqueLinks.Add(lnk))
|
||||
{
|
||||
var num = m.Groups["commit_hash"].Value;
|
||||
if (string.IsNullOrEmpty(num))
|
||||
if (num is not {Length: >0})
|
||||
continue;
|
||||
|
||||
if (num.Length > 7)
|
||||
@@ -95,7 +101,7 @@ internal static class UpdateInfoFormatter
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!string.IsNullOrEmpty(desc) && GithubLinksHandler.ImageMarkup().Matches(desc) is {Count: >0} imgMatches)
|
||||
if (desc is {Length: >0} && GithubLinksHandler.ImageMarkup().Matches(desc) is {Count: >0} imgMatches)
|
||||
{
|
||||
var uniqueLinks = new HashSet<string>(10);
|
||||
foreach (Match m in imgMatches)
|
||||
@@ -104,9 +110,9 @@ internal static class UpdateInfoFormatter
|
||||
{
|
||||
var caption = m.Groups["img_caption"].Value;
|
||||
var link = m.Groups["img_link"].Value;
|
||||
if (!string.IsNullOrEmpty(caption))
|
||||
if (caption is {Length: >0})
|
||||
caption = " " + caption;
|
||||
desc = desc.Replace(str, $"[🖼{caption}]({link})");
|
||||
desc = desc.Replace(str, $"[🖼️{caption}]({link})");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -115,11 +121,11 @@ internal static class UpdateInfoFormatter
|
||||
var currentCommit = currentPrInfo?.MergeCommitSha;
|
||||
var latestCommit = latestPrInfo?.MergeCommitSha;
|
||||
var buildTimestampKind = "Built";
|
||||
DateTime? latestBuildTimestamp = null, currentBuildTimestamp = null;
|
||||
if (Config.GetAzureDevOpsClient() is {} azureClient)
|
||||
DateTimeOffset? latestBuildTimestamp = null, currentBuildTimestamp = null;
|
||||
//if (Config.GetAzureDevOpsClient() is {} azureClient)
|
||||
{
|
||||
var currentAppveyorBuild = await azureClient.GetMasterBuildInfoAsync(currentCommit, currentPrInfo?.MergedAt?.DateTime, Config.Cts.Token).ConfigureAwait(false);
|
||||
var latestAppveyorBuild = await azureClient.GetMasterBuildInfoAsync(latestCommit, latestPrInfo?.MergedAt?.DateTime, Config.Cts.Token).ConfigureAwait(false);
|
||||
var currentAppveyorBuild = await GithubClient.GetMasterBuildInfoAsync(currentCommit, currentPrInfo?.MergedAt?.DateTime, Config.Cts.Token).ConfigureAwait(false);
|
||||
var latestAppveyorBuild = await GithubClient.GetMasterBuildInfoAsync(latestCommit, latestPrInfo?.MergedAt?.DateTime, Config.Cts.Token).ConfigureAwait(false);
|
||||
latestBuildTimestamp = latestAppveyorBuild?.FinishTime;
|
||||
currentBuildTimestamp = currentAppveyorBuild?.FinishTime;
|
||||
if (!latestBuildTimestamp.HasValue)
|
||||
@@ -129,10 +135,11 @@ internal static class UpdateInfoFormatter
|
||||
}
|
||||
}
|
||||
|
||||
var linkedBuild = useCurrent ? currentBuild : latestBuild;
|
||||
if (!string.IsNullOrEmpty(linkedBuild?.Datetime))
|
||||
var linkedX64Build = useCurrent ? info.X64?.CurrentBuild : info.X64?.LatestBuild;
|
||||
var linkedArmBuild = useCurrent ? info.Arm?.CurrentBuild : info.Arm?.LatestBuild;
|
||||
if ((linkedX64Build ?? linkedArmBuild)?.Datetime is {Length: >0} dateTime)
|
||||
{
|
||||
var timestampInfo = (useCurrent ? currentBuildTimestamp : latestBuildTimestamp)?.ToString("u") ?? linkedBuild.Datetime;
|
||||
var timestampInfo = (useCurrent ? currentBuildTimestamp : latestBuildTimestamp)?.ToString("u") ?? dateTime;
|
||||
if (!useCurrent
|
||||
&& currentPr > 0
|
||||
&& currentPr != latestPr
|
||||
@@ -153,14 +160,17 @@ internal static class UpdateInfoFormatter
|
||||
builder.WithFooter($"{buildTimestampKind} on {timestampInfo}");
|
||||
}
|
||||
return builder
|
||||
.AddField("Windows download", GetLinkMessage(linkedBuild?.Windows, true), true)
|
||||
.AddField("Linux download", GetLinkMessage(linkedBuild?.Linux, true), true)
|
||||
.AddField("Mac download", GetLinkMessage(linkedBuild?.Mac, true), true);
|
||||
.AddField("Windows x64", GetLinkMessage(linkedX64Build?.Windows, true), true)
|
||||
.AddField("Linux x64", GetLinkMessage(linkedX64Build?.Linux, true), true)
|
||||
.AddField("Mac Intel", GetLinkMessage(linkedX64Build?.Mac, true), true)
|
||||
.AddField("Windows ARM64", "-", true)
|
||||
.AddField("Linux ARM64", GetLinkMessage(linkedArmBuild?.Linux, true), true)
|
||||
.AddField("Mac Apple Silicon", GetLinkMessage(linkedArmBuild?.Mac, true), true);
|
||||
}
|
||||
|
||||
private static string GetLinkMessage(BuildLink? link, bool simpleName)
|
||||
{
|
||||
if (link is null or { Download: null or "" } or { Size: null or 0 })
|
||||
if (link is not {Download.Length: >0, Size: >0})
|
||||
return "No link available";
|
||||
|
||||
var text = new Uri(link.Download).Segments.Last();
|
||||
@@ -203,19 +213,16 @@ internal static class UpdateInfoFormatter
|
||||
#endif
|
||||
});
|
||||
|
||||
public static TimeSpan? GetUpdateDelta(DateTime? latest, DateTime? current)
|
||||
public static TimeSpan? GetUpdateDelta(DateTimeOffset? latest, DateTimeOffset? current)
|
||||
{
|
||||
if (latest.HasValue && current.HasValue)
|
||||
return latest - current;
|
||||
return null;
|
||||
}
|
||||
|
||||
public static TimeSpan? GetUpdateDelta(this UpdateInfo? updateInfo)
|
||||
public static TimeSpan? GetUpdateDelta(this UpdateInfo updateInfo)
|
||||
{
|
||||
if (updateInfo?.LatestBuild?.Datetime is string latestDateTimeStr
|
||||
&& DateTime.TryParse(latestDateTimeStr, out var latestDateTime)
|
||||
&& updateInfo.CurrentBuild?.Datetime is string dateTimeBuildStr
|
||||
&& DateTime.TryParse(dateTimeBuildStr, out var dateTimeBuild))
|
||||
if (updateInfo is { LatestDatetime: DateTime latestDateTime, CurrentDatetime: DateTime dateTimeBuild })
|
||||
return latestDateTime - dateTimeBuild;
|
||||
return null;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user