mirror of
https://github.com/PluralKit/PluralKit.git
synced 2026-02-10 15:57:53 +00:00
feat: better parameters handling, implement multi-token matching
This commit is contained in:
parent
b29c51f103
commit
482c923507
14 changed files with 521 additions and 251 deletions
|
|
@ -4,30 +4,29 @@ namespace PluralKit.Bot;
|
|||
|
||||
public partial class CommandTree
|
||||
{
|
||||
public Task ExecuteCommand(Context ctx, ResolvedParameters parameters)
|
||||
public Task ExecuteCommand(Context ctx)
|
||||
{
|
||||
switch (parameters.Raw.Callback())
|
||||
return ctx.Parameters.Callback() switch
|
||||
{
|
||||
case "help":
|
||||
return ctx.Execute<Help>(Help, m => m.HelpRoot(ctx));
|
||||
case "help_commands":
|
||||
return ctx.Reply(
|
||||
"For the list of commands, see the website: <https://pluralkit.me/commands>");
|
||||
case "help_proxy":
|
||||
return ctx.Reply(
|
||||
"The proxy help page has been moved! See the website: https://pluralkit.me/guide#proxying");
|
||||
case "member_show":
|
||||
return ctx.Execute<Member>(MemberInfo, m => m.ViewMember(ctx, parameters.MemberParams["target"]));
|
||||
case "member_new":
|
||||
return ctx.Execute<Member>(MemberNew, m => m.NewMember(ctx, parameters.Raw.Params()["name"]));
|
||||
case "fun_thunder":
|
||||
return ctx.Execute<Fun>(null, m => m.Thunder(ctx));
|
||||
case "fun_meow":
|
||||
return ctx.Execute<Fun>(null, m => m.Meow(ctx));
|
||||
default:
|
||||
return ctx.Reply(
|
||||
$"{Emojis.Error} Parsed command {parameters.Raw.Callback().AsCode()} not implemented in PluralKit.Bot!");
|
||||
}
|
||||
"help" => ctx.Execute<Help>(Help, m => m.HelpRoot(ctx)),
|
||||
"help_commands" => ctx.Reply(
|
||||
"For the list of commands, see the website: <https://pluralkit.me/commands>"),
|
||||
"help_proxy" => ctx.Reply(
|
||||
"The proxy help page has been moved! See the website: https://pluralkit.me/guide#proxying"),
|
||||
"member_show" => ctx.Execute<Member>(MemberInfo, m => m.ViewMember(ctx)),
|
||||
"member_new" => ctx.Execute<Member>(MemberNew, m => m.NewMember(ctx)),
|
||||
"member_soulscream" => ctx.Execute<Member>(MemberInfo, m => m.Soulscream(ctx)),
|
||||
"cfg_ap_account_show" => ctx.Execute<Config>(null, m => m.ViewAutoproxyAccount(ctx)),
|
||||
"cfg_ap_account_update" => ctx.Execute<Config>(null, m => m.EditAutoproxyAccount(ctx)),
|
||||
"cfg_ap_timeout_show" => ctx.Execute<Config>(null, m => m.ViewAutoproxyTimeout(ctx)),
|
||||
"cfg_ap_timeout_update" => ctx.Execute<Config>(null, m => m.EditAutoproxyTimeout(ctx)),
|
||||
"fun_thunder" => ctx.Execute<Fun>(null, m => m.Thunder(ctx)),
|
||||
"fun_meow" => ctx.Execute<Fun>(null, m => m.Meow(ctx)),
|
||||
_ =>
|
||||
// this should only ever occur when deving if commands are not implemented...
|
||||
ctx.Reply(
|
||||
$"{Emojis.Error} Parsed command {ctx.Parameters.Callback().AsCode()} not implemented in PluralKit.Bot!"),
|
||||
};
|
||||
if (ctx.Match("system", "s"))
|
||||
return HandleSystemCommand(ctx);
|
||||
if (ctx.Match("member", "m"))
|
||||
|
|
@ -405,10 +404,6 @@ public partial class CommandTree
|
|||
await ctx.Execute<MemberEdit>(MemberPrivacy, m => m.Privacy(ctx, target, PrivacyLevel.Private));
|
||||
else if (ctx.Match("public", "shown", "show", "unhide", "unhidden"))
|
||||
await ctx.Execute<MemberEdit>(MemberPrivacy, m => m.Privacy(ctx, target, PrivacyLevel.Public));
|
||||
else if (ctx.Match("soulscream"))
|
||||
await ctx.Execute<Member>(MemberInfo, m => m.Soulscream(ctx, target));
|
||||
else if (!ctx.HasNext()) // Bare command
|
||||
await ctx.Execute<Member>(MemberInfo, m => m.ViewMember(ctx, target));
|
||||
else
|
||||
await PrintCommandNotFoundError(ctx, MemberInfo, MemberRename, MemberDisplayName, MemberServerName,
|
||||
MemberDesc, MemberPronouns, MemberColor, MemberBirthday, MemberProxy, MemberDelete, MemberAvatar,
|
||||
|
|
@ -576,10 +571,6 @@ public partial class CommandTree
|
|||
if (!ctx.HasNext())
|
||||
return ctx.Execute<Config>(null, m => m.ShowConfig(ctx));
|
||||
|
||||
if (ctx.MatchMultiple(new[] { "autoproxy", "ap" }, new[] { "account", "ac" }))
|
||||
return ctx.Execute<Config>(null, m => m.AutoproxyAccount(ctx));
|
||||
if (ctx.MatchMultiple(new[] { "autoproxy", "ap" }, new[] { "timeout", "tm" }))
|
||||
return ctx.Execute<Config>(null, m => m.AutoproxyTimeout(ctx));
|
||||
if (ctx.Match("timezone", "zone", "tz"))
|
||||
return ctx.Execute<Config>(null, m => m.SystemTimezone(ctx));
|
||||
if (ctx.Match("ping"))
|
||||
|
|
|
|||
|
|
@ -26,11 +26,9 @@ public class Context
|
|||
private readonly IMetrics _metrics;
|
||||
private readonly CommandMessageService _commandMessageService;
|
||||
|
||||
private Command? _currentCommand;
|
||||
|
||||
public Context(ILifetimeScope provider, int shardId, Guild? guild, Channel channel, MessageCreateEvent message,
|
||||
int commandParseOffset, PKSystem senderSystem, SystemConfig config,
|
||||
GuildConfig? guildConfig, string[] prefixes)
|
||||
GuildConfig? guildConfig, string[] prefixes, Parameters parameters)
|
||||
{
|
||||
Message = (Message)message;
|
||||
ShardId = shardId;
|
||||
|
|
@ -50,6 +48,7 @@ public class Context
|
|||
DefaultPrefix = prefixes[0];
|
||||
Rest = provider.Resolve<DiscordApiClient>();
|
||||
Cluster = provider.Resolve<Cluster>();
|
||||
Parameters = parameters;
|
||||
}
|
||||
|
||||
public readonly IDiscordCache Cache;
|
||||
|
|
@ -75,6 +74,7 @@ public class Context
|
|||
|
||||
public readonly string CommandPrefix;
|
||||
public readonly string DefaultPrefix;
|
||||
public readonly Parameters Parameters;
|
||||
|
||||
internal readonly IDatabase Database;
|
||||
internal readonly ModelRepository Repository;
|
||||
|
|
@ -111,8 +111,6 @@ public class Context
|
|||
|
||||
public async Task Execute<T>(Command? commandDef, Func<T, Task> handler, bool deprecated = false)
|
||||
{
|
||||
_currentCommand = commandDef;
|
||||
|
||||
if (deprecated && commandDef != null)
|
||||
{
|
||||
await Reply($"{Emojis.Warn} Server configuration has moved to `{DefaultPrefix}serverconfig`. The command you are trying to run is now `{DefaultPrefix}{commandDef.Key}`.");
|
||||
|
|
@ -153,8 +151,8 @@ public class Context
|
|||
|
||||
public LookupContext LookupContextFor(SystemId systemId)
|
||||
{
|
||||
var hasPrivateOverride = this.MatchFlag("private", "priv");
|
||||
var hasPublicOverride = this.MatchFlag("public", "pub");
|
||||
var hasPrivateOverride = Parameters.HasFlag("private", "priv");
|
||||
var hasPublicOverride = Parameters.HasFlag("public", "pub");
|
||||
|
||||
if (hasPrivateOverride && hasPublicOverride)
|
||||
throw new PKError("Cannot match both public and private flags at the same time.");
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ public static class ContextEntityArgumentsExt
|
|||
return null;
|
||||
}
|
||||
|
||||
public static async Task<PKMember> ParseMember(this Context ctx, Parameters parameters, string input, SystemId? restrictToSystem = null)
|
||||
public static async Task<PKMember> ParseMember(this Context ctx, string input, bool byId, SystemId? restrictToSystem = null)
|
||||
{
|
||||
// Member references can have one of three forms, depending on
|
||||
// whether you're in a system or not:
|
||||
|
|
@ -69,7 +69,7 @@ public static class ContextEntityArgumentsExt
|
|||
|
||||
// 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 && !parameters.HasFlag("id", "by-id"))
|
||||
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)
|
||||
|
|
@ -169,9 +169,9 @@ public static class ContextEntityArgumentsExt
|
|||
return group;
|
||||
}
|
||||
|
||||
public static string CreateNotFoundError(this Context ctx, Parameters parameters, string entity, string input)
|
||||
public static string CreateNotFoundError(this Context ctx, string entity, string input, bool byId = false)
|
||||
{
|
||||
var isIDOnlyQuery = ctx.System == null || parameters.HasFlag("id", "by-id");
|
||||
var isIDOnlyQuery = ctx.System == null || byId;
|
||||
var inputIsHid = HidUtils.ParseHid(input) != null;
|
||||
|
||||
if (isIDOnlyQuery)
|
||||
|
|
|
|||
64
PluralKit.Bot/CommandSystem/Context/ContextParametersExt.cs
Normal file
64
PluralKit.Bot/CommandSystem/Context/ContextParametersExt.cs
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
using PluralKit.Core;
|
||||
|
||||
namespace PluralKit.Bot;
|
||||
|
||||
public static class ContextParametersExt
|
||||
{
|
||||
public static async Task<string?> ParamResolveOpaque(this Context ctx, string param_name)
|
||||
{
|
||||
return await ctx.Parameters.ResolveParameter(
|
||||
ctx, param_name,
|
||||
param => (param as Parameter.Opaque)?.value
|
||||
);
|
||||
}
|
||||
|
||||
public static async Task<PKMember?> ParamResolveMember(this Context ctx, string param_name)
|
||||
{
|
||||
return await ctx.Parameters.ResolveParameter(
|
||||
ctx, param_name,
|
||||
param => (param as Parameter.MemberRef)?.member
|
||||
);
|
||||
}
|
||||
|
||||
public static async Task<PKSystem?> ParamResolveSystem(this Context ctx, string param_name)
|
||||
{
|
||||
return await ctx.Parameters.ResolveParameter(
|
||||
ctx, param_name,
|
||||
param => (param as Parameter.SystemRef)?.system
|
||||
);
|
||||
}
|
||||
|
||||
public static async Task<MemberPrivacySubject?> ParamResolveMemberPrivacyTarget(this Context ctx, string param_name)
|
||||
{
|
||||
return await ctx.Parameters.ResolveParameter(
|
||||
ctx, param_name,
|
||||
param => (param as Parameter.MemberPrivacyTarget)?.target
|
||||
);
|
||||
}
|
||||
|
||||
public static async Task<string?> ParamResolvePrivacyLevel(this Context ctx, string param_name)
|
||||
{
|
||||
return await ctx.Parameters.ResolveParameter(
|
||||
ctx, param_name,
|
||||
param => (param as Parameter.PrivacyLevel)?.level
|
||||
);
|
||||
}
|
||||
|
||||
public static async Task<bool?> ParamResolveToggle(this Context ctx, string param_name)
|
||||
{
|
||||
return await ctx.Parameters.ResolveParameter(
|
||||
ctx, param_name,
|
||||
param => (param as Parameter.Toggle)?.value
|
||||
);
|
||||
}
|
||||
|
||||
// this can never really be false (either it's present and is true or it's not present)
|
||||
// but we keep it nullable for consistency with the other methods
|
||||
public static async Task<bool?> ParamResolveReset(this Context ctx, string param_name)
|
||||
{
|
||||
return await ctx.Parameters.ResolveParameter<bool?>(
|
||||
ctx, param_name,
|
||||
param => param is Parameter.Reset
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,14 +1,27 @@
|
|||
using System.Diagnostics;
|
||||
using PluralKit.Core;
|
||||
using uniffi.commands;
|
||||
|
||||
namespace PluralKit.Bot;
|
||||
|
||||
// corresponds to the ffi Paramater type, but with stricter types (also avoiding exposing ffi types!)
|
||||
public abstract record Parameter()
|
||||
{
|
||||
public record MemberRef(PKMember member): Parameter;
|
||||
public record SystemRef(PKSystem system): Parameter;
|
||||
public record MemberPrivacyTarget(MemberPrivacySubject target): Parameter;
|
||||
public record PrivacyLevel(string level): Parameter;
|
||||
public record Toggle(bool value): Parameter;
|
||||
public record Opaque(string value): Parameter;
|
||||
public record Reset(): Parameter;
|
||||
}
|
||||
|
||||
public class Parameters
|
||||
{
|
||||
private string _cb { get; init; }
|
||||
private List<string> _args { get; init; }
|
||||
private Dictionary<string, string?> _flags { get; init; }
|
||||
private Dictionary<string, Parameter> _params { get; init; }
|
||||
private Dictionary<string, uniffi.commands.Parameter> _params { get; init; }
|
||||
|
||||
// just used for errors, temporarily
|
||||
public string FullCommand { get; init; }
|
||||
|
|
@ -31,68 +44,61 @@ public class Parameters
|
|||
}
|
||||
}
|
||||
|
||||
public async Task<ResolvedParameters> ResolveParameters(Context ctx)
|
||||
{
|
||||
var parsed_members = await MemberParams().ToAsyncEnumerable().ToDictionaryAwaitAsync(async item => item.Key, async item =>
|
||||
await ctx.ParseMember(this, item.Value) ?? throw new PKError(ctx.CreateNotFoundError(this, "Member", item.Value))
|
||||
);
|
||||
var parsed_systems = await SystemParams().ToAsyncEnumerable().ToDictionaryAwaitAsync(async item => item.Key, async item =>
|
||||
await ctx.ParseSystem(item.Value) ?? throw new PKError(ctx.CreateNotFoundError(this, "System", item.Value))
|
||||
);
|
||||
return new ResolvedParameters(this, parsed_members, parsed_systems);
|
||||
}
|
||||
|
||||
public string Callback()
|
||||
{
|
||||
return _cb;
|
||||
}
|
||||
|
||||
public IDictionary<string, string> Flags()
|
||||
public bool HasFlag(params string[] potentialMatches)
|
||||
{
|
||||
return _flags;
|
||||
return potentialMatches.Any(_flags.ContainsKey);
|
||||
}
|
||||
|
||||
private Dictionary<string, string> Params(Func<ParameterKind, bool> filter)
|
||||
// resolves a single parameter
|
||||
private async Task<Parameter?> ResolveParameter(Context ctx, string param_name)
|
||||
{
|
||||
return _params.Where(item => filter(item.Value.@kind)).ToDictionary(item => item.Key, item => item.Value.@raw);
|
||||
if (!_params.ContainsKey(param_name)) return null;
|
||||
switch (_params[param_name])
|
||||
{
|
||||
case uniffi.commands.Parameter.MemberRef memberRef:
|
||||
var byId = HasFlag("id", "by-id");
|
||||
return new Parameter.MemberRef(
|
||||
await ctx.ParseMember(memberRef.member, byId)
|
||||
?? throw new PKError(ctx.CreateNotFoundError("Member", memberRef.member, byId))
|
||||
);
|
||||
case uniffi.commands.Parameter.SystemRef systemRef:
|
||||
// todo: do we need byId here?
|
||||
return new Parameter.SystemRef(
|
||||
await ctx.ParseSystem(systemRef.system)
|
||||
?? throw new PKError(ctx.CreateNotFoundError("System", systemRef.system))
|
||||
);
|
||||
case uniffi.commands.Parameter.MemberPrivacyTarget memberPrivacyTarget:
|
||||
// this should never really fail...
|
||||
// todo: we shouldn't have *three* different MemberPrivacyTarget types (rust, ffi, c#) syncing the cases will be annoying...
|
||||
if (!MemberPrivacyUtils.TryParseMemberPrivacy(memberPrivacyTarget.target, out var target))
|
||||
throw new PKError($"Invalid member privacy target {memberPrivacyTarget.target}");
|
||||
return new Parameter.MemberPrivacyTarget(target);
|
||||
case uniffi.commands.Parameter.PrivacyLevel privacyLevel:
|
||||
return new Parameter.PrivacyLevel(privacyLevel.level);
|
||||
case uniffi.commands.Parameter.Toggle toggle:
|
||||
return new Parameter.Toggle(toggle.toggle);
|
||||
case uniffi.commands.Parameter.OpaqueString opaque:
|
||||
return new Parameter.Opaque(opaque.raw);
|
||||
case uniffi.commands.Parameter.Reset _:
|
||||
return new Parameter.Reset();
|
||||
}
|
||||
// this should also never happen
|
||||
throw new PKError($"Unknown parameter type for parameter {param_name}");
|
||||
}
|
||||
|
||||
public IDictionary<string, string> Params()
|
||||
public async Task<T> ResolveParameter<T>(Context ctx, string param_name, Func<Parameter, T?> extract_func)
|
||||
{
|
||||
return Params(_ => true);
|
||||
var param = await ResolveParameter(ctx, param_name);
|
||||
// todo: i think this should return null for everything...?
|
||||
if (param == null) return default;
|
||||
return extract_func(param)
|
||||
// this should never really happen (hopefully!), but in case the parameter names dont match up (typos...) between rust <-> c#...
|
||||
// (it would be very cool to have this statically checked somehow..?)
|
||||
?? throw new PKError($"Parameter {param_name.AsCode()} was not found for command {Callback().AsCode()} -- this is a bug!!");
|
||||
}
|
||||
|
||||
public IDictionary<string, string> MemberParams()
|
||||
{
|
||||
return Params(kind => kind == ParameterKind.MemberRef);
|
||||
}
|
||||
|
||||
public IDictionary<string, string> SystemParams()
|
||||
{
|
||||
return Params(kind => kind == ParameterKind.SystemRef);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: im not really sure if this should be the way to go
|
||||
public class ResolvedParameters
|
||||
{
|
||||
public readonly Parameters Raw;
|
||||
public readonly Dictionary<string, PKMember> MemberParams;
|
||||
public readonly Dictionary<string, PKSystem> SystemParams;
|
||||
|
||||
public ResolvedParameters(Parameters parameters, Dictionary<string, PKMember> member_params, Dictionary<string, PKSystem> system_params)
|
||||
{
|
||||
Raw = parameters;
|
||||
MemberParams = member_params;
|
||||
SystemParams = system_params;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: move this to another file (?)
|
||||
public static class ParametersExt
|
||||
{
|
||||
public static bool HasFlag(this Parameters parameters, params string[] potentialMatches)
|
||||
{
|
||||
return potentialMatches.Any(parameters.Flags().ContainsKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -190,17 +190,17 @@ public class Config
|
|||
}
|
||||
private string EnabledDisabled(bool value) => value ? "enabled" : "disabled";
|
||||
|
||||
public async Task AutoproxyAccount(Context ctx)
|
||||
public async Task ViewAutoproxyAccount(Context ctx)
|
||||
{
|
||||
var allowAutoproxy = await ctx.Repository.GetAutoproxyEnabled(ctx.Author.Id);
|
||||
|
||||
if (!ctx.HasNext())
|
||||
{
|
||||
await ctx.Reply($"Autoproxy is currently **{EnabledDisabled(allowAutoproxy)}** for account <@{ctx.Author.Id}>.");
|
||||
return;
|
||||
}
|
||||
await ctx.Reply($"Autoproxy is currently **{EnabledDisabled(allowAutoproxy)}** for account <@{ctx.Author.Id}>.");
|
||||
}
|
||||
|
||||
var allow = ctx.MatchToggle(true);
|
||||
public async Task EditAutoproxyAccount(Context ctx)
|
||||
{
|
||||
var allowAutoproxy = await ctx.Repository.GetAutoproxyEnabled(ctx.Author.Id);
|
||||
var allow = await ctx.ParamResolveToggle("toggle") ?? throw new PKSyntaxError("You need to specify whether to enable or disable autoproxy for this account.");
|
||||
|
||||
var statusString = EnabledDisabled(allow);
|
||||
if (allowAutoproxy == allow)
|
||||
|
|
@ -213,31 +213,34 @@ public class Config
|
|||
await ctx.Reply($"{Emojis.Success} Autoproxy {statusString} for account <@{ctx.Author.Id}>.");
|
||||
}
|
||||
|
||||
|
||||
public async Task AutoproxyTimeout(Context ctx)
|
||||
public async Task ViewAutoproxyTimeout(Context ctx)
|
||||
{
|
||||
if (!ctx.HasNext())
|
||||
{
|
||||
var timeout = ctx.Config.LatchTimeout.HasValue
|
||||
? Duration.FromSeconds(ctx.Config.LatchTimeout.Value)
|
||||
: (Duration?)null;
|
||||
var timeout = ctx.Config.LatchTimeout.HasValue
|
||||
? Duration.FromSeconds(ctx.Config.LatchTimeout.Value)
|
||||
: (Duration?)null;
|
||||
|
||||
if (timeout == null)
|
||||
await ctx.Reply($"You do not have a custom autoproxy timeout duration set. The default latch timeout duration is {ProxyMatcher.DefaultLatchExpiryTime.ToTimeSpan().Humanize(4)}.");
|
||||
else if (timeout == Duration.Zero)
|
||||
await ctx.Reply("Latch timeout is currently **disabled** for your system. Latch mode autoproxy will never time out.");
|
||||
else
|
||||
await ctx.Reply($"The current latch timeout duration for your system is {timeout.Value.ToTimeSpan().Humanize(4)}.");
|
||||
return;
|
||||
}
|
||||
if (timeout == null)
|
||||
await ctx.Reply($"You do not have a custom autoproxy timeout duration set. The default latch timeout duration is {ProxyMatcher.DefaultLatchExpiryTime.ToTimeSpan().Humanize(4)}.");
|
||||
else if (timeout == Duration.Zero)
|
||||
await ctx.Reply("Latch timeout is currently **disabled** for your system. Latch mode autoproxy will never time out.");
|
||||
else
|
||||
await ctx.Reply($"The current latch timeout duration for your system is {timeout.Value.ToTimeSpan().Humanize(4)}.");
|
||||
}
|
||||
|
||||
public async Task EditAutoproxyTimeout(Context ctx)
|
||||
{
|
||||
var _newTimeout = await ctx.ParamResolveOpaque("timeout");
|
||||
var _reset = await ctx.ParamResolveReset("reset");
|
||||
var _toggle = await ctx.ParamResolveToggle("toggle");
|
||||
|
||||
Duration? newTimeout;
|
||||
Duration overflow = Duration.Zero;
|
||||
if (ctx.Match("off", "stop", "cancel", "no", "disable", "remove")) newTimeout = Duration.Zero;
|
||||
else if (ctx.MatchClear()) newTimeout = null;
|
||||
if (_toggle == false) newTimeout = Duration.Zero;
|
||||
else if (_reset == true) newTimeout = null;
|
||||
else
|
||||
{
|
||||
var timeoutStr = ctx.RemainderOrNull();
|
||||
// todo: we should parse date in the command parser
|
||||
var timeoutStr = _newTimeout;
|
||||
var timeoutPeriod = DateUtils.ParsePeriod(timeoutStr);
|
||||
if (timeoutPeriod == null) throw new PKError($"Could not parse '{timeoutStr}' as a valid duration. Try using a syntax such as \"3h5m\" (i.e. 3 hours and 5 minutes).");
|
||||
if (timeoutPeriod.Value.TotalHours > 100000)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using System.Net;
|
||||
using System.Reflection.Metadata;
|
||||
using System.Web;
|
||||
|
||||
using Dapper;
|
||||
|
|
@ -27,8 +28,10 @@ public class Member
|
|||
_avatarHosting = avatarHosting;
|
||||
}
|
||||
|
||||
public async Task NewMember(Context ctx, string memberName)
|
||||
public async Task NewMember(Context ctx)
|
||||
{
|
||||
var memberName = await ctx.ParamResolveOpaque("name");
|
||||
|
||||
if (ctx.System == null) throw Errors.NoSystemError(ctx.DefaultPrefix);
|
||||
memberName = memberName ?? throw new PKSyntaxError("You must pass a member name.");
|
||||
|
||||
|
|
@ -124,17 +127,19 @@ public class Member
|
|||
$"{Emojis.Warn} You are approaching the per-system member limit ({memberCount} / {memberLimit} members). Once you reach this limit, you will be unable to create new members until existing members are deleted, or you can ask for your limit to be raised in the PluralKit support server: <https://discord.gg/PczBt78>");
|
||||
}
|
||||
|
||||
public async Task ViewMember(Context ctx, PKMember target)
|
||||
public async Task ViewMember(Context ctx)
|
||||
{
|
||||
var target = await ctx.ParamResolveMember("target");
|
||||
var system = await ctx.Repository.GetSystem(target.System);
|
||||
await ctx.Reply(
|
||||
embed: await _embeds.CreateMemberEmbed(system, target, ctx.Guild, ctx.Config, ctx.LookupContextFor(system.Id), ctx.Zone));
|
||||
}
|
||||
|
||||
public async Task Soulscream(Context ctx, PKMember target)
|
||||
public async Task Soulscream(Context ctx)
|
||||
{
|
||||
// this is for a meme, please don't take this code seriously. :)
|
||||
|
||||
var target = await ctx.ParamResolveMember("target");
|
||||
var name = target.NameFor(ctx.LookupContextFor(target.System));
|
||||
var encoded = HttpUtility.UrlEncode(name);
|
||||
|
||||
|
|
|
|||
|
|
@ -137,23 +137,29 @@ public class MessageCreated: IEventHandler<MessageCreateEvent>
|
|||
var system = await _repo.GetSystemByAccount(evt.Author.Id);
|
||||
var config = system != null ? await _repo.GetSystemConfig(system.Id) : null;
|
||||
var guildConfig = guild != null ? await _repo.GetGuild(guild.Id) : null;
|
||||
var ctx = new Context(_services, shardId, guild, channel, evt, cmdStart, system, config, guildConfig, _config.Prefixes ?? BotConfig.DefaultPrefixes);
|
||||
|
||||
// parse parameters
|
||||
Parameters parameters;
|
||||
try
|
||||
{
|
||||
var parameters = new Parameters(evt.Content?.Substring(cmdStart));
|
||||
var resolved_parameters = await parameters.ResolveParameters(ctx);
|
||||
await _tree.ExecuteCommand(ctx, resolved_parameters);
|
||||
parameters = new Parameters(evt.Content?.Substring(cmdStart));
|
||||
}
|
||||
catch (PKError e)
|
||||
{
|
||||
// don't send an "invalid command" response if the guild has those turned off
|
||||
// TODO: only dont send command not found, not every parse error (eg. missing params, syntax error...)
|
||||
if (!(ctx.GuildConfig != null && ctx.GuildConfig!.InvalidCommandResponseEnabled != true))
|
||||
if (!(guildConfig != null && guildConfig!.InvalidCommandResponseEnabled != true))
|
||||
{
|
||||
await ctx.Reply($"{Emojis.Error} {e.Message}");
|
||||
await _rest.CreateMessage(channel.Id, new MessageRequest
|
||||
{
|
||||
Content = $"{Emojis.Error} {e.Message}",
|
||||
});
|
||||
}
|
||||
throw;
|
||||
}
|
||||
|
||||
var ctx = new Context(_services, shardId, guild, channel, evt, cmdStart, system, config, guildConfig, _config.Prefixes ?? BotConfig.DefaultPrefixes, parameters);
|
||||
await _tree.ExecuteCommand(ctx);
|
||||
}
|
||||
catch (PKError)
|
||||
{
|
||||
|
|
@ -212,4 +218,4 @@ public class MessageCreated: IEventHandler<MessageCreateEvent>
|
|||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue