bot: add system deletion command

This commit is contained in:
Ske 2019-04-26 18:15:25 +02:00
parent b5d87290db
commit 876dcc0145
3 changed files with 51 additions and 23 deletions

View File

@ -94,9 +94,12 @@ namespace PluralKit.Bot
await _commands.AddModulesAsync(Assembly.GetEntryAssembly(), _services);
_client.Ready += Ready;
_client.MessageReceived += MessageReceived;
_client.ReactionAdded += _proxy.HandleReactionAddedAsync;
_client.MessageDeleted += _proxy.HandleMessageDeletedAsync;
// Deliberately wrapping in an async function *without* awaiting, we don't want to "block" since this'd hold up the main loop
// These handlers return Task so we gotta be careful not to return the Task itself (which would then be awaited) - kinda weird design but eh
_client.MessageReceived += async (msg) => MessageReceived(msg);
_client.ReactionAdded += async (message, channel, reaction) => _proxy.HandleReactionAddedAsync(message, channel, reaction);
_client.MessageDeleted += async (message, channel) => _proxy.HandleMessageDeletedAsync(message, channel);
}
private async Task UpdatePeriodic()
@ -118,9 +121,11 @@ namespace PluralKit.Bot
if (!_result.IsSuccess) {
// If this is a PKError (ie. thrown deliberately), show user facing message
// If not, log as error
var pkError = (_result as ExecuteResult?)?.Exception as PKError;
if (pkError != null) {
await ctx.Message.Channel.SendMessageAsync("\u274C " + pkError.Message);
var exception = (_result as ExecuteResult?)?.Exception;
if (exception is PKError) {
await ctx.Message.Channel.SendMessageAsync($"{Emojis.Error} {exception.Message}");
} else if (exception is TimeoutException) {
await ctx.Message.Channel.SendMessageAsync($"{Emojis.Error} Operation timed out. Try being faster");
} else {
HandleRuntimeError(ctx.Message as SocketMessage, (_result as ExecuteResult?)?.Exception);
}

View File

@ -30,7 +30,7 @@ namespace PluralKit.Bot.Commands
public async Task New([Remainder] string systemName = null)
{
if (ContextEntity != null) throw OTHER_SYSTEM_CONTEXT_ERROR;
if (Context.SenderSystem != null) throw new PKError("You already have a system registered with PluralKit. To view it, type `pk;system`. If you'd like to delete your system and start anew, type `pk;system delete`, or if you'd like to unlink this account from it, type `pk;unlink.");
if (Context.SenderSystem != null) throw new PKError("You already have a system registered with PluralKit. To view it, type `pk;system`. If you'd like to delete your system and start anew, type `pk;system delete`, or if you'd like to unlink this account from it, type `pk;unlink`.");
var system = await Systems.Create(systemName);
await Systems.Link(system, Context.User.Id);
@ -79,6 +79,19 @@ namespace PluralKit.Bot.Commands
await Context.Channel.SendMessageAsync($"{Emojis.Success} System tag {(newTag != null ? "changed" : "cleared")}.");
}
[Command("delete")]
public async Task Delete() {
if (ContextEntity != null) throw OTHER_SYSTEM_CONTEXT_ERROR;
if (Context.SenderSystem == null) throw NO_SYSTEM_ERROR;
var msg = await Context.Channel.SendMessageAsync($"{Emojis.Warn} Are you sure you want to delete your system? If so, reply to this message with your system's ID (`{Context.SenderSystem.Hid}`).\n**Note: this action is permanent.**");
var reply = await Context.AwaitMessage(Context.Channel, Context.User, timeout: TimeSpan.FromMinutes(1));
if (reply.Content != Context.SenderSystem.Hid) throw new PKError($"System deletion cancelled. Note that you must reply with your system ID (`{Context.SenderSystem.Hid}`) *verbatim*.");
await Systems.Delete(Context.SenderSystem);
await Context.Channel.SendMessageAsync($"{Emojis.Success} System deleted.");
}
public override async Task<PKSystem> ReadContextParameterAsync(string value)
{
var res = await new PKSystemTypeReader().ReadAsync(Context, value, _services);

View File

@ -162,33 +162,43 @@ namespace PluralKit.Bot
public static class ContextExt {
public static async Task<bool> PromptYesNo(this ICommandContext ctx, IMessage message, TimeSpan? timeout = null) {
await ctx.Message.AddReactionsAsync(new[] {new Emoji(Emojis.Success), new Emoji(Emojis.Error)});
var reaction = await ctx.WaitForReaction(ctx.Message, message.Author, (r) => r.Emote.Name == Emojis.Success || r.Emote.Name == Emojis.Error);
var reaction = await ctx.AwaitReaction(ctx.Message, message.Author, (r) => r.Emote.Name == Emojis.Success || r.Emote.Name == Emojis.Error, timeout);
return reaction.Emote.Name == Emojis.Success;
}
public static async Task<SocketReaction> WaitForReaction(this ICommandContext ctx, IUserMessage message, IUser user = null, Func<SocketReaction, bool> predicate = null, TimeSpan? timeout = null) {
public static async Task<SocketReaction> AwaitReaction(this ICommandContext ctx, IUserMessage message, IUser user = null, Func<SocketReaction, bool> predicate = null, TimeSpan? timeout = null) {
var tcs = new TaskCompletionSource<SocketReaction>();
Task Inner(Cacheable<IUserMessage, ulong> _message, ISocketMessageChannel _channel, SocketReaction reaction) {
// Ignore reactions for different messages
if (message.Id != _message.Id) return Task.CompletedTask;
// Ignore messages from other users if a user was defined
if (user != null && user.Id != reaction.UserId) return Task.CompletedTask;
// Check the predicate, if true - accept the reaction
if (predicate?.Invoke(reaction) ?? true) {
tcs.SetResult(reaction);
}
if (message.Id != _message.Id) return Task.CompletedTask; // Ignore reactions for different messages
if (user != null && user.Id != reaction.UserId) return Task.CompletedTask; // Ignore messages from other users if a user was defined
if (predicate != null && !predicate.Invoke(reaction)) return Task.CompletedTask; // Check predicate
tcs.SetResult(reaction);
return Task.CompletedTask;
}
(ctx as BaseSocketClient).ReactionAdded += Inner;
(ctx.Client as BaseSocketClient).ReactionAdded += Inner;
try {
return await (tcs.Task.TimeoutAfter(timeout));
} finally {
(ctx as BaseSocketClient).ReactionAdded -= Inner;
(ctx.Client as BaseSocketClient).ReactionAdded -= Inner;
}
}
public static async Task<IUserMessage> AwaitMessage(this ICommandContext ctx, IMessageChannel channel, IUser user = null, Func<SocketMessage, bool> predicate = null, TimeSpan? timeout = null) {
var tcs = new TaskCompletionSource<IUserMessage>();
Task Inner(SocketMessage msg) {
if (channel != msg.Channel) return Task.CompletedTask; // Ignore messages in a different channel
if (user != null && user != msg.Author) return Task.CompletedTask; // Ignore messages from other users
if (predicate != null && !predicate.Invoke(msg)) return Task.CompletedTask; // Check predicate
tcs.SetResult(msg as IUserMessage);
return Task.CompletedTask;
}
(ctx.Client as BaseSocketClient).MessageReceived += Inner;
try {
return await (tcs.Task.TimeoutAfter(timeout));
} finally {
(ctx.Client as BaseSocketClient).MessageReceived -= Inner;
}
}
}