Add system linking commands
This commit is contained in:
		| @@ -130,6 +130,9 @@ namespace PluralKit.Bot | ||||
|                         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 next time :)"); | ||||
|                     } else if (_result is PreconditionResult) | ||||
|                     { | ||||
|                         await ctx.Message.Channel.SendMessageAsync($"{Emojis.Error} {_result.ErrorReason}"); | ||||
|                     } else { | ||||
|                         HandleRuntimeError((_result as ExecuteResult?)?.Exception); | ||||
|                     } | ||||
|   | ||||
							
								
								
									
										50
									
								
								PluralKit.Bot/Commands/LinkCommands.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								PluralKit.Bot/Commands/LinkCommands.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,50 @@ | ||||
| using System.Linq; | ||||
| using System.Threading.Tasks; | ||||
| using Discord; | ||||
| using Discord.Commands; | ||||
|  | ||||
| namespace PluralKit.Bot.Commands | ||||
| { | ||||
|     public class LinkCommands: ModuleBase<PKCommandContext> | ||||
|     { | ||||
|         public SystemStore Systems { get; set; } | ||||
|  | ||||
|  | ||||
|         [Command("link")] | ||||
|         [Remarks("link <account>")] | ||||
|         [MustHaveSystem] | ||||
|         public async Task LinkSystem(IUser account) | ||||
|         { | ||||
|             var accountIds = await Systems.GetLinkedAccountIds(Context.SenderSystem); | ||||
|             if (accountIds.Contains(account.Id)) throw Errors.AccountAlreadyLinked; | ||||
|  | ||||
|             var existingAccount = await Systems.GetByAccount(account.Id); | ||||
|             if (existingAccount != null) throw Errors.AccountInOtherSystem(existingAccount);  | ||||
|  | ||||
|             var msg = await Context.Channel.SendMessageAsync( | ||||
|                 $"{account.Mention}, please confirm the link by clicking the {Emojis.Success} reaction on this message."); | ||||
|             if (!await Context.PromptYesNo(msg, user: account)) throw Errors.MemberLinkCancelled; | ||||
|             await Systems.Link(Context.SenderSystem, account.Id); | ||||
|             await Context.Channel.SendMessageAsync($"{Emojis.Success} Account linked to system."); | ||||
|         } | ||||
|  | ||||
|         [Command("unlink")] | ||||
|         [Remarks("unlink [account]")] | ||||
|         [MustHaveSystem] | ||||
|         public async Task UnlinkAccount(IUser account = null) | ||||
|         { | ||||
|             if (account == null) account = Context.User; | ||||
|              | ||||
|             var accountIds = (await Systems.GetLinkedAccountIds(Context.SenderSystem)).ToList(); | ||||
|             if (!accountIds.Contains(account.Id)) throw Errors.AccountNotLinked; | ||||
|             if (accountIds.Count == 1) throw Errors.UnlinkingLastAccount; | ||||
|              | ||||
|             var msg = await Context.Channel.SendMessageAsync( | ||||
|                 $"Are you sure you want to unlink {account.Mention} from your system?"); | ||||
|             if (!await Context.PromptYesNo(msg)) throw Errors.MemberUnlinkCancelled; | ||||
|  | ||||
|             await Systems.Unlink(Context.SenderSystem, account.Id); | ||||
|             await Context.Channel.SendMessageAsync($"{Emojis.Success} Account unlinked."); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -2,7 +2,7 @@ using System.Threading.Tasks; | ||||
| using Discord; | ||||
| using Discord.Commands; | ||||
|  | ||||
| namespace PluralKit.Bot { | ||||
| namespace PluralKit.Bot.Commands { | ||||
|     public class MiscCommands: ModuleBase<PKCommandContext> { | ||||
|         [Command("invite")] | ||||
|         [Remarks("invite")] | ||||
|   | ||||
| @@ -20,7 +20,7 @@ namespace PluralKit.Bot.Commands | ||||
|         [Command] | ||||
|         public async Task Query(PKSystem system = null) { | ||||
|             if (system == null) system = Context.SenderSystem; | ||||
|             if (system == null) throw Errors.NotOwnSystemError; | ||||
|             if (system == null) throw Errors.NoSystemError; | ||||
|  | ||||
|             await Context.Channel.SendMessageAsync(embed: await EmbedService.CreateSystemEmbed(system)); | ||||
|         } | ||||
|   | ||||
| @@ -8,9 +8,9 @@ using Discord.WebSocket; | ||||
|  | ||||
| namespace PluralKit.Bot { | ||||
|     public static class ContextUtils { | ||||
|                 public static async Task<bool> PromptYesNo(this ICommandContext ctx, IUserMessage message, TimeSpan? timeout = null) { | ||||
|                 public static async Task<bool> PromptYesNo(this ICommandContext ctx, IUserMessage message, IUser user = null, TimeSpan? timeout = null) { | ||||
|             await message.AddReactionsAsync(new[] {new Emoji(Emojis.Success), new Emoji(Emojis.Error)}); | ||||
|             var reaction = await ctx.AwaitReaction(message, ctx.User, (r) => r.Emote.Name == Emojis.Success || r.Emote.Name == Emojis.Error, timeout ?? TimeSpan.FromMinutes(1)); | ||||
|             var reaction = await ctx.AwaitReaction(message, user ?? ctx.User, (r) => r.Emote.Name == Emojis.Success || r.Emote.Name == Emojis.Error, timeout ?? TimeSpan.FromMinutes(1)); | ||||
|             return reaction.Emote.Name == Emojis.Success; | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -28,5 +28,12 @@ namespace PluralKit.Bot { | ||||
|         public static PKError AvatarNotAnImage(string mimeType) => new PKError($"The given link does not point to an image{(mimeType != null ? $" ({mimeType})" : "")}. Make sure you're using a direct link (ending in .jpg, .png, .gif)."); | ||||
|         public static PKError AvatarDimensionsTooLarge(int width, int height) => new PKError($"Image too large ({width}x{height} > {Limits.AvatarDimensionLimit}x{Limits.AvatarDimensionLimit}), try resizing the image."); | ||||
|         public static PKError InvalidUrl(string url) => new PKError($"The given URL is invalid."); | ||||
|          | ||||
|         public static PKError AccountAlreadyLinked => new PKError("That account is already linked to your system."); | ||||
|         public static PKError AccountNotLinked => new PKError("That account isn't linked to your system."); | ||||
|         public static PKError AccountInOtherSystem(PKSystem system) => new PKError($"The mentioned account is already linked to another system (see `pk;system {system.Hid}`)."); | ||||
|         public static PKError UnlinkingLastAccount => new PKError("Since this is the only account linked to this system, you cannot unlink it (as that would leave your system account-less)."); | ||||
|         public static PKError MemberLinkCancelled => new PKError("Member link cancelled."); | ||||
|         public static PKError MemberUnlinkCancelled => new PKError("Member unlink cancelled."); | ||||
|     } | ||||
| } | ||||
| @@ -66,7 +66,7 @@ namespace PluralKit.Bot | ||||
|         public override async Task<TypeReaderResult> ReadAsync(ICommandContext context, string input, IServiceProvider services) | ||||
|         { | ||||
|             var client = services.GetService<IDiscordClient>(); | ||||
|             var conn = services.GetService<IDbConnection>(); | ||||
|             var systems = services.GetService<SystemStore>(); | ||||
|  | ||||
|             // System references can take three forms: | ||||
|             // - The direct user ID of an account connected to the system | ||||
| @@ -74,20 +74,20 @@ namespace PluralKit.Bot | ||||
|             // - A system hid | ||||
|  | ||||
|             // First, try direct user ID parsing | ||||
|             if (ulong.TryParse(input, out var idFromNumber)) return await FindSystemByAccountHelper(idFromNumber, client, conn); | ||||
|             if (ulong.TryParse(input, out var idFromNumber)) return await FindSystemByAccountHelper(idFromNumber, client, systems); | ||||
|  | ||||
|             // Then, try mention parsing. | ||||
|             if (MentionUtils.TryParseUser(input, out var idFromMention)) return await FindSystemByAccountHelper(idFromMention, client, conn); | ||||
|             if (MentionUtils.TryParseUser(input, out var idFromMention)) return await FindSystemByAccountHelper(idFromMention, client, systems); | ||||
|  | ||||
|             // Finally, try HID parsing | ||||
|             var res = await conn.QuerySingleOrDefaultAsync<PKSystem>("select * from systems where hid = @Hid", new { Hid = input }); | ||||
|             var res = await systems.GetByHid(input); | ||||
|             if (res != null) return TypeReaderResult.FromSuccess(res); | ||||
|             return TypeReaderResult.FromError(CommandError.ObjectNotFound, $"System with ID `{input}` not found."); | ||||
|         } | ||||
|  | ||||
|         async Task<TypeReaderResult> FindSystemByAccountHelper(ulong id, IDiscordClient client, IDbConnection conn) | ||||
|         async Task<TypeReaderResult> FindSystemByAccountHelper(ulong id, IDiscordClient client, SystemStore systems) | ||||
|         { | ||||
|             var foundByAccountId = await conn.QuerySingleOrDefaultAsync<PKSystem>("select * from accounts, systems where accounts.system = system.id and accounts.id = @Id", new { Id = id }); | ||||
|             var foundByAccountId = await systems.GetByAccount(id); | ||||
|             if (foundByAccountId != null) return TypeReaderResult.FromSuccess(foundByAccountId); | ||||
|  | ||||
|             // We didn't find any, so we try to resolve the user ID to find the associated account, | ||||
|   | ||||
| @@ -23,9 +23,13 @@ namespace PluralKit { | ||||
|         public async Task Link(PKSystem system, ulong accountId) { | ||||
|             await conn.ExecuteAsync("insert into accounts (uid, system) values (@Id, @SystemId)", new { Id = accountId, SystemId = system.Id }); | ||||
|         } | ||||
|          | ||||
|         public async Task Unlink(PKSystem system, ulong accountId) { | ||||
|             await conn.ExecuteAsync("delete from accounts where uid = @Id and system = @SystemId", new { Id = accountId, SystemId = system.Id }); | ||||
|         } | ||||
|  | ||||
|         public async Task<PKSystem> GetByAccount(ulong accountId) { | ||||
|             return await conn.QuerySingleOrDefaultAsync<PKSystem>("select systems.* from systems, accounts where accounts.system = system.id and accounts.uid = @Id", new { Id = accountId }); | ||||
|             return await conn.QuerySingleOrDefaultAsync<PKSystem>("select systems.* from systems, accounts where accounts.system = systems.id and accounts.uid = @Id", new { Id = accountId }); | ||||
|         } | ||||
|  | ||||
|         public async Task<PKSystem> GetByHid(string hid) { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user