run dotnet format
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Text.RegularExpressions;
|
||||
@@ -8,11 +8,13 @@ using PluralKit.Core;
|
||||
|
||||
using SixLabors.ImageSharp;
|
||||
|
||||
namespace PluralKit.Bot {
|
||||
public static class AvatarUtils {
|
||||
namespace PluralKit.Bot
|
||||
{
|
||||
public static class AvatarUtils
|
||||
{
|
||||
public static async Task VerifyAvatarOrThrow(HttpClient client, string url, bool isFullSizeImage = false)
|
||||
{
|
||||
if (url.Length > Limits.MaxUriLength)
|
||||
if (url.Length > Limits.MaxUriLength)
|
||||
throw Errors.UrlTooLong(url);
|
||||
|
||||
// List of MIME types we consider acceptable
|
||||
|
@@ -17,8 +17,10 @@ using NodaTime;
|
||||
using PluralKit.Bot.Interactive;
|
||||
using PluralKit.Core;
|
||||
|
||||
namespace PluralKit.Bot {
|
||||
public static class ContextUtils {
|
||||
namespace PluralKit.Bot
|
||||
{
|
||||
public static class ContextUtils
|
||||
{
|
||||
public static async Task<bool> ConfirmClear(this Context ctx, string toClear)
|
||||
{
|
||||
if (!(await ctx.PromptYesNo($"{Emojis.Warn} Are you sure you want to clear {toClear}?", "Clear"))) throw Errors.GenericCancelled();
|
||||
@@ -50,7 +52,7 @@ namespace PluralKit.Bot {
|
||||
if (predicate != null && !predicate.Invoke(evt)) return false; // Check predicate
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
return await ctx.Services.Resolve<HandlerQueue<MessageReactionAddEvent>>().WaitFor(ReactionPredicate, timeout);
|
||||
}
|
||||
|
||||
@@ -58,20 +60,21 @@ namespace PluralKit.Bot {
|
||||
{
|
||||
bool Predicate(MessageCreateEvent e) =>
|
||||
e.Author.Id == ctx.Author.Id && e.ChannelId == ctx.Channel.Id;
|
||||
|
||||
|
||||
var msg = await ctx.Services.Resolve<HandlerQueue<MessageCreateEvent>>()
|
||||
.WaitFor(Predicate, Duration.FromMinutes(1));
|
||||
|
||||
|
||||
return string.Equals(msg.Content, expectedReply, StringComparison.InvariantCultureIgnoreCase);
|
||||
}
|
||||
|
||||
public static async Task Paginate<T>(this Context ctx, IAsyncEnumerable<T> items, int totalCount, int itemsPerPage, string title, string color, Func<EmbedBuilder, IEnumerable<T>, Task> renderer) {
|
||||
public static async Task Paginate<T>(this Context ctx, IAsyncEnumerable<T> items, int totalCount, int itemsPerPage, string title, string color, Func<EmbedBuilder, IEnumerable<T>, Task> renderer)
|
||||
{
|
||||
// TODO: make this generic enough we can use it in Choose<T> below
|
||||
|
||||
var buffer = new List<T>();
|
||||
await using var enumerator = items.GetAsyncEnumerator();
|
||||
|
||||
var pageCount = (int) Math.Ceiling(totalCount / (double) itemsPerPage);
|
||||
var pageCount = (int)Math.Ceiling(totalCount / (double)itemsPerPage);
|
||||
async Task<Embed> MakeEmbedForPage(int page)
|
||||
{
|
||||
var bufferedItemsNeeded = (page + 1) * itemsPerPage;
|
||||
@@ -79,10 +82,10 @@ namespace PluralKit.Bot {
|
||||
buffer.Add(enumerator.Current);
|
||||
|
||||
var eb = new EmbedBuilder();
|
||||
eb.Title(pageCount > 1 ? $"[{page+1}/{pageCount}] {title}" : title);
|
||||
eb.Title(pageCount > 1 ? $"[{page + 1}/{pageCount}] {title}" : title);
|
||||
if (color != null)
|
||||
eb.Color(color.ToDiscordColor());
|
||||
await renderer(eb, buffer.Skip(page*itemsPerPage).Take(itemsPerPage));
|
||||
await renderer(eb, buffer.Skip(page * itemsPerPage).Take(itemsPerPage));
|
||||
return eb.Build();
|
||||
}
|
||||
|
||||
@@ -94,9 +97,11 @@ namespace PluralKit.Bot {
|
||||
|
||||
var _ = ctx.Rest.CreateReactionsBulk(msg, botEmojis); // Again, "fork"
|
||||
|
||||
try {
|
||||
try
|
||||
{
|
||||
var currentPage = 0;
|
||||
while (true) {
|
||||
while (true)
|
||||
{
|
||||
var reaction = await ctx.AwaitReaction(msg, ctx.Author, timeout: Duration.FromMinutes(5));
|
||||
|
||||
// Increment/decrement page counter based on which reaction was clicked
|
||||
@@ -105,19 +110,21 @@ namespace PluralKit.Bot {
|
||||
if (reaction.Emoji.Name == "\u27A1") currentPage = (currentPage + 1) % pageCount; // >
|
||||
if (reaction.Emoji.Name == "\u23E9") currentPage = pageCount - 1; // >>
|
||||
if (reaction.Emoji.Name == Emojis.Error) break; // X
|
||||
|
||||
|
||||
// C#'s % operator is dumb and wrong, so we fix negative numbers
|
||||
if (currentPage < 0) currentPage += pageCount;
|
||||
|
||||
|
||||
// If we can, remove the user's reaction (so they can press again quickly)
|
||||
if (ctx.BotPermissions.HasFlag(PermissionSet.ManageMessages))
|
||||
await ctx.Rest.DeleteUserReaction(msg.ChannelId, msg.Id, reaction.Emoji, reaction.UserId);
|
||||
|
||||
|
||||
// Edit the embed with the new page
|
||||
var embed = await MakeEmbedForPage(currentPage);
|
||||
await ctx.Rest.EditMessage(msg.ChannelId, msg.Id, new MessageEditRequest {Embed = embed});
|
||||
await ctx.Rest.EditMessage(msg.ChannelId, msg.Id, new MessageEditRequest { Embed = embed });
|
||||
}
|
||||
} catch (TimeoutException) {
|
||||
}
|
||||
catch (TimeoutException)
|
||||
{
|
||||
// "escape hatch", clean up as if we hit X
|
||||
}
|
||||
|
||||
@@ -132,7 +139,7 @@ namespace PluralKit.Bot {
|
||||
// either way, nothing to do here
|
||||
catch (ForbiddenException) { }
|
||||
}
|
||||
|
||||
|
||||
public static async Task<T> Choose<T>(this Context ctx, string description, IList<T> items, Func<T, string> display = null)
|
||||
{
|
||||
// Generate a list of :regional_indicator_?: emoji surrogate pairs (starting at codepoint 0x1F1E6)
|
||||
@@ -157,27 +164,27 @@ namespace PluralKit.Bot {
|
||||
if (items.Count > pageSize)
|
||||
{
|
||||
var currPage = 0;
|
||||
var pageCount = (items.Count-1) / pageSize + 1;
|
||||
|
||||
var pageCount = (items.Count - 1) / pageSize + 1;
|
||||
|
||||
// Send the original message
|
||||
var msg = await ctx.Reply($"**[Page {currPage + 1}/{pageCount}]**\n{description}\n{MakeOptionList(currPage)}");
|
||||
|
||||
|
||||
// Add back/forward reactions and the actual indicator emojis
|
||||
async Task AddEmojis()
|
||||
{
|
||||
await ctx.Rest.CreateReaction(msg.ChannelId, msg.Id, new() { Name = "\u2B05" });
|
||||
await ctx.Rest.CreateReaction(msg.ChannelId, msg.Id, new() { Name = "\u27A1" });
|
||||
for (int i = 0; i < items.Count; i++)
|
||||
for (int i = 0; i < items.Count; i++)
|
||||
await ctx.Rest.CreateReaction(msg.ChannelId, msg.Id, new() { Name = indicators[i] });
|
||||
}
|
||||
|
||||
var _ = AddEmojis(); // Not concerned about awaiting
|
||||
|
||||
|
||||
while (true)
|
||||
{
|
||||
// Wait for a reaction
|
||||
var reaction = await ctx.AwaitReaction(msg, ctx.Author);
|
||||
|
||||
|
||||
// If it's a movement reaction, inc/dec the page index
|
||||
if (reaction.Emoji.Name == "\u2B05") currPage -= 1; // <
|
||||
if (reaction.Emoji.Name == "\u27A1") currPage += 1; // >
|
||||
@@ -210,17 +217,17 @@ namespace PluralKit.Bot {
|
||||
async Task AddEmojis()
|
||||
{
|
||||
for (int i = 0; i < items.Count; i++)
|
||||
await ctx.Rest.CreateReaction(msg.ChannelId, msg.Id, new() {Name = indicators[i]});
|
||||
await ctx.Rest.CreateReaction(msg.ChannelId, msg.Id, new() { Name = indicators[i] });
|
||||
}
|
||||
|
||||
var _ = AddEmojis();
|
||||
|
||||
// Then wait for a reaction and return whichever one we found
|
||||
var reaction = await ctx.AwaitReaction(msg, ctx.Author,rx => indicators.Contains(rx.Emoji.Name));
|
||||
var reaction = await ctx.AwaitReaction(msg, ctx.Author, rx => indicators.Contains(rx.Emoji.Name));
|
||||
return items[Array.IndexOf(indicators, reaction.Emoji.Name)];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static async Task BusyIndicator(this Context ctx, Func<Task> f, string emoji = "\u23f3" /* hourglass */)
|
||||
{
|
||||
await ctx.BusyIndicator<object>(async () =>
|
||||
@@ -239,13 +246,13 @@ namespace PluralKit.Bot {
|
||||
|
||||
try
|
||||
{
|
||||
await Task.WhenAll(ctx.Rest.CreateReaction(ctx.Message.ChannelId, ctx.Message.Id, new() {Name = emoji}), task);
|
||||
await Task.WhenAll(ctx.Rest.CreateReaction(ctx.Message.ChannelId, ctx.Message.Id, new() { Name = emoji }), task);
|
||||
return await task;
|
||||
}
|
||||
finally
|
||||
{
|
||||
var _ = ctx.Rest.DeleteOwnReaction(ctx.Message.ChannelId, ctx.Message.Id, new() { Name = emoji });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -24,7 +24,7 @@ namespace PluralKit.Bot
|
||||
public const uint Green = 0x00cc78;
|
||||
public const uint Red = 0xef4b3d;
|
||||
public const uint Gray = 0x979c9f;
|
||||
|
||||
|
||||
private static readonly Regex USER_MENTION = new Regex("<@!?(\\d{17,19})>");
|
||||
private static readonly Regex ROLE_MENTION = new Regex("<@&(\\d{17,19})>");
|
||||
private static readonly Regex EVERYONE_HERE_MENTION = new Regex("@(everyone|here)");
|
||||
@@ -36,23 +36,23 @@ namespace PluralKit.Bot
|
||||
// corresponding to: https://github.com/Khan/simple-markdown/blob/master/src/index.js#L1489
|
||||
// I added <? and >? at the start/end; they need to be handled specially later...
|
||||
private static readonly Regex UNBROKEN_LINK_REGEX = new Regex("<?(https?:\\/\\/[^\\s<]+[^<.,:;\"')\\]\\s])>?");
|
||||
|
||||
|
||||
public static string NameAndMention(this User user)
|
||||
{
|
||||
return $"{user.Username}#{user.Discriminator} ({user.Mention()})";
|
||||
}
|
||||
|
||||
|
||||
public static Instant SnowflakeToInstant(ulong snowflake) =>
|
||||
Instant.FromUtc(2015, 1, 1, 0, 0, 0) + Duration.FromMilliseconds(snowflake >> 22);
|
||||
|
||||
public static ulong InstantToSnowflake(Instant time) =>
|
||||
(ulong) (time - Instant.FromUtc(2015, 1, 1, 0, 0, 0)).TotalMilliseconds << 22;
|
||||
|
||||
(ulong)(time - Instant.FromUtc(2015, 1, 1, 0, 0, 0)).TotalMilliseconds << 22;
|
||||
|
||||
public static async Task CreateReactionsBulk(this DiscordApiClient rest, Message msg, string[] reactions)
|
||||
{
|
||||
foreach (var reaction in reactions)
|
||||
{
|
||||
await rest.CreateReaction(msg.ChannelId, msg.Id, new() {Name = reaction});
|
||||
await rest.CreateReaction(msg.ChannelId, msg.Id, new() { Name = reaction });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,22 +97,23 @@ namespace PluralKit.Bot
|
||||
var users = USER_MENTION.Matches(input).Select(x => ulong.Parse(x.Groups[1].Value));
|
||||
var roles = ROLE_MENTION.Matches(input).Select(x => ulong.Parse(x.Groups[1].Value));
|
||||
var everyone = EVERYONE_HERE_MENTION.IsMatch(input);
|
||||
|
||||
|
||||
return new AllowedMentions
|
||||
{
|
||||
Users = users.Distinct().ToArray(),
|
||||
Roles = roles.Distinct().ToArray(),
|
||||
Parse = everyone ? new[] {AllowedMentions.ParseType.Everyone} : null
|
||||
Parse = everyone ? new[] { AllowedMentions.ParseType.Everyone } : null
|
||||
};
|
||||
}
|
||||
|
||||
public static AllowedMentions RemoveUnmentionableRoles(this AllowedMentions mentions, Guild guild)
|
||||
{
|
||||
return mentions with {
|
||||
return mentions with
|
||||
{
|
||||
Roles = mentions.Roles
|
||||
?.Where(id => guild.Roles.FirstOrDefault(r => r.Id == id)?.Mentionable == true)
|
||||
.ToArray()
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
public static string EscapeMarkdown(this string input)
|
||||
@@ -146,7 +147,7 @@ namespace PluralKit.Bot
|
||||
// So, surrounding with two backticks, then escaping all backtick pairs makes it impossible(!) to "break out"
|
||||
return $"``{EscapeBacktickPair(input)}``";
|
||||
}
|
||||
|
||||
|
||||
public static EmbedBuilder WithSimpleLineContent(this EmbedBuilder eb, IEnumerable<string> lines)
|
||||
{
|
||||
static int CharacterLimit(int pageNumber) =>
|
||||
@@ -178,7 +179,7 @@ namespace PluralKit.Bot
|
||||
return $"<{match.Value}>";
|
||||
});
|
||||
|
||||
public static string EventType(this IGatewayEvent evt) =>
|
||||
public static string EventType(this IGatewayEvent evt) =>
|
||||
evt.GetType().Name.Replace("Event", "");
|
||||
|
||||
public static bool HasReactionPermissions(Context ctx)
|
||||
@@ -188,11 +189,11 @@ namespace PluralKit.Bot
|
||||
}
|
||||
|
||||
public static bool IsValidGuildChannel(Channel channel) =>
|
||||
channel.Type is
|
||||
channel.Type is
|
||||
Channel.ChannelType.GuildText or
|
||||
Channel.ChannelType.GuildNews or
|
||||
Channel.ChannelType.GuildNews or
|
||||
Channel.ChannelType.GuildPublicThread or
|
||||
Channel.ChannelType.GuildPrivateThread or
|
||||
Channel.ChannelType.GuildNewsThread;
|
||||
}
|
||||
}
|
||||
}
|
@@ -13,7 +13,7 @@ namespace PluralKit.Bot
|
||||
{
|
||||
private readonly InteractionCreateEvent _evt;
|
||||
private readonly ILifetimeScope _services;
|
||||
|
||||
|
||||
public InteractionContext(InteractionCreateEvent evt, ILifetimeScope services)
|
||||
{
|
||||
_evt = evt;
|
||||
@@ -55,9 +55,9 @@ namespace PluralKit.Bot
|
||||
}
|
||||
|
||||
public async Task Respond(InteractionResponse.ResponseType type, InteractionApplicationCommandCallbackData? data)
|
||||
{
|
||||
{
|
||||
var rest = _services.Resolve<DiscordApiClient>();
|
||||
await rest.CreateInteractionResponse(_evt.Id, _evt.Token, new InteractionResponse {Type = type, Data = data});
|
||||
await rest.CreateInteractionResponse(_evt.Id, _evt.Token, new InteractionResponse { Type = type, Data = data });
|
||||
}
|
||||
}
|
||||
}
|
@@ -5,7 +5,7 @@ namespace PluralKit.Bot.Utils
|
||||
{
|
||||
// PK note: class is wholesale copied from Discord.NET (MIT-licensed)
|
||||
// https://github.com/discord-net/Discord.Net/blob/ff0fea98a65d907fbce07856f1a9ef4aebb9108b/src/Discord.Net.Core/Utils/MentionUtils.cs
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Provides a series of helper methods for parsing mentions.
|
||||
/// </summary>
|
||||
|
@@ -16,10 +16,11 @@ using Polly.Timeout;
|
||||
|
||||
namespace PluralKit.Bot
|
||||
{
|
||||
public static class MiscUtils {
|
||||
public static string ProxyTagsString(this PKMember member, string separator = ", ") =>
|
||||
public static class MiscUtils
|
||||
{
|
||||
public static string ProxyTagsString(this PKMember member, string separator = ", ") =>
|
||||
string.Join(separator, member.ProxyTags.Select(t => t.ProxyString.AsCode()));
|
||||
|
||||
|
||||
public static bool IsOurProblem(this Exception e)
|
||||
{
|
||||
// This function filters out sporadic errors out of our control from being reported to Sentry
|
||||
@@ -40,24 +41,24 @@ namespace PluralKit.Bot
|
||||
if (e is TimeoutRejectedException) return false;
|
||||
|
||||
// 5xxs? also not our problem :^)
|
||||
if (e is UnknownDiscordRequestException udre && (int) udre.StatusCode >= 500) return false;
|
||||
if (e is UnknownDiscordRequestException udre && (int)udre.StatusCode >= 500) return false;
|
||||
|
||||
// Webhook server errors are also *not our problem*
|
||||
// (this includes rate limit errors, WebhookRateLimited is a subclass)
|
||||
if (e is WebhookExecutionErrorOnDiscordsEnd) return false;
|
||||
|
||||
|
||||
// Socket errors are *not our problem*
|
||||
if (e.GetBaseException() is SocketException) return false;
|
||||
|
||||
|
||||
// Tasks being cancelled for whatver reason are, you guessed it, also not our problem.
|
||||
if (e is TaskCanceledException) return false;
|
||||
|
||||
// Sometimes Discord just times everything out.
|
||||
if (e is TimeoutException) return false;
|
||||
|
||||
|
||||
// Ignore "Database is shutting down" error
|
||||
if (e is PostgresException pe && pe.SqlState == "57P03") return false;
|
||||
|
||||
|
||||
// Ignore thread pool exhaustion errors
|
||||
if (e is NpgsqlException npe && npe.Message.Contains("The connection pool has been exhausted")) return false;
|
||||
|
||||
|
@@ -1,4 +1,4 @@
|
||||
using System.Linq;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
using PluralKit.Core;
|
||||
@@ -24,21 +24,21 @@ namespace PluralKit.Bot
|
||||
bool IsSimple(string s) =>
|
||||
// No spaces, no symbols, allow single quote but not at the start
|
||||
Regex.IsMatch(s, "^[\\w\\d\\-_'?]+$") && !s.StartsWith("'");
|
||||
|
||||
|
||||
// If it's very long (>25 chars), always use hid
|
||||
if (name.Length >= 25)
|
||||
return hid;
|
||||
|
||||
// If name is "simple" just use that
|
||||
if (IsSimple(name))
|
||||
if (IsSimple(name))
|
||||
return name;
|
||||
|
||||
|
||||
// If three or fewer "words" and they're all simple individually, quote them
|
||||
var words = name.Split(' ');
|
||||
if (words.Length <= 3 && words.All(w => w.Length > 0 && IsSimple(w)))
|
||||
// Words with double quotes are never "simple" so we're safe to naive-quote here
|
||||
return $"\"{name}\"";
|
||||
|
||||
|
||||
// Otherwise, just use hid
|
||||
return hid;
|
||||
}
|
||||
|
@@ -7,7 +7,7 @@ using Sentry;
|
||||
|
||||
namespace PluralKit.Bot
|
||||
{
|
||||
public interface ISentryEnricher<T> where T: IGatewayEvent
|
||||
public interface ISentryEnricher<T> where T : IGatewayEvent
|
||||
{
|
||||
void Enrich(Scope scope, Shard shard, T evt);
|
||||
}
|
||||
@@ -25,10 +25,10 @@ namespace PluralKit.Bot
|
||||
{
|
||||
_bot = bot;
|
||||
}
|
||||
|
||||
|
||||
// TODO: should this class take the Scope by dependency injection instead?
|
||||
// Would allow us to create a centralized "chain of handlers" where this class could just be registered as an entry in
|
||||
|
||||
|
||||
public void Enrich(Scope scope, Shard shard, MessageCreateEvent evt)
|
||||
{
|
||||
scope.AddBreadcrumb(evt.Content, "event.message", data: new Dictionary<string, string>
|
||||
|
@@ -1,4 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Myriad.Cache;
|
||||
using Myriad.Extensions;
|
||||
@@ -26,14 +26,14 @@ namespace PluralKit.Bot
|
||||
{
|
||||
new("ShardId", new ScalarValue(shard.ShardId)),
|
||||
};
|
||||
|
||||
|
||||
var (guild, channel) = GetGuildChannelId(evt);
|
||||
var user = GetUserId(evt);
|
||||
var message = GetMessageId(evt);
|
||||
|
||||
|
||||
if (guild != null)
|
||||
props.Add(new("GuildId", new ScalarValue(guild.Value)));
|
||||
|
||||
|
||||
if (channel != null)
|
||||
{
|
||||
props.Add(new("ChannelId", new ScalarValue(channel.Value)));
|
||||
@@ -44,10 +44,10 @@ namespace PluralKit.Bot
|
||||
props.Add(new("BotPermissions", new ScalarValue(botPermissions)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (message != null)
|
||||
props.Add(new("MessageId", new ScalarValue(message.Value)));
|
||||
|
||||
|
||||
if (user != null)
|
||||
props.Add(new("UserId", new ScalarValue(user.Value)));
|
||||
|
||||
@@ -56,7 +56,7 @@ namespace PluralKit.Bot
|
||||
|
||||
return new Inner(props);
|
||||
}
|
||||
|
||||
|
||||
private (ulong?, ulong?) GetGuildChannelId(IGatewayEvent evt) => evt switch
|
||||
{
|
||||
ChannelCreateEvent e => (e.GuildId, e.Id),
|
||||
@@ -101,7 +101,7 @@ namespace PluralKit.Bot
|
||||
{
|
||||
public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
|
||||
{
|
||||
foreach (var prop in Properties)
|
||||
foreach (var prop in Properties)
|
||||
logEvent.AddPropertyIfAbsent(prop);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user