bot: finish tag setting command
This commit is contained in:
		| @@ -68,7 +68,6 @@ namespace PluralKit.Bot | ||||
|                 .AddSingleton<MessageStore>() | ||||
|                 .BuildServiceProvider(); | ||||
|     } | ||||
|  | ||||
|     class Bot | ||||
|     { | ||||
|         private IServiceProvider _services; | ||||
| @@ -117,7 +116,14 @@ namespace PluralKit.Bot | ||||
|         private async Task CommandExecuted(Optional<CommandInfo> cmd, ICommandContext ctx, IResult _result) | ||||
|         { | ||||
|             if (!_result.IsSuccess) { | ||||
|                 await ctx.Message.Channel.SendMessageAsync("\u274C " + _result.ErrorReason); | ||||
|                 // 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); | ||||
|                 } else { | ||||
|                     HandleRuntimeError(ctx.Message as SocketMessage, (_result as ExecuteResult?)?.Exception); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -1,4 +1,6 @@ | ||||
| using System; | ||||
| using System.Linq; | ||||
| using System.Runtime.Serialization; | ||||
| using System.Threading.Tasks; | ||||
| using Dapper; | ||||
| using Discord.Commands; | ||||
| @@ -13,67 +15,68 @@ namespace PluralKit.Bot.Commands | ||||
|         public MemberStore Members {get; set;} | ||||
|         public EmbedService EmbedService {get; set;} | ||||
|  | ||||
|         private RuntimeResult NO_SYSTEM_ERROR => PKResult.Error($"You do not have a system registered with PluralKit. To create one, type `pk;system new`. If you already have a system registered on another account, type `pk;link {Context.User.Mention}` from that account to link it here."); | ||||
|         private RuntimeResult OTHER_SYSTEM_CONTEXT_ERROR => PKResult.Error("You can only run this command on your own system."); | ||||
|         private PKError NO_SYSTEM_ERROR => new PKError($"You do not have a system registered with PluralKit. To create one, type `pk;system new`. If you already have a system registered on another account, type `pk;link {Context.User.Mention}` from that account to link it here."); | ||||
|         private PKError OTHER_SYSTEM_CONTEXT_ERROR => new PKError("You can only run this command on your own system."); | ||||
|  | ||||
|         [Command] | ||||
|         public async Task<RuntimeResult> Query(PKSystem system = null) { | ||||
|         public async Task Query(PKSystem system = null) { | ||||
|             if (system == null) system = Context.SenderSystem; | ||||
|             if (system == null) return NO_SYSTEM_ERROR; | ||||
|             if (system == null) throw NO_SYSTEM_ERROR; | ||||
|  | ||||
|             await Context.Channel.SendMessageAsync(embed: await EmbedService.CreateSystemEmbed(system)); | ||||
|             return PKResult.Success(); | ||||
|         } | ||||
|  | ||||
|         [Command("new")] | ||||
|         public async Task<RuntimeResult> New([Remainder] string systemName = null) | ||||
|         public async Task New([Remainder] string systemName = null) | ||||
|         { | ||||
|             if (ContextEntity != null) return OTHER_SYSTEM_CONTEXT_ERROR; | ||||
|             if (Context.SenderSystem != null) return PKResult.Error("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 (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."); | ||||
|  | ||||
|             var system = await Systems.Create(systemName); | ||||
|             await Systems.Link(system, Context.User.Id); | ||||
|  | ||||
|             await ReplyAsync("Your system has been created. Type `pk;system` to view it, and type `pk;help` for more information about commands you can use now."); | ||||
|             return PKResult.Success(); | ||||
|         } | ||||
|  | ||||
|         [Command("name")] | ||||
|         public async Task<RuntimeResult> Name([Remainder] string newSystemName = null) { | ||||
|             if (ContextEntity != null) return OTHER_SYSTEM_CONTEXT_ERROR; | ||||
|             if (Context.SenderSystem == null) return NO_SYSTEM_ERROR; | ||||
|             if (newSystemName != null && newSystemName.Length > 250) return PKResult.Error($"Your chosen system name is too long. ({newSystemName.Length} > 250 characters)"); | ||||
|         public async Task Name([Remainder] string newSystemName = null) { | ||||
|             if (ContextEntity != null) throw OTHER_SYSTEM_CONTEXT_ERROR; | ||||
|             if (Context.SenderSystem == null) throw NO_SYSTEM_ERROR; | ||||
|             if (newSystemName != null && newSystemName.Length > 250) throw new PKError($"Your chosen system name is too long. ({newSystemName.Length} > 250 characters)"); | ||||
|  | ||||
|             Context.SenderSystem.Name = newSystemName; | ||||
|             await Systems.Save(Context.SenderSystem); | ||||
|             return PKResult.Success(); | ||||
|             await Context.Channel.SendMessageAsync($"{Emojis.Success} System name {(newSystemName != null ? "changed" : "cleared")}."); | ||||
|         } | ||||
|  | ||||
|         [Command("description")] | ||||
|         public async Task<RuntimeResult> Description([Remainder] string newDescription = null) { | ||||
|             if (ContextEntity != null) return OTHER_SYSTEM_CONTEXT_ERROR; | ||||
|             if (Context.SenderSystem == null) return NO_SYSTEM_ERROR; | ||||
|             if (newDescription != null && newDescription.Length > 1000) return PKResult.Error($"Your chosen description is too long. ({newDescription.Length} > 250 characters)"); | ||||
|         public async Task Description([Remainder] string newDescription = null) { | ||||
|             if (ContextEntity != null) throw OTHER_SYSTEM_CONTEXT_ERROR; | ||||
|             if (Context.SenderSystem == null) throw NO_SYSTEM_ERROR; | ||||
|             if (newDescription != null && newDescription.Length > 1000) throw new PKError($"Your chosen description is too long. ({newDescription.Length} > 250 characters)"); | ||||
|  | ||||
|             Context.SenderSystem.Description = newDescription; | ||||
|             await Systems.Save(Context.SenderSystem); | ||||
|             return PKResult.Success("uwu"); | ||||
|             await Context.Channel.SendMessageAsync($"{Emojis.Success} System description {(newDescription != null ? "changed" : "cleared")}."); | ||||
|         } | ||||
|  | ||||
|         [Command("tag")] | ||||
|         public async Task<RuntimeResult> Tag([Remainder] string newTag = null) { | ||||
|             if (ContextEntity != null) return OTHER_SYSTEM_CONTEXT_ERROR; | ||||
|             if (Context.SenderSystem == null) return NO_SYSTEM_ERROR; | ||||
|         public async Task Tag([Remainder] string newTag = null) { | ||||
|             if (ContextEntity != null) throw OTHER_SYSTEM_CONTEXT_ERROR; | ||||
|             if (Context.SenderSystem == null) throw NO_SYSTEM_ERROR; | ||||
|             if (newTag.Length > 30) throw new PKError($"Your chosen description is too long. ({newTag.Length} > 30 characters)"); | ||||
|  | ||||
|             Context.SenderSystem.Tag = newTag; | ||||
|  | ||||
|             // Check unproxyable messages *after* changing the tag (so it's seen in the method) but *before* we save to DB (so we can cancel) | ||||
|             var unproxyableMembers = await Members.GetUnproxyableMembers(Context.SenderSystem); | ||||
|             //if (unproxyableMembers.Count > 0) { | ||||
|                 throw new Exception("sdjsdflsdf"); | ||||
|             //} | ||||
|             if (unproxyableMembers.Count > 0) { | ||||
|                 var msg = await Context.Channel.SendMessageAsync($"{Emojis.Warn} Changing your system tag to '{newTag}' will result in the following members being unproxyable, since the tag would bring their name over 32 characters:\n**{string.Join(", ", unproxyableMembers.Select((m) => m.Name))}**\nDo you want to continue anyway?"); | ||||
|                 if (!await Context.PromptYesNo(msg, TimeSpan.FromMinutes(5))) throw new PKError("Tag change cancelled."); | ||||
|             } | ||||
|  | ||||
|             await Systems.Save(Context.SenderSystem); | ||||
|             return PKResult.Success("uwu"); | ||||
|             await Context.Channel.SendMessageAsync($"{Emojis.Success} System tag {(newTag != null ? "changed" : "cleared")}."); | ||||
|         } | ||||
|  | ||||
|         public override async Task<PKSystem> ReadContextParameterAsync(string value) | ||||
|   | ||||
| @@ -159,13 +159,43 @@ namespace PluralKit.Bot | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public class PKResult : RuntimeResult | ||||
|     { | ||||
|         public PKResult(CommandError? error, string reason) : base(error, reason) | ||||
|         { | ||||
|     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); | ||||
|             return reaction.Emote.Name == Emojis.Success; | ||||
|         } | ||||
|  | ||||
|         public static RuntimeResult Error(string reason) => new PKResult(CommandError.Unsuccessful, reason); | ||||
|         public static RuntimeResult Success(string reason = null) => new PKResult(null, reason); | ||||
|         public static async Task<SocketReaction> WaitForReaction(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); | ||||
|                 } | ||||
|                 return Task.CompletedTask; | ||||
|             } | ||||
|  | ||||
|             (ctx as BaseSocketClient).ReactionAdded += Inner; | ||||
|  | ||||
|             try { | ||||
|                 return await (tcs.Task.TimeoutAfter(timeout)); | ||||
|             } finally { | ||||
|                 (ctx as BaseSocketClient).ReactionAdded -= Inner; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     class PKError : Exception | ||||
|     { | ||||
|         public PKError(string message) : base(message) | ||||
|         { | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -37,11 +37,11 @@ namespace PluralKit { | ||||
|         } | ||||
|  | ||||
|         public async Task Save(PKSystem system) { | ||||
|             await conn.UpdateAsync(system); | ||||
|             await conn.ExecuteAsync("update systems set name = @Name, description = @Description, tag = @Tag, avatar_url = @AvatarUrl, token = @Token, ui_tz = @UiTz where id = @Id", system); | ||||
|         } | ||||
|  | ||||
|         public async Task Delete(PKSystem system) { | ||||
|             await conn.DeleteAsync(system); | ||||
|             await conn.ExecuteAsync("delete from systems where id = @Id", system); | ||||
|         }         | ||||
|  | ||||
|         public async Task<IEnumerable<ulong>> GetLinkedAccountIds(PKSystem system) | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| using System; | ||||
| using System.Data; | ||||
| using System.Threading; | ||||
| using System.Threading.Tasks; | ||||
| using Dapper; | ||||
| using Discord; | ||||
| @@ -28,5 +29,24 @@ namespace PluralKit | ||||
|             if (str.Length < maxLength) return str; | ||||
|             return str.Substring(0, maxLength - ellipsis.Length) + ellipsis; | ||||
|         } | ||||
|  | ||||
|         public static async Task<TResult> TimeoutAfter<TResult>(this Task<TResult> task, TimeSpan? timeout) { | ||||
|             // https://stackoverflow.com/a/22078975 | ||||
|             using (var timeoutCancellationTokenSource = new CancellationTokenSource()) { | ||||
|                 var completedTask = await Task.WhenAny(task, Task.Delay(timeout ?? TimeSpan.FromMilliseconds(-1), timeoutCancellationTokenSource.Token)); | ||||
|                 if (completedTask == task) { | ||||
|                     timeoutCancellationTokenSource.Cancel(); | ||||
|                     return await task;  // Very important in order to propagate exceptions | ||||
|                 } else { | ||||
|                     throw new TimeoutException(); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public static class Emojis { | ||||
|         public static readonly string Warn = "\u26A0"; | ||||
|         public static readonly string Success = "\u2705"; | ||||
|         public static readonly string Error = "\u274C"; | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user