feat: proxy debug command
Co-authored-by: Spectralitree <72747870+Spectralitree@users.noreply.github.com>
This commit is contained in:
parent
d8458c0846
commit
b9f73cadb7
@ -71,25 +71,26 @@ namespace PluralKit.Bot
|
||||
return matched;
|
||||
}
|
||||
|
||||
public static ulong? MatchMessage(this Context ctx, bool parseRawMessageId)
|
||||
public static (ulong? messageId, ulong? channelId) MatchMessage(this Context ctx, bool parseRawMessageId)
|
||||
{
|
||||
if (ctx.Message.Type == Message.MessageType.Reply && ctx.Message.MessageReference != null)
|
||||
return ctx.Message.MessageReference.MessageId;
|
||||
if (ctx.Message.Type == Message.MessageType.Reply && ctx.Message.MessageReference?.MessageId != null)
|
||||
return (ctx.Message.MessageReference.MessageId, ctx.Message.MessageReference.ChannelId);
|
||||
|
||||
var word = ctx.PeekArgument();
|
||||
if (word == null)
|
||||
return null;
|
||||
return (null, null);
|
||||
|
||||
if (parseRawMessageId && ulong.TryParse(word, out var mid))
|
||||
return mid;
|
||||
return (mid, null);
|
||||
|
||||
var match = Regex.Match(word, "https://(?:\\w+.)?discord(?:app)?.com/channels/\\d+/\\d+/(\\d+)");
|
||||
var match = Regex.Match(word, "https://(?:\\w+.)?discord(?:app)?.com/channels/\\d+/(\\d+)/(\\d+)");
|
||||
if (!match.Success)
|
||||
return null;
|
||||
return (null, null);
|
||||
|
||||
var messageId = ulong.Parse(match.Groups[1].Value);
|
||||
var channelId = ulong.Parse(match.Groups[1].Value);
|
||||
var messageId = ulong.Parse(match.Groups[2].Value);
|
||||
ctx.PopArgument();
|
||||
return messageId;
|
||||
return (messageId, channelId);
|
||||
}
|
||||
|
||||
public static async Task<List<PKMember>> ParseMemberList(this Context ctx, SystemId? restrictToSystem)
|
||||
|
@ -84,6 +84,7 @@ namespace PluralKit.Bot
|
||||
public static Command Explain = new Command("explain", "explain", "Explains the basics of systems and proxying");
|
||||
public static Command Message = new Command("message", "message <id|link> [delete|author]", "Looks up a proxied message");
|
||||
public static Command MessageEdit = new Command("edit", "edit [link] <text>", "Edit a previously proxied message");
|
||||
public static Command ProxyCheck = new Command("proxycheck", "proxycheck [link]", "Checks why your message has not been proxied");
|
||||
public static Command LogChannel = new Command("log channel", "log channel <channel>", "Designates a channel to post proxied messages to");
|
||||
public static Command LogChannelClear = new Command("log channel", "log channel -clear", "Clears the currently set log channel");
|
||||
public static Command LogEnable = new Command("log enable", "log enable all|<channel> [channel 2] [channel 3...]", "Enables message logging in certain channels");
|
||||
@ -191,7 +192,10 @@ namespace PluralKit.Bot
|
||||
return PrintCommandList(ctx, "channel blacklisting", BlacklistCommands);
|
||||
else return PrintCommandExpectedError(ctx, BlacklistCommands);
|
||||
if (ctx.Match("proxy"))
|
||||
return ctx.Execute<SystemEdit>(SystemProxy, m => m.SystemProxy(ctx));
|
||||
if (ctx.Match("debug"))
|
||||
return ctx.Execute<Misc>(ProxyCheck, m => m.MessageProxyCheck(ctx));
|
||||
else
|
||||
return ctx.Execute<SystemEdit>(SystemProxy, m => m.SystemProxy(ctx));
|
||||
if (ctx.Match("invite")) return ctx.Execute<Misc>(Invite, m => m.Invite(ctx));
|
||||
if (ctx.Match("mn")) return ctx.Execute<Fun>(null, m => m.Mn(ctx));
|
||||
if (ctx.Match("fire")) return ctx.Execute<Fun>(null, m => m.Fire(ctx));
|
||||
@ -202,6 +206,10 @@ namespace PluralKit.Bot
|
||||
if (ctx.Match("stats")) return ctx.Execute<Misc>(null, m => m.Stats(ctx));
|
||||
if (ctx.Match("permcheck"))
|
||||
return ctx.Execute<Misc>(PermCheck, m => m.PermCheckGuild(ctx));
|
||||
if (ctx.Match("proxycheck"))
|
||||
return ctx.Execute<Misc>(ProxyCheck, m => m.MessageProxyCheck(ctx));
|
||||
if (ctx.Match("debug"))
|
||||
return HandleDebugCommand(ctx);
|
||||
if (ctx.Match("admin"))
|
||||
return HandleAdminCommand(ctx);
|
||||
if (ctx.Match("random", "r"))
|
||||
@ -231,6 +239,20 @@ namespace PluralKit.Bot
|
||||
await ctx.Reply($"{Emojis.Error} Unknown command.");
|
||||
}
|
||||
|
||||
private async Task HandleDebugCommand(Context ctx)
|
||||
{
|
||||
var availableCommandsStr = "Available debug targets: `permissions`, `proxying`";
|
||||
|
||||
if (ctx.Match("permissions", "perms", "permcheck"))
|
||||
await ctx.Execute<Misc>(PermCheck, m => m.PermCheckGuild(ctx));
|
||||
else if (ctx.Match("proxy", "proxying", "proxycheck"))
|
||||
await ctx.Execute<Misc>(ProxyCheck, m => m.MessageProxyCheck(ctx));
|
||||
else if (!ctx.HasNext())
|
||||
await ctx.Reply($"{Emojis.Error} You need to pass a command. {availableCommandsStr}");
|
||||
else
|
||||
await ctx.Reply($"{Emojis.Error} Unknown debug command {ctx.PeekArgument().AsCode()}. {availableCommandsStr}");
|
||||
}
|
||||
|
||||
private async Task HandleSystemCommand(Context ctx)
|
||||
{
|
||||
// If we have no parameters, default to self-target
|
||||
|
@ -75,7 +75,7 @@ namespace PluralKit.Bot
|
||||
await using var conn = await _db.Obtain();
|
||||
FullMessage? msg = null;
|
||||
|
||||
var referencedMessage = ctx.MatchMessage(false);
|
||||
var (referencedMessage, _) = ctx.MatchMessage(false);
|
||||
if (referencedMessage != null)
|
||||
{
|
||||
msg = await _repo.GetMessage(conn, referencedMessage.Value);
|
||||
|
@ -17,6 +17,7 @@ using Myriad.Cache;
|
||||
using Myriad.Extensions;
|
||||
using Myriad.Gateway;
|
||||
using Myriad.Rest;
|
||||
using Myriad.Rest.Exceptions;
|
||||
using Myriad.Rest.Types.Requests;
|
||||
using Myriad.Types;
|
||||
|
||||
@ -34,8 +35,11 @@ namespace PluralKit.Bot {
|
||||
private readonly DiscordApiClient _rest;
|
||||
private readonly Cluster _cluster;
|
||||
private readonly Bot _bot;
|
||||
private readonly ProxyService _proxy;
|
||||
private readonly ProxyMatcher _matcher;
|
||||
|
||||
public Misc(BotConfig botConfig, IMetrics metrics, CpuStatService cpu, ShardInfoService shards, EmbedService embeds, ModelRepository repo, IDatabase db, IDiscordCache cache, DiscordApiClient rest, Bot bot, Cluster cluster)
|
||||
public Misc(BotConfig botConfig, IMetrics metrics, CpuStatService cpu, ShardInfoService shards, EmbedService embeds, ModelRepository repo,
|
||||
IDatabase db, IDiscordCache cache, DiscordApiClient rest, Bot bot, Cluster cluster, ProxyService proxy, ProxyMatcher matcher)
|
||||
{
|
||||
_botConfig = botConfig;
|
||||
_metrics = metrics;
|
||||
@ -48,6 +52,8 @@ namespace PluralKit.Bot {
|
||||
_rest = rest;
|
||||
_bot = bot;
|
||||
_cluster = cluster;
|
||||
_proxy = proxy;
|
||||
_matcher = matcher;
|
||||
}
|
||||
|
||||
public async Task Invite(Context ctx)
|
||||
@ -218,7 +224,7 @@ namespace PluralKit.Bot {
|
||||
|
||||
public async Task GetMessage(Context ctx)
|
||||
{
|
||||
var messageId = ctx.MatchMessage(true);
|
||||
var (messageId, _) = ctx.MatchMessage(true);
|
||||
if (messageId == null)
|
||||
{
|
||||
if (!ctx.HasNext())
|
||||
@ -250,5 +256,69 @@ namespace PluralKit.Bot {
|
||||
|
||||
await ctx.Reply(embed: await _embeds.CreateMessageInfoEmbed(message));
|
||||
}
|
||||
|
||||
public async Task MessageProxyCheck(Context ctx)
|
||||
{
|
||||
if (!ctx.HasNext() && ctx.Message.MessageReference == null)
|
||||
throw new PKError("You need to specify a message.");
|
||||
|
||||
var failedToGetMessage = "Could not find a valid message to check, was not able to fetch the message, or the message was not sent by you.";
|
||||
|
||||
var (messageId, channelId) = ctx.MatchMessage(false);
|
||||
if (messageId == null || channelId == null)
|
||||
throw new PKError(failedToGetMessage);
|
||||
|
||||
await using var conn = await _db.Obtain();
|
||||
|
||||
var proxiedMsg = await _repo.GetMessage(conn, messageId.Value);
|
||||
if (proxiedMsg != null)
|
||||
{
|
||||
await ctx.Reply($"{Emojis.Success} This message was proxied successfully.");
|
||||
return;
|
||||
}
|
||||
|
||||
// get the message info
|
||||
var msg = ctx.Message;
|
||||
try
|
||||
{
|
||||
msg = await _rest.GetMessage(channelId.Value, messageId.Value);
|
||||
}
|
||||
catch (ForbiddenException)
|
||||
{
|
||||
throw new PKError(failedToGetMessage);
|
||||
}
|
||||
|
||||
// if user is fetching a message in a different channel sent by someone else, throw a generic error message
|
||||
if (msg == null || (msg.Author.Id != ctx.Author.Id && msg.ChannelId != ctx.Channel.Id))
|
||||
throw new PKError(failedToGetMessage);
|
||||
|
||||
if ((_botConfig.Prefixes ?? BotConfig.DefaultPrefixes).Any(p => msg.Content.StartsWith(p)))
|
||||
throw new PKError("This message starts with the bot's prefix, and was parsed as a command.");
|
||||
if (msg.WebhookId != null)
|
||||
throw new PKError("You cannot check messages sent by a webhook.");
|
||||
if (msg.Author.Id != ctx.Author.Id)
|
||||
throw new PKError("You can only check your own messages.");
|
||||
|
||||
// get the channel info
|
||||
var channel = _cache.GetChannel(channelId.Value);
|
||||
if (channel == null)
|
||||
throw new PKError("Unable to get the channel associated with this message.");
|
||||
|
||||
// using channel.GuildId here since _rest.GetMessage() doesn't return the GuildId
|
||||
var context = await _repo.GetMessageContext(conn, msg.Author.Id, channel.GuildId.Value, msg.ChannelId);
|
||||
var members = (await _repo.GetProxyMembers(conn, msg.Author.Id, channel.GuildId.Value)).ToList();
|
||||
|
||||
// Run everything through the checks, catch the ProxyCheckFailedException, and reply with the error message.
|
||||
try
|
||||
{
|
||||
_proxy.ShouldProxy(channel, msg, context);
|
||||
_matcher.TryMatch(context, members, out var match, msg.Content, msg.Attachments.Length > 0, context.AllowAutoproxy);
|
||||
|
||||
await ctx.Reply("I'm not sure why this message was not proxied, sorry.");
|
||||
} catch (ProxyService.ProxyChecksFailedException e)
|
||||
{
|
||||
await ctx.Reply($"{e.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -154,6 +154,10 @@ namespace PluralKit.Bot
|
||||
{
|
||||
return await _proxy.HandleIncomingMessage(shard, evt, ctx, guild, channel, allowAutoproxy: ctx.AllowAutoproxy, botPermissions);
|
||||
}
|
||||
|
||||
// Catch any failed proxy checks so they get ignored in the global error handler
|
||||
catch (ProxyService.ProxyChecksFailedException) {}
|
||||
|
||||
catch (PKError e)
|
||||
{
|
||||
// User-facing errors, print to the channel properly formatted
|
||||
|
@ -45,7 +45,7 @@ namespace PluralKit.Bot
|
||||
|
||||
// Skip autoproxy match if we hit the escape character
|
||||
if (messageContent.StartsWith(AutoproxyEscapeCharacter))
|
||||
return false;
|
||||
throw new ProxyService.ProxyChecksFailedException("This message matches none of your proxy tags, and it was not autoproxied because it starts with a backslash (`\\`).");
|
||||
|
||||
// Find the member we should autoproxy (null if none)
|
||||
var member = ctx.AutoproxyMode switch
|
||||
@ -56,13 +56,30 @@ namespace PluralKit.Bot
|
||||
AutoproxyMode.Front when ctx.LastSwitchMembers.Length > 0 =>
|
||||
members.FirstOrDefault(m => m.Id == ctx.LastSwitchMembers[0]),
|
||||
|
||||
AutoproxyMode.Latch when ctx.LastMessageMember != null && !IsLatchExpired(ctx) =>
|
||||
AutoproxyMode.Latch when ctx.LastMessageMember != null =>
|
||||
members.FirstOrDefault(m => m.Id == ctx.LastMessageMember.Value),
|
||||
|
||||
_ => null
|
||||
};
|
||||
// Throw an error if the member is null, message varies depending on autoproxy mode
|
||||
if (member == null)
|
||||
{
|
||||
if (ctx.AutoproxyMode == AutoproxyMode.Front)
|
||||
throw new ProxyService.ProxyChecksFailedException("You are using autoproxy front, but no members are currently registered as fronting. Please use `pk;switch <member>` to log a new switch.");
|
||||
else if (ctx.AutoproxyMode == AutoproxyMode.Member)
|
||||
throw new ProxyService.ProxyChecksFailedException("You are using member-specific autoproxy with an invalid member. Was this member deleted?");
|
||||
else if (ctx.AutoproxyMode == AutoproxyMode.Latch)
|
||||
throw new ProxyService.ProxyChecksFailedException("You are using autoproxy latch, but have not sent any messages yet in this server. Please send a message using proxy tags first.");
|
||||
throw new ProxyService.ProxyChecksFailedException("This message matches none of your proxy tags and autoproxy is not enabled.");
|
||||
}
|
||||
|
||||
if (ctx.AutoproxyMode != AutoproxyMode.Member && !member.AllowAutoproxy)
|
||||
throw new ProxyService.ProxyChecksFailedException("This member has autoproxy disabled. To enable it, use `pk;m <member> autoproxy on`.");
|
||||
|
||||
// Moved the IsLatchExpired() check to here, so that an expired latch and a latch without any previous messages throw different errors
|
||||
if (ctx.AutoproxyMode == AutoproxyMode.Latch && IsLatchExpired(ctx))
|
||||
throw new ProxyService.ProxyChecksFailedException("Latch-mode autoproxy has timed out. Please send a new message using proxy tags.");
|
||||
|
||||
if (member == null || (ctx.AutoproxyMode != AutoproxyMode.Member && !member.AllowAutoproxy)) return false;
|
||||
match = new ProxyMatch
|
||||
{
|
||||
Content = messageContent,
|
||||
|
@ -89,24 +89,34 @@ namespace PluralKit.Bot
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool ShouldProxy(Channel channel, Message msg, MessageContext ctx)
|
||||
public bool ShouldProxy(Channel channel, Message msg, MessageContext ctx)
|
||||
{
|
||||
// Make sure author has a system
|
||||
if (ctx.SystemId == null) return false;
|
||||
if (ctx.SystemId == null)
|
||||
throw new ProxyChecksFailedException(Errors.NoSystemError.Message);
|
||||
|
||||
// Make sure channel is a guild text channel and this is a normal message
|
||||
if (!DiscordUtils.IsValidGuildChannel(channel)) return false;
|
||||
if (msg.Type != Message.MessageType.Default && msg.Type != Message.MessageType.Reply) return false;
|
||||
if (!DiscordUtils.IsValidGuildChannel(channel))
|
||||
throw new ProxyChecksFailedException("This channel is not a text channel.");
|
||||
if (msg.Type != Message.MessageType.Default && msg.Type != Message.MessageType.Reply)
|
||||
throw new ProxyChecksFailedException("This message is not a normal message.");
|
||||
|
||||
// Make sure author is a normal user
|
||||
if (msg.Author.System == true || msg.Author.Bot || msg.WebhookId != null) return false;
|
||||
if (msg.Author.System == true || msg.Author.Bot || msg.WebhookId != null)
|
||||
throw new ProxyChecksFailedException("This message was not sent by a normal user.");
|
||||
|
||||
// Make sure proxying is enabled here
|
||||
if (!ctx.ProxyEnabled || ctx.InBlacklist) return false;
|
||||
if (ctx.InBlacklist)
|
||||
throw new ProxyChecksFailedException($"Proxying was disabled in this channel by a server administrator (via the proxy blacklist).");
|
||||
|
||||
// Make sure the system has proxying enabled in the server
|
||||
if (!ctx.ProxyEnabled)
|
||||
throw new ProxyChecksFailedException("Your system has proxying disabled in this server. Type `pk;proxy on` to enable it.");
|
||||
|
||||
// Make sure we have either an attachment or message content
|
||||
var isMessageBlank = msg.Content == null || msg.Content.Trim().Length == 0;
|
||||
if (isMessageBlank && msg.Attachments.Length == 0) return false;
|
||||
if (isMessageBlank && msg.Attachments.Length == 0)
|
||||
throw new ProxyChecksFailedException("Message cannot be blank.");
|
||||
|
||||
// All good!
|
||||
return true;
|
||||
@ -364,5 +374,9 @@ namespace PluralKit.Bot
|
||||
{
|
||||
if (proxyName.Length > Limits.MaxProxyNameLength) throw Errors.ProxyNameTooLong(proxyName);
|
||||
}
|
||||
public class ProxyChecksFailedException : Exception
|
||||
{
|
||||
public ProxyChecksFailedException(string message) : base(message) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -125,7 +125,8 @@ Some arguments indicate the use of specific Discord features. These include:
|
||||
- `pk;invite` - Sends the bot invite link for PluralKit.
|
||||
- `pk;import` - Imports a data file from PluralKit or Tupperbox.
|
||||
- `pk;export` - Exports a data file containing your system information.
|
||||
- `pk;permcheck [server id]` - [Checks the given server's permission setup](./staff/permissions.md#permission-checker-command) to check if it's compatible with PluralKit.
|
||||
- `pk;debug permissions [server id]` - [Checks the given server's permission setup](./staff/permissions.md#permission-checker-command) to check if it's compatible with PluralKit.
|
||||
- `pk;debug proxying <message link|reply>` - Checks why your message has not been proxied.
|
||||
- `pk;edit [message link|reply] <new content>` - Edits a proxied message. Without an explicit message target, will target the last message proxied by your system in the current channel. **Does not support message IDs!**
|
||||
|
||||
## API
|
||||
|
Loading…
Reference in New Issue
Block a user