Merge pull request #158 from 13xforever/vnext

Improvements to !pr command
This commit is contained in:
Ilya 2019-01-08 19:48:33 +05:00 committed by GitHub
commit 477c9dc489
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 261 additions and 125 deletions

View File

@ -41,10 +41,14 @@ namespace AppveyorClient
{
try
{
if (!int.TryParse(new Uri(githubStatusTargetUrl).Segments.Last(), out var buildNumber))
var buildUrl = githubStatusTargetUrl.Replace("ci.appveyor.com/project/", "ci.appveyor.com/api/projects/");
if (buildUrl == githubStatusTargetUrl)
{
ApiConfig.Log.Warn("Unexpected AppVeyor link: " + githubStatusTargetUrl);
return null;
}
var buildInfo = await GetBuildInfoAsync(buildNumber, cancellationToken).ConfigureAwait(false);
var buildInfo = await GetBuildInfoAsync(buildUrl, cancellationToken).ConfigureAwait(false);
var job = buildInfo?.Build.Jobs?.FirstOrDefault(j => j.Status == "success");
if (string.IsNullOrEmpty(job?.JobId))
return null;
@ -66,16 +70,79 @@ namespace AppveyorClient
{
ApiConfig.Log.Error(e);
}
ResponseCache.TryGetValue(githubStatusTargetUrl, out var o);
return o as ArtifactInfo;
ResponseCache.TryGetValue(githubStatusTargetUrl, out ArtifactInfo o);
return o;
}
public async Task<BuildInfo> GetBuildInfoAsync(int buildNumber, CancellationToken cancellationToken)
public async Task<ArtifactInfo> GetPrDownloadAsync(int prNumber, DateTime dateTimeLimit, CancellationToken cancellationToken)
{
var requestUri = "https://ci.appveyor.com/api/projects/rpcs3/rpcs3/builds/" + buildNumber;
if (ResponseCache.TryGetValue(prNumber, out ArtifactInfo result))
return result;
try
{
using (var message = new HttpRequestMessage(HttpMethod.Get, requestUri))
var baseUrl = new Uri("https://ci.appveyor.com/api/projects/rpcs3/RPCS3/history?recordsNumber=100");
var historyUrl = baseUrl;
HistoryInfo historyPage = null;
Build build = null;
do
{
using (var message = new HttpRequestMessage(HttpMethod.Get, historyUrl))
{
message.Headers.UserAgent.Add(ProductInfoHeader);
using (var response = await client.SendAsync(message, HttpCompletionOption.ResponseContentRead, cancellationToken).ConfigureAwait(false))
{
try
{
await response.Content.LoadIntoBufferAsync().ConfigureAwait(false);
historyPage = await response.Content.ReadAsAsync<HistoryInfo>(formatters, cancellationToken).ConfigureAwait(false);
build = historyPage.Builds.FirstOrDefault(b => b.PullRequestId == prNumber && b.Status == "success");
}
catch (Exception e)
{
ConsoleLogger.PrintError(e, response);
break;
}
}
}
historyUrl = baseUrl.SetQueryParameter("startBuildId", historyPage.Builds.Last().BuildId.ToString());
} while (build == null && historyPage.Builds?.Count > 0 && historyPage.Builds.Last(b => b.Started.HasValue).Started > dateTimeLimit);
var buildInfo = await GetBuildInfoAsync(build.BuildId, cancellationToken).ConfigureAwait(false);
var job = buildInfo?.Build.Jobs?.FirstOrDefault(j => j.Status == "success");
if (string.IsNullOrEmpty(job?.JobId))
return null;
var artifacts = await GetJobArtifactsAsync(job.JobId, cancellationToken).ConfigureAwait(false);
var rpcs3Build = artifacts?.FirstOrDefault(a => a.Name == "rpcs3");
if (rpcs3Build == null)
return null;
result = new ArtifactInfo
{
Artifact = rpcs3Build,
DownloadUrl = $"https://ci.appveyor.com/api/buildjobs/{job.JobId}/artifacts/{rpcs3Build.FileName}",
};
ResponseCache.Set(prNumber, result, CacheTime);
return result;
}
catch (Exception e)
{
ApiConfig.Log.Error(e);
}
return null;
}
private Task<BuildInfo> GetBuildInfoAsync(int buildId, CancellationToken cancellationToken)
{
return GetBuildInfoAsync("https://ci.appveyor.com/api/projects/rpcs3/rpcs3/builds/" + buildId, cancellationToken);
}
private async Task<BuildInfo> GetBuildInfoAsync(string buildUrl, CancellationToken cancellationToken)
{
try
{
using (var message = new HttpRequestMessage(HttpMethod.Get, buildUrl))
{
message.Headers.UserAgent.Add(ProductInfoHeader);
using (var response = await client.SendAsync(message, HttpCompletionOption.ResponseContentRead, cancellationToken).ConfigureAwait(false))
@ -84,7 +151,7 @@ namespace AppveyorClient
{
await response.Content.LoadIntoBufferAsync().ConfigureAwait(false);
var result = await response.Content.ReadAsAsync<BuildInfo>(formatters, cancellationToken).ConfigureAwait(false);
ResponseCache.Set(requestUri, result, CacheTime);
ResponseCache.Set(buildUrl, result, CacheTime);
return result;
}
catch (Exception e)
@ -98,8 +165,8 @@ namespace AppveyorClient
{
ApiConfig.Log.Error(e);
}
ResponseCache.TryGetValue(requestUri, out var o);
return o as BuildInfo;
ResponseCache.TryGetValue(buildUrl, out BuildInfo o);
return o;
}
public async Task<List<Artifact>> GetJobArtifactsAsync(string jobId, CancellationToken cancellationToken)
@ -130,8 +197,9 @@ namespace AppveyorClient
{
ApiConfig.Log.Error(e);
}
ResponseCache.TryGetValue(requestUri, out var o);
return o as List<Artifact>;
ResponseCache.TryGetValue(requestUri, out List<Artifact> o);
return o;
}
}
}

