2022-04-24 14:51:47 -04:00
|
|
|
using System.Text.RegularExpressions;
|
|
|
|
|
|
2020-12-25 13:19:35 +01:00
|
|
|
using Myriad.Extensions;
|
2020-12-22 16:55:13 +01:00
|
|
|
using Myriad.Types;
|
|
|
|
|
|
2020-07-01 18:27:26 +02:00
|
|
|
using PluralKit.Bot.Utils;
|
|
|
|
|
using PluralKit.Core;
|
|
|
|
|
|
2021-11-26 21:10:56 -05:00
|
|
|
namespace PluralKit.Bot;
|
|
|
|
|
|
|
|
|
|
public static class ContextEntityArgumentsExt
|
2020-07-01 18:27:26 +02:00
|
|
|
{
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-05 13:00:06 +09:00
|
|
|
public static async Task<PKSystem> ParseSystem(this Context ctx, string input)
|
2021-11-26 21:10:56 -05:00
|
|
|
{
|
|
|
|
|
// 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
|
2020-07-01 18:27:26 +02:00
|
|
|
|
2021-11-26 21:10:56 -05:00
|
|
|
// Direct IDs and mentions are both handled by the below method:
|
|
|
|
|
if (input.TryParseMention(out var id))
|
|
|
|
|
return await ctx.Repository.GetSystemByAccount(id);
|
2020-07-01 18:27:26 +02:00
|
|
|
|
2021-11-26 21:10:56 -05:00
|
|
|
// 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;
|
2021-11-26 21:10:56 -05:00
|
|
|
}
|
2020-07-01 18:27:26 +02:00
|
|
|
|
2025-10-04 01:57:48 +00:00
|
|
|
public static async Task<PKMember?> ParseMember(this Context ctx, string input, bool byId)
|
2021-11-26 21:10:56 -05:00
|
|
|
{
|
|
|
|
|
// 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*
|
2020-07-01 18:27:26 +02:00
|
|
|
|
2021-11-26 21:10:56 -05:00
|
|
|
// Skip name / display name matching if the user does not have a system
|
|
|
|
|
// or if they specifically request by-HID matching
|
2025-01-07 23:15:18 +09:00
|
|
|
if (ctx.System != null && !byId)
|
2020-07-01 18:27:26 +02:00
|
|
|
{
|
2021-11-26 21:10:56 -05:00
|
|
|
// 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;
|
|
|
|
|
}
|
2020-07-01 18:27:26 +02:00
|
|
|
|
2022-04-24 14:51:47 -04:00
|
|
|
// 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))
|
2022-04-24 14:51:47 -04:00
|
|
|
return null;
|
2022-04-06 00:19:35 -06:00
|
|
|
|
|
|
|
|
// 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)
|
2022-04-06 00:19:35 -06:00
|
|
|
{
|
2025-10-04 01:57:48 +00:00
|
|
|
memberByHid = await ctx.Repository.GetMemberByHid(hid);
|
2022-04-06 00:19:35 -06:00
|
|
|
if (memberByHid != null)
|
|
|
|
|
return memberByHid;
|
|
|
|
|
}
|
2020-07-01 18:27:26 +02:00
|
|
|
|
2021-11-26 21:10:56 -05:00
|
|
|
// We didn't find anything, so we return null.
|
|
|
|
|
return null;
|
|
|
|
|
}
|
2021-08-27 11:03:47 -04:00
|
|
|
|
2025-09-26 23:56:49 +00:00
|
|
|
public static async Task<PKGroup> ParseGroup(this Context ctx, string input, bool byId, SystemId? restrictToSystem = null)
|
2021-11-26 21:10:56 -05:00
|
|
|
{
|
2025-09-26 23:56:49 +00:00
|
|
|
if (ctx.System != null && !byId)
|
2020-06-29 23:51:12 +02:00
|
|
|
{
|
2021-11-26 21:10:56 -05: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
|
|
|
}
|
2020-07-01 18:27:26 +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)
|
2021-11-26 21:10:56 -05:00
|
|
|
return byHid;
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
2025-09-26 23:56:49 +00:00
|
|
|
|
2025-01-07 23:15:18 +09:00
|
|
|
public static string CreateNotFoundError(this Context ctx, string entity, string input, bool byId = false)
|
2021-11-26 21:10:56 -05:00
|
|
|
{
|
2025-01-07 23:15:18 +09:00
|
|
|
var isIDOnlyQuery = ctx.System == null || byId;
|
2024-05-11 11:48:16 +12:00
|
|
|
var inputIsHid = HidUtils.ParseHid(input) != null;
|
2021-11-26 21:10:56 -05:00
|
|
|
|
|
|
|
|
if (isIDOnlyQuery)
|
2020-07-01 18:27:26 +02:00
|
|
|
{
|
2024-05-11 11:48:16 +12:00
|
|
|
if (inputIsHid)
|
2021-11-26 21:10:56 -05:00
|
|
|
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.";
|
2020-07-06 19:50:39 +02:00
|
|
|
}
|
2021-08-27 11:03:47 -04:00
|
|
|
|
2024-05-11 11:48:16 +12:00
|
|
|
if (inputIsHid)
|
2021-11-26 21:10:56 -05:00
|
|
|
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.";
|
2021-11-26 21:10:56 -05:00
|
|
|
}
|
2020-12-22 16:55:13 +01:00
|
|
|
|
2021-11-26 21:10:56 -05:00
|
|
|
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);
|
2022-03-09 20:06:53 -05:00
|
|
|
if (channel == null)
|
|
|
|
|
channel = await ctx.Rest.GetChannelOrNull(id);
|
|
|
|
|
if (channel == null)
|
2021-11-26 21:10:56 -05:00
|
|
|
return null;
|
2021-08-27 11:03:47 -04:00
|
|
|
|
2021-11-26 21:10:56 -05:00
|
|
|
if (!DiscordUtils.IsValidGuildChannel(channel))
|
|
|
|
|
return null;
|
2021-04-05 22:25:13 -06:00
|
|
|
|
2021-11-26 21:10:56 -05:00
|
|
|
ctx.PopArgument();
|
|
|
|
|
return channel;
|
|
|
|
|
}
|
2020-07-01 18:27:26 +02:00
|
|
|
}
|