mirror of
https://github.com/RPCS3/discord-bot.git
synced 2024-11-27 12:10:22 +00:00
Merge pull request #158 from 13xforever/vnext
Improvements to !pr command
This commit is contained in:
commit
477c9dc489
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -1,6 +1,4 @@
|
||||
using System.Text;
|
||||
|
||||
namespace AppveyorClient.POCOs
|
||||
namespace AppveyorClient.POCOs
|
||||
{
|
||||
public class BuildInfo
|
||||
{
|
||||
|
10
AppveyorClient/POCOs/HistoryInfo.cs
Normal file
10
AppveyorClient/POCOs/HistoryInfo.cs
Normal file
@ -0,0 +1,10 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace AppveyorClient.POCOs
|
||||
{
|
||||
public class HistoryInfo
|
||||
{
|
||||
public List<Build> Builds;
|
||||
public Project Project;
|
||||
}
|
||||
}
|
@ -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
|
||||
{
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
{
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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}");
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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))
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user