View File

@ -19,7 +19,7 @@ namespace AppveyorClient.POCOs
public string PullRequestHeadBranch;
public string PullRequestHeadCommitId;
public string PullRequestHeadRepository;
public string PullRequestId;
public int PullRequestId;
public string PullRequestName;
public string Status;
public string Version;

View File

@ -1,6 +1,4 @@
using System.Text;
namespace AppveyorClient.POCOs
namespace AppveyorClient.POCOs
{
public class BuildInfo
{

View File

@ -0,0 +1,10 @@
using System.Collections.Generic;
namespace AppveyorClient.POCOs
{
public class HistoryInfo
{
public List<Build> Builds;
public Project Project;
}
}

View File

@ -11,7 +11,7 @@ using Microsoft.EntityFrameworkCore;
namespace CompatBot.Commands
{
[Group("piracy"), RequiresBotModRole, RequiresDm, TriggersTyping]
[Group("piracy"), RequiresBotModRole, RequiresDm]
[Description("Used to manage piracy filters **in DM**")]
internal sealed class Antipiracy: BaseCommandModuleCustom
{

View File

@ -1,23 +1,16 @@
using System;
using System.Threading.Tasks;
using DSharpPlus.CommandsNext;
using DSharpPlus.CommandsNext.Attributes;
namespace CompatBot.Commands.Attributes
{
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)]
internal class TriggersTyping: CheckBaseAttribute
internal class TriggersTyping: Attribute
{
public bool InDmOnly { get; set; }
public override async Task<bool> ExecuteCheckAsync(CommandContext ctx, bool help)
public bool ExecuteCheck(CommandContext ctx)
{
if (help)
return true;
if (!InDmOnly || ctx.Channel.IsPrivate)
await ctx.TriggerTypingAsync().ConfigureAwait(false);
return true;
return !InDmOnly || ctx.Channel.IsPrivate;
}
}
}

View File

@ -8,7 +8,7 @@ using org.mariuszgromada.math.mxparser;
namespace CompatBot.Commands
{
[Group("math"), TriggersTyping]
[Group("math")]
[Description("Math, here you go Juhn. Use `math help` for syntax help")]
internal sealed class BotMath : BaseCommandModuleCustom
{

View File

@ -14,7 +14,7 @@ namespace CompatBot.Commands
[Description("Used to enabe and disable bot commands at runtime")]
public sealed class CommandsManagement : BaseCommandModule
{
[Command("list"), Aliases("show"), TriggersTyping]
[Command("list"), Aliases("show")]
[Description("Lists the disabled commands")]
public async Task List(CommandContext ctx)
{

View File

@ -107,7 +107,7 @@ Example usage:
await DoRequestAndRespond(ctx, requestBuilder).ConfigureAwait(false);
}
[Command("filters"), TriggersTyping(InDmOnly = true)]
[Command("filters")]
[Description("Provides information about available filters for the !top command")]
public async Task Filters(CommandContext ctx)
{

View File

@ -1,6 +1,8 @@
using System.Threading.Tasks;
using System.Linq;
using System.Threading.Tasks;
using CompatBot.Commands.Attributes;
using CompatBot.Database.Providers;
using CompatBot.Utils;
using DSharpPlus.CommandsNext;
using DSharpPlus.CommandsNext.Attributes;
using DSharpPlus.Entities;
@ -18,7 +20,23 @@ namespace CompatBot.Commands
throw new DSharpPlus.CommandsNext.Exceptions.ChecksFailedException(ctx.Command, ctx, new CheckBaseAttribute[] {new RequiresDm()});
}
if (TriggersTyping(ctx))
await ctx.ReactWithAsync(Config.Reactions.PleaseWait).ConfigureAwait(false);
await base.BeforeExecutionAsync(ctx).ConfigureAwait(false);
}
public override async Task AfterExecutionAsync(CommandContext ctx)
{
if (TriggersTyping(ctx))
await ctx.RemoveReactionAsync(Config.Reactions.PleaseWait).ConfigureAwait(false);
await base.AfterExecutionAsync(ctx).ConfigureAwait(false);
}
private static bool TriggersTyping(CommandContext ctx)
{
return ctx.Command.CustomAttributes.OfType<TriggersTyping>().FirstOrDefault() is TriggersTyping a && a.ExecuteCheck(ctx);
}
}
}

View File

@ -24,7 +24,6 @@ namespace CompatBot.Commands
[GroupCommand]
public async Task ShowExplanation(CommandContext ctx, [RemainingText, Description("Term to explain")] string term)
{
await ctx.TriggerTypingAsync().ConfigureAwait(false);
string inSpecificLocation = null;
if (!LimitedToSpamChannel.IsSpamChannel(ctx.Channel))
{
@ -171,7 +170,6 @@ namespace CompatBot.Commands
[Description("List all known terms that could be used for !explain command")]
public async Task List(CommandContext ctx)
{
await ctx.TriggerTypingAsync().ConfigureAwait(false);
using (var db = new BotDb())
{
var keywords = await db.Explanation.Select(e => e.Keyword).OrderBy(t => t).ToListAsync().ConfigureAwait(false);

View File

@ -14,7 +14,7 @@ using Microsoft.EntityFrameworkCore;
namespace CompatBot.Commands
{
[Group("invite"), Aliases("invites"), RequiresBotModRole, TriggersTyping]
[Group("invite"), Aliases("invites"), RequiresBotModRole]
[Description("Used to manage Discord invites whitelist")]
internal sealed class Invites: BaseCommandModuleCustom
{

View File

@ -114,7 +114,6 @@ namespace CompatBot.Commands
var embed = new DiscordEmbedBuilder();
if (dices is string dice && Regex.Matches(dice, @"(?<num>\d+)?d(?<face>\d+)(?:\+(?<mod>\d+))?") is MatchCollection matches && matches.Count > 0 && matches.Count <= EmbedPager.MaxFields)
{
await ctx.TriggerTypingAsync().ConfigureAwait(false);
var grandTotal = 0;
foreach (Match m in matches)
{

View File

@ -44,7 +44,7 @@ namespace CompatBot.Commands
try
{
await ctx.TriggerTypingAsync().ConfigureAwait(false);
await ctx.ReactWithAsync(Config.Reactions.PleaseWait).ConfigureAwait(false);
var members = GetMembers(ctx.Client);
using (var compressedResult = new MemoryStream())
{
@ -84,6 +84,7 @@ namespace CompatBot.Commands
finally
{
CheckLock.Release(1);
await ctx.RemoveReactionAsync(Config.Reactions.PleaseWait).ConfigureAwait(false);
}
}
@ -108,7 +109,7 @@ namespace CompatBot.Commands
try
{
await ctx.TriggerTypingAsync().ConfigureAwait(false);
await ctx.ReactWithAsync(Config.Reactions.PleaseWait).ConfigureAwait(false);
var members = GetMembers(ctx.Client);
if (members.Count < 2)
return;
@ -169,6 +170,7 @@ namespace CompatBot.Commands
finally
{
CheckLock.Release(1);
await ctx.RemoveReactionAsync(Config.Reactions.PleaseWait).ConfigureAwait(false);
}
}
}

View File

@ -2,7 +2,9 @@
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using AppveyorClient.POCOs;
using CompatApiClient.Utils;
using CompatBot.Commands.Attributes;
using CompatBot.Utils;
using CompatBot.Utils.ResultFormatters;
using DSharpPlus.CommandsNext;
@ -11,12 +13,13 @@ using GithubClient.POCOs;
namespace CompatBot.Commands
{
[Group("pr")]
[Group("pr"), TriggersTyping]
[Description("Commands to list opened pull requests information")]
internal sealed class Pr: BaseCommandModuleCustom
{
private static readonly GithubClient.Client githubClient = new GithubClient.Client();
private static readonly AppveyorClient.Client appveyorClient = new AppveyorClient.Client();
private static readonly CompatApiClient.Client compatApiClient = new CompatApiClient.Client();
private const string appveyorContext = "continuous-integration/appveyor/pr";
[GroupCommand]
@ -29,15 +32,16 @@ namespace CompatBot.Commands
return;
}
var prState = prInfo.GetState();
var embed = prInfo.AsEmbed();
if (prInfo.State == "open")
if (prState.state == "Open" || prState.state == "Closed")
{
var downloadHeader = "PR Build Download";
var downloadText = "";
if (prInfo.StatusesUrl is string statusesUrl)
{
var statuses = await githubClient.GetStatusesAsync(statusesUrl, Config.Cts.Token).ConfigureAwait(false);
statuses = statuses?.Where(s => s.Context == appveyorContext).ToList();
var downloadHeader = "PR Build Download";
var downloadText = "";
if (statuses?.Count > 0)
{
if (statuses.FirstOrDefault(s => s.State == "success") is StatusInfo statusSuccess)
@ -52,12 +56,29 @@ namespace CompatBot.Commands
downloadText = $"[⏬ {artifactInfo.Artifact.FileName}]({artifactInfo.DownloadUrl})";
}
}
else if (await appveyorClient.GetPrDownloadAsync(prInfo.Number, prInfo.CreatedAt, Config.Cts.Token).ConfigureAwait(false) is ArtifactInfo artifactInfo)
{
if (artifactInfo.Artifact.Created is DateTime buildTime)
downloadHeader = $"{downloadHeader} ({buildTime:u})";
downloadText = $"[⏬ {artifactInfo.Artifact.FileName}]({artifactInfo.DownloadUrl})";
}
else
downloadText = statuses.First().Description;
}
if (!string.IsNullOrEmpty(downloadText))
embed.AddField(downloadHeader, downloadText);
}
else if (await appveyorClient.GetPrDownloadAsync(prInfo.Number, prInfo.CreatedAt, Config.Cts.Token).ConfigureAwait(false) is ArtifactInfo artifactInfo)
{
if (artifactInfo.Artifact.Created is DateTime buildTime)
downloadHeader = $"{downloadHeader} ({buildTime:u})";
downloadText = $"[⏬ {artifactInfo.Artifact.FileName}]({artifactInfo.DownloadUrl})";
}
if (!string.IsNullOrEmpty(downloadText))
embed.AddField(downloadHeader, downloadText);
}
else if (prState.state == "Merged")
{
var updateInfo = await compatApiClient.GetUpdateAsync(Config.Cts.Token).ConfigureAwait(false);
embed = await updateInfo.AsEmbedAsync(embed).ConfigureAwait(false);
}
await ctx.RespondAsync(embed: embed).ConfigureAwait(false);
}

View File

@ -18,7 +18,7 @@ namespace CompatBot.Commands
[Description("Commands to manage the bot instance")]
public sealed partial class Bot: BaseCommandModuleCustom
{
[Command("version"), TriggersTyping]
[Command("version")]
[Description("Returns currently checked out bot commit")]
public async Task Version(CommandContext ctx)
{
@ -41,7 +41,7 @@ namespace CompatBot.Commands
}
}
[Command("update"), Aliases("upgrade", "restart", "reboot", "pull"), TriggersTyping]
[Command("update"), Aliases("upgrade", "restart", "reboot", "pull")]
[Description("Restarts bot and pulls the newest commit")]
public async Task Update(CommandContext ctx)
{

View File

@ -18,11 +18,11 @@ namespace CompatBot.Commands
private static readonly Regex Timestamp = new Regex(@"^(?<cutout>(?<date>\d{4}-\d\d-\d\d[ T][0-9:\.]+Z?) - )", RegexOptions.ExplicitCapture | RegexOptions.Singleline);
private static readonly Regex Channel = new Regex(@"(?<id><#\d+>)", RegexOptions.ExplicitCapture | RegexOptions.Singleline);
[Group("fix"), Hidden]
[Group("fix"), Hidden, TriggersTyping]
[Description("Commands to fix various stuff")]
public sealed class Fix: BaseCommandModuleCustom
{
[Command("timestamps"), TriggersTyping]
[Command("timestamps")]
[Description("Fixes `timestamp` column in the `warning` table")]
public async Task Timestamps(CommandContext ctx)
{
@ -53,7 +53,7 @@ namespace CompatBot.Commands
}
}
[Command("channels"), TriggersTyping]
[Command("channels")]
[Description("Fixes channel mentions in `warning` table")]
public async Task Channels(CommandContext ctx)
{

View File

@ -15,7 +15,7 @@ namespace CompatBot.Commands
[Description("Used to manage bot moderators")]
public sealed class Mod : BaseCommandModuleCustom
{
[Command("add"), TriggersTyping]
[Command("add")]
[Description("Adds a new moderator")]
public async Task Add(CommandContext ctx, [Description("Discord user to add to the bot mod list")] DiscordMember user)
{
@ -30,7 +30,7 @@ namespace CompatBot.Commands
await ctx.ReactWithAsync(Config.Reactions.Failure, $"{user.Mention} is already a moderator").ConfigureAwait(false);
}
[Command("remove"), Aliases("delete", "del"), TriggersTyping]
[Command("remove"), Aliases("delete", "del")]
[Description("Removes a moderator")]
public async Task Remove(CommandContext ctx, [Description("Discord user to remove from the bot mod list")] DiscordMember user)
{
@ -46,7 +46,7 @@ namespace CompatBot.Commands
await ctx.ReactWithAsync(Config.Reactions.Failure, $"{user.Mention} is not a moderator").ConfigureAwait(false);
}
[Command("list"), Aliases("show"), TriggersTyping]
[Command("list"), Aliases("show")]
[Description("Lists all moderators")]
public async Task List(CommandContext ctx)
{
@ -56,7 +56,7 @@ namespace CompatBot.Commands
await ctx.SendAutosplitMessageAsync(list.Append("```")).ConfigureAwait(false);
}
[Command("sudo"), TriggersTyping]
[Command("sudo")]
[Description("Makes a moderator a sudoer")]
public async Task Sudo(CommandContext ctx, [Description("Discord user on the moderator list to grant the sudoer rights to")] DiscordMember moderator)
{
@ -71,7 +71,7 @@ namespace CompatBot.Commands
await ctx.ReactWithAsync(Config.Reactions.Failure, $"{moderator.Mention} is not a moderator (yet)").ConfigureAwait(false);
}
[Command("unsudo"), TriggersTyping]
[Command("unsudo")]
[Description("Makes a sudoer a regular moderator")]
public async Task Unsudo(CommandContext ctx, [Description("Discord user on the moderator list to strip the sudoer rights from")] DiscordMember sudoer)
{

View File

@ -15,7 +15,7 @@ namespace CompatBot.Commands
{
internal sealed partial class Warnings
{
[Group("list"), Aliases("show"), TriggersTyping]
[Group("list"), Aliases("show")]
[Description("Allows to list warnings in various ways. Users can only see their own warnings.")]
public class ListGroup : BaseCommandModuleCustom
{
@ -72,7 +72,7 @@ namespace CompatBot.Commands
await ctx.SendAutosplitMessageAsync(result.Append("```")).ConfigureAwait(false);
}
[Command("recent"), Aliases("last", "all"), RequiresBotModRole, TriggersTyping]
[Command("recent"), Aliases("last", "all"), RequiresBotModRole]
[Description("Shows last issued warnings in chronological order")]
public async Task Last(CommandContext ctx, [Description("Optional number of items to show. Default is 10")] int number = 10)
{

View File

@ -26,7 +26,6 @@ namespace CompatBot.Commands
if (!await new RequiresBotModRole().ExecuteCheckAsync(ctx, false).ConfigureAwait(false))
return;
await ctx.TriggerTypingAsync().ConfigureAwait(false);
if (await AddAsync(ctx, user.Id, user.Username.Sanitize(), ctx.Message.Author, reason).ConfigureAwait(false))
await ctx.ReactWithAsync(Config.Reactions.Success).ConfigureAwait(false);
else
@ -39,14 +38,13 @@ namespace CompatBot.Commands
if (!await new RequiresBotModRole().ExecuteCheckAsync(ctx, false).ConfigureAwait(false))
return;
await ctx.TriggerTypingAsync().ConfigureAwait(false);
if (await AddAsync(ctx, userId, $"<@{userId}>", ctx.Message.Author, reason).ConfigureAwait(false))
await ctx.ReactWithAsync(Config.Reactions.Success).ConfigureAwait(false);
else
await ctx.ReactWithAsync(Config.Reactions.Failure, "Couldn't save the warning, please try again").ConfigureAwait(false);
}
[Command("remove"), Aliases("delete", "del"), RequiresBotModRole, TriggersTyping]
[Command("remove"), Aliases("delete", "del"), RequiresBotModRole]
[Description("Removes specified warnings")]
public async Task Remove(CommandContext ctx, [Description("Warning IDs to remove separated with space")] params int[] ids)
{
@ -70,7 +68,7 @@ namespace CompatBot.Commands
return Clear(ctx, user.Id);
}
[Command("clear"), RequiresBotModRole, TriggersTyping]
[Command("clear"), RequiresBotModRole]
public async Task Clear(CommandContext ctx, [Description("User ID to clear warnings for")] ulong userId)
{
try
@ -138,7 +136,6 @@ namespace CompatBot.Commands
private static async Task ListUserWarningsAsync(DiscordClient client, DiscordMessage message, ulong userId, string userName, bool skipIfOne = true)
{
var channel = message.Channel;
await channel.TriggerTypingAsync().ConfigureAwait(false);
int count;
using (var db = new BotDb())
count = await db.Warning.CountAsync(w => w.DiscordId == userId).ConfigureAwait(false);

View File

@ -73,6 +73,7 @@ namespace CompatBot
public static readonly DiscordEmoji Starbucks = DiscordEmoji.FromUnicode("☕");
public static readonly DiscordEmoji Moderated = DiscordEmoji.FromUnicode("🔨");
public static readonly DiscordEmoji No = DiscordEmoji.FromUnicode("😐");
public static readonly DiscordEmoji PleaseWait = DiscordEmoji.FromUnicode("👀");
}
public static class Moderation

View File

@ -69,7 +69,7 @@ namespace CompatBot.EventHandlers
foreach (var handler in handlers)
if (await handler.CanHandleAsync(attachment).ConfigureAwait(false))
{
await args.Channel.TriggerTypingAsync().ConfigureAwait(false);
await args.Message.ReactWithAsync(args.Client, Config.Reactions.PleaseWait).ConfigureAwait(false);
Config.Log.Debug($">>>>>>> {message.Id % 100} Parsing log from attachment {attachment.FileName} ({attachment.FileSize})...");
parsedLog = true;
LogParseState result = null;
@ -157,6 +157,7 @@ namespace CompatBot.EventHandlers
finally
{
QueueLimiter.Release();
await args.Message.RemoveReactionAsync(Config.Reactions.PleaseWait).ConfigureAwait(false);
if (parsedLog)
Config.Log.Debug($"<<<<<<< {message.Id % 100} Finished parsing in {startTime.Elapsed}");
}

View File

@ -57,46 +57,53 @@ namespace CompatBot.EventHandlers
if (codesToLookup.Count == 0)
return;
await args.Channel.TriggerTypingAsync().ConfigureAwait(false);
var results = new List<(string code, Task<DiscordEmbedBuilder> task)>(codesToLookup.Count);
foreach (var code in codesToLookup)
results.Add((code, args.Client.LookupGameInfoAsync(code)));
var formattedResults = new List<DiscordEmbedBuilder>(results.Count);
foreach (var result in results)
try
{
formattedResults.Add(await result.task.ConfigureAwait(false));
}
catch (Exception e)
{
Config.Log.Warn(e, $"Couldn't get product code info for {result.code}");
}
// get only results with unique titles
formattedResults = formattedResults.GroupBy(e => e.Title).Select(g => g.First()).ToList();
DiscordEmoji sqvat = null;
foreach (var result in formattedResults)
try
{
if (!args.Channel.IsPrivate
&& args.Message.Author.Id == 197163728867688448
&& (
result.Title.Contains("africa", StringComparison.InvariantCultureIgnoreCase) ||
result.Title.Contains("afrika", StringComparison.InvariantCultureIgnoreCase)
))
await args.Message.ReactWithAsync(args.Client, Config.Reactions.PleaseWait).ConfigureAwait(false);
try
{
var results = new List<(string code, Task<DiscordEmbedBuilder> task)>(codesToLookup.Count);
foreach (var code in codesToLookup)
results.Add((code, args.Client.LookupGameInfoAsync(code)));
var formattedResults = new List<DiscordEmbedBuilder>(results.Count);
foreach (var result in results)
try
{
sqvat = sqvat ?? args.Client.GetEmoji(":sqvat:", Config.Reactions.No);
result.Title = "How about no (๑•ิཬ•ั๑)";
if (!string.IsNullOrEmpty(result.ThumbnailUrl))
result.ThumbnailUrl = "https://cdn.discordapp.com/attachments/417347469521715210/516340151589535745/onionoff.png";
await args.Message.ReactWithAsync(args.Client, sqvat).ConfigureAwait(false);
formattedResults.Add(await result.task.ConfigureAwait(false));
}
await args.Channel.SendMessageAsync(embed: result).ConfigureAwait(false);
}
catch (Exception e)
{
Config.Log.Warn(e, $"Couldn't post result for {result.Title}");
}
catch (Exception e)
{
Config.Log.Warn(e, $"Couldn't get product code info for {result.code}");
}
// get only results with unique titles
formattedResults = formattedResults.GroupBy(e => e.Title).Select(g => g.First()).ToList();
DiscordEmoji sqvat = null;
foreach (var result in formattedResults)
try
{
if (!args.Channel.IsPrivate
&& args.Message.Author.Id == 197163728867688448
&& (
result.Title.Contains("africa", StringComparison.InvariantCultureIgnoreCase) ||
result.Title.Contains("afrika", StringComparison.InvariantCultureIgnoreCase)
))
{
sqvat = sqvat ?? args.Client.GetEmoji(":sqvat:", Config.Reactions.No);
result.Title = "How about no (๑•ิཬ•ั๑)";
if (!string.IsNullOrEmpty(result.ThumbnailUrl))
result.ThumbnailUrl = "https://cdn.discordapp.com/attachments/417347469521715210/516340151589535745/onionoff.png";
await args.Message.ReactWithAsync(args.Client, sqvat).ConfigureAwait(false);
}
await args.Channel.SendMessageAsync(embed: result).ConfigureAwait(false);
}
catch (Exception e)
{
Config.Log.Warn(e, $"Couldn't post result for {result.Title}");
}
}
finally
{
await args.Message.RemoveReactionAsync(Config.Reactions.PleaseWait).ConfigureAwait(false);
}
}
public static List<string> GetProductIds(string input)

View File

@ -101,9 +101,9 @@ namespace CompatBot.EventHandlers
try
{
var displayName = GetCanonical(potentialVictims[0].DisplayName);
if (SpoofingReportThrottlingCache.TryGetValue(displayName, out var x) && x is string y && !string.IsNullOrEmpty(y))
if (SpoofingReportThrottlingCache.TryGetValue(displayName, out string s) && !string.IsNullOrEmpty(s))
{
SpoofingReportThrottlingCache.Set(displayName, y, SnoozeDuration);
SpoofingReportThrottlingCache.Set(displayName, s, SnoozeDuration);
return true;
}

View File

@ -48,6 +48,18 @@ namespace CompatBot.Utils
}
}
public static async Task RemoveReactionAsync(this DiscordMessage message, DiscordEmoji emoji)
{
try
{
await message.DeleteOwnReactionAsync(emoji).ConfigureAwait(false);
}
catch (Exception e)
{
Config.Log.Warn(e);
}
}
public static async Task ReactWithAsync(this DiscordMessage message, DiscordClient client, DiscordEmoji emoji, string fallbackMessage = null, bool showBoth = false)
{
try
@ -64,6 +76,11 @@ namespace CompatBot.Utils
}
}
public static Task RemoveReactionAsync(this CommandContext ctx, DiscordEmoji emoji)
{
return RemoveReactionAsync(ctx.Message, emoji);
}
public static Task ReactWithAsync(this CommandContext ctx, DiscordEmoji emoji, string fallbackMessage = null, bool showBoth = false)
{
return ReactWithAsync(ctx.Message, ctx.Client, emoji, fallbackMessage, showBoth);

View File

@ -1,5 +1,4 @@
using System.Reflection.Metadata;
using DSharpPlus.Entities;
using DSharpPlus.Entities;
using GithubClient.POCOs;
namespace CompatBot.Utils.ResultFormatters
@ -8,17 +7,26 @@ namespace CompatBot.Utils.ResultFormatters
{
public static DiscordEmbedBuilder AsEmbed(this PrInfo prInfo)
{
(string, DiscordColor) state;
if (prInfo.State == "open")
state = ("Open", Config.Colors.PrOpen);
else if (prInfo.State == "closed")
state = prInfo.MergedAt.HasValue ? ("Merged", Config.Colors.PrMerged) : ("Closed", Config.Colors.PrClosed);
else
state = (null, Config.Colors.DownloadLinks);
(string, DiscordColor) state = prInfo.GetState();
var stateLabel = state.Item1 == null ? null : $"[{state.Item1}] ";
var pr = $"{stateLabel}PR #{prInfo.Number} by {prInfo.User?.Login ?? "???"}";
return new DiscordEmbedBuilder {Title = pr, Url = prInfo.HtmlUrl, Description = prInfo.Title, Color = state.Item2};
}
public static (string state, DiscordColor color) GetState(this PrInfo prInfo)
{
if (prInfo.State == "open")
return ("Open", Config.Colors.PrOpen);
if (prInfo.State == "closed")
{
if (prInfo.MergedAt.HasValue)
return ("Merged", Config.Colors.PrMerged);
return ("Closed", Config.Colors.PrClosed);
}
return (null, Config.Colors.DownloadLinks);
}
}
}

View File

@ -36,18 +36,18 @@ namespace CompatBot.Utils.ResultFormatters
}
}
builder = builder ?? new DiscordEmbedBuilder {Title = pr, Url = url, Description = prInfo?.Title, Color = Config.Colors.DownloadLinks};
if (!justAppend)
if (!string.IsNullOrEmpty(build?.Datetime))
{
if (!string.IsNullOrEmpty(build?.Datetime))
{
var timestampInfo = build.Datetime;
if (info.CurrentBuild?.Pr is string buildPr
&& buildPr != info.LatestBuild?.Pr
&& GetUpdateDelta(info) is TimeSpan timeDelta)
timestampInfo += $" ({timeDelta.GetTimeDeltaDescription()} newer)";
var timestampInfo = build.Datetime;
if (info.CurrentBuild?.Pr is string buildPr
&& buildPr != info.LatestBuild?.Pr
&& GetUpdateDelta(info) is TimeSpan timeDelta)
timestampInfo += $" ({timeDelta.GetTimeDeltaDescription()} newer)";
if (justAppend)
builder.AddField($"Latest master build ({timestampInfo})", "This pull request has been merged, and is a part of `master` now");
else
builder.AddField("Build timestamp", timestampInfo);
}
}
return builder
.AddField($"Windows ".FixSpaces(), GetLinkMessage(build?.Windows?.Download, true), true)

View File

@ -83,10 +83,9 @@ namespace GithubClient
public async Task<List<PrInfo>> GetOpenPrsAsync(CancellationToken cancellationToken)
{
var requestUri = "https://api.github.com/repos/RPCS3/rpcs3/pulls?state=open";
if (StatusesCache.TryGetValue(requestUri, out var o) && o is List<PrInfo> result)
if (StatusesCache.TryGetValue(requestUri, out List<PrInfo> result))
return result;
result = null;
try
{
using (var message = new HttpRequestMessage(HttpMethod.Get, requestUri))
@ -117,10 +116,9 @@ namespace GithubClient
public async Task<List<StatusInfo>> GetStatusesAsync(string statusesUrl, CancellationToken cancellationToken)
{
if (StatusesCache.TryGetValue(statusesUrl, out var o) && o is List<StatusInfo> result)
if (StatusesCache.TryGetValue(statusesUrl, out List<StatusInfo> result))
return result;
result = null;
try
{
using (var message = new HttpRequestMessage(HttpMethod.Get, statusesUrl))

View File

@ -9,7 +9,7 @@ namespace GithubClient.POCOs
public string State;
public string Title;
public GithubUser User;
public DateTime? CreatedAt;
public DateTime CreatedAt;
public DateTime? UpdatedAt;
public DateTime? ClosedAt;
public DateTime? MergedAt;