PluralKit/PluralKit.Bot/CommandSystem/Context/ContextEntityArgumentsExt.cs

142 lines
5.1 KiB
C#
Raw Normal View History

using System.Text.RegularExpressions;
2020-12-25 13:19:35 +01:00
using Myriad.Extensions;
using Myriad.Types;
using PluralKit.Bot.Utils;
using PluralKit.Core;
namespace PluralKit.Bot;
public static class ContextEntityArgumentsExt
{
2025-04-04 03:50:07 +09:00
public static async Task<User> ParseUser(this Context ctx, string arg)
{
if (arg.TryParseMention(out var id))
return await ctx.Cache.GetOrFetchUser(ctx.Rest, id);
return null;
}
public static async Task<PKSystem> ParseSystem(this Context ctx, string input)
{
// System references can take three forms:
// - The direct user ID of an account connected to the system
// - A @mention of an account connected to the system (<@uid>)
// - A system hid
// Direct IDs and mentions are both handled by the below method:
if (input.TryParseMention(out var id))
return await ctx.Repository.GetSystemByAccount(id);
// Finally, try HID parsing
2024-04-28 15:46:06 +12:00
if (input.TryParseHid(out var hid))
return await ctx.Repository.GetSystemByHid(hid);
return null;
}
2025-10-04 01:57:48 +00:00
public static async Task<PKMember?> ParseMember(this Context ctx, string input, bool byId)
{
// Member references can have one of three forms, depending on
// whether you're in a system or not:
// - A member hid
// - A textual name of a member *in your own system*
// - a textual display name of a member *in your own system*
// Skip name / display name matching if the user does not have a system
// or if they specifically request by-HID matching
if (ctx.System != null && !byId)
{
// First, try finding by member name in system
if (await ctx.Repository.GetMemberByName(ctx.System.Id, input) is PKMember memberByName)
return memberByName;
// And if that fails, we try finding a member with a display name matching the argument from the system
if (ctx.System != null &&
await ctx.Repository.GetMemberByDisplayName(ctx.System.Id, input) is PKMember memberByDisplayName)
return memberByDisplayName;
}
// Finally (or if by-HID lookup is specified), check if input is a valid HID and then try member HID parsing:
2024-04-28 15:46:06 +12:00
if (!input.TryParseHid(out var hid))
return null;
// For posterity:
// There was a bug that made `SELECT * FROM MEMBERS WHERE HID = $1` hang forever BUT
// `SELECT * FROM MEMBERS WHERE HID = $1 AND SYSTEM = $2` *doesn't* hang! So this is a bandaid for that
// If we are supposed to restrict it to a system anyway we can just do that
PKMember memberByHid = null;
2025-10-04 01:57:48 +00:00
memberByHid = await ctx.Repository.GetMemberByHid(hid, ctx.System?.Id);
if (memberByHid != null)
return memberByHid;
// ff ctx.System was null then this would be a duplicate of above and we don't want to run it again
if (ctx.System != null)
{
2025-10-04 01:57:48 +00:00
memberByHid = await ctx.Repository.GetMemberByHid(hid);
if (memberByHid != null)
return memberByHid;
}
// We didn't find anything, so we return null.
return null;
}
2021-08-27 11:03:47 -04:00
public static async Task<PKGroup> ParseGroup(this Context ctx, string input, bool byId, SystemId? restrictToSystem = null)
{
if (ctx.System != null && !byId)
2020-06-29 23:51:12 +02:00
{
if (await ctx.Repository.GetGroupByName(ctx.System.Id, input) is { } byName)
return byName;
if (await ctx.Repository.GetGroupByDisplayName(ctx.System.Id, input) is { } byDisplayName)
return byDisplayName;
2020-06-29 23:51:12 +02:00
}
2024-04-28 15:46:06 +12:00
if (!input.TryParseHid(out var hid))
return null;
if (await ctx.Repository.GetGroupByHid(hid, restrictToSystem) is { } byHid)
return byHid;
return null;
}
public static string CreateNotFoundError(this Context ctx, string entity, string input, bool byId = false)
{
var isIDOnlyQuery = ctx.System == null || byId;
var inputIsHid = HidUtils.ParseHid(input) != null;
if (isIDOnlyQuery)
{
if (inputIsHid)
return $"{entity} with ID \"{input}\" not found.";
2024-04-28 15:46:06 +12:00
return $"{entity} not found. Note that a {entity.ToLower()} ID is 5 or 6 characters long.";
}
2021-08-27 11:03:47 -04:00
if (inputIsHid)
return $"{entity} with ID or name \"{input}\" not found.";
2024-04-28 15:46:06 +12:00
return $"{entity} with name \"{input}\" not found. Note that a {entity.ToLower()} ID is 5 or 6 characters long.";
}
public static async Task<Channel> MatchChannel(this Context ctx)
{
if (!MentionUtils.TryParseChannel(ctx.PeekArgument(), out var id))
return null;
2021-08-27 11:03:47 -04:00
2024-09-14 12:19:47 +09:00
// todo: match channels in other guilds
var channel = await ctx.Cache.TryGetChannel(ctx.Guild!.Id, id);
if (channel == null)
channel = await ctx.Rest.GetChannelOrNull(id);
if (channel == null)
return null;
2021-08-27 11:03:47 -04:00
if (!DiscordUtils.IsValidGuildChannel(channel))
return null;
ctx.PopArgument();
return channel;
}
}