private async Task AvatarCommandTree(AvatarLocation location, Context ctx, PKMember target, MemberGuildSettings?guildData) { // First, see if we need to *clear* if (await ctx.MatchClear(location == AvatarLocation.Server ? "this member's server avatar" : "this member's avatar")) { ctx.CheckSystem().CheckOwnMember(target); await AvatarClear(location, ctx, target, guildData); return; } // Then, parse an image from the command (from various sources...) var avatarArg = await ctx.MatchImage(); if (avatarArg == null) { // If we didn't get any, just show the current avatar await AvatarShow(location, ctx, target, guildData); return; } ctx.CheckSystem().CheckOwnMember(target); await AvatarUtils.VerifyAvatarOrThrow(avatarArg.Value.Url); await UpdateAvatar(location, ctx, target, avatarArg.Value.Url); await PrintResponse(location, ctx, target, avatarArg.Value, guildData); }
public async Task Avatar(Context ctx) { ctx.CheckSystem(); if (ctx.Match("clear") || ctx.MatchFlag("c", "clear")) { ctx.System.AvatarUrl = null; await _data.SaveSystem(ctx.System); await ctx.Reply($"{Emojis.Success} System avatar cleared."); return; } else if (ctx.RemainderOrNull() == null && ctx.Message.Attachments.Count == 0) { if ((ctx.System.AvatarUrl?.Trim() ?? "").Length > 0) { var eb = new EmbedBuilder() .WithTitle($"System avatar") .WithImageUrl(ctx.System.AvatarUrl) .WithDescription($"To clear, use `pk;system avatar clear`."); await ctx.Reply(embed : eb.Build()); } else { throw new PKSyntaxError($"This system does not have an avatar set. Set one by attaching an image to this command, or by passing an image URL or @mention."); } return; } var member = await ctx.MatchUser(); if (member != null) { if (member.AvatarId == null) { throw Errors.UserHasNoAvatar; } ctx.System.AvatarUrl = member.GetAvatarUrl(ImageFormat.Png, size: 256); await _data.SaveSystem(ctx.System); var embed = new EmbedBuilder().WithImageUrl(ctx.System.AvatarUrl).Build(); await ctx.Reply( $"{Emojis.Success} System avatar changed to {member.Username}'s avatar! {Emojis.Warn} Please note that if {member.Username} changes their avatar, the system's avatar will need to be re-set.", embed : embed); } else { // They can't both be null - otherwise we would've hit the conditional at the very top string url = ctx.RemainderOrNull() ?? ctx.Message.Attachments.FirstOrDefault()?.ProxyUrl; await ctx.BusyIndicator(() => AvatarUtils.VerifyAvatarOrThrow(url)); ctx.System.AvatarUrl = url; await _data.SaveSystem(ctx.System); var embed = url != null ? new EmbedBuilder().WithImageUrl(url).Build() : null; await ctx.Reply($"{Emojis.Success} System avatar changed.", embed : embed); } }
private static Task ValidateUrl(string url) { if (url.Length > Limits.MaxUriLength) { throw Errors.InvalidUrl(url); } return(AvatarUtils.VerifyAvatarOrThrow(url)); }
private async Task AvatarFromAttachment(AvatarLocation location, Context ctx, PKMember target, DiscordAttachment attachment) { ctx.CheckSystem().CheckOwnMember(target); await AvatarUtils.VerifyAvatarOrThrow(attachment.Url); await UpdateAvatar(location, ctx, target, attachment.Url); if (location == AvatarLocation.Server) { await ctx.Reply($"{Emojis.Success} Member server avatar changed to attached image. This avatar will now be used when proxying in this server (**{ctx.Guild.Name}**). Please note that if you delete the message containing the attachment, the avatar will stop working."); } else if (location == AvatarLocation.Member) { await ctx.Reply($"{Emojis.Success} Member avatar changed to attached image. Please note that if you delete the message containing the attachment, the avatar will stop working."); } }
private async Task AvatarFromArg(AvatarLocation location, Context ctx, PKMember target, string url) { ctx.CheckSystem().CheckOwnMember(target); if (url.Length > Limits.MaxUriLength) { throw Errors.InvalidUrl(url); } await AvatarUtils.VerifyAvatarOrThrow(url); await UpdateAvatar(location, ctx, target, url); var embed = new DiscordEmbedBuilder().WithImageUrl(url).Build(); if (location == AvatarLocation.Server) { await ctx.Reply($"{Emojis.Success} Member server avatar changed. This avatar will now be used when proxying in this server (**{ctx.Guild.Name}**).", embed : embed); } }
public async Task Avatar(Context ctx, PKMember target) { var guildData = ctx.Guild != null ? await _data.GetMemberGuildSettings(target, ctx.Guild.Id) : null; if (ctx.RemainderOrNull() == null && ctx.Message.Attachments.Count == 0) { if ((target.AvatarUrl?.Trim() ?? "").Length > 0) { var eb = new EmbedBuilder() .WithTitle($"{target.Name.SanitizeMentions()}'s avatar") .WithImageUrl(target.AvatarUrl); if (target.System == ctx.System?.Id) { eb.WithDescription($"To clear, use `pk;member {target.Hid} avatar clear`."); } await ctx.Reply(embed : eb.Build()); } else { if (target.System == ctx.System?.Id) { throw new PKSyntaxError($"This member does not have an avatar set. Set one by attaching an image to this command, or by passing an image URL or @mention."); } throw new PKError($"This member does not have an avatar set."); } return; } if (ctx.System == null) { throw Errors.NoSystemError; } if (target.System != ctx.System.Id) { throw Errors.NotOwnMemberError; } if (ctx.Match("clear", "remove", "reset")) { target.AvatarUrl = null; await _data.SaveMember(target); if (guildData?.AvatarUrl != null) { await ctx.Reply($"{Emojis.Success} Member avatar cleared. Note that this member has a server-specific avatar set here, type `pk;member {target.Hid} serveravatar clear` if you wish to clear that too."); } else { await ctx.Reply($"{Emojis.Success} Member avatar cleared."); } } else if (await ctx.MatchUser() is IUser user) { if (user.AvatarId == null) { throw Errors.UserHasNoAvatar; } target.AvatarUrl = user.GetAvatarUrl(ImageFormat.Png, size: 256); await _data.SaveMember(target); var embed = new EmbedBuilder().WithImageUrl(target.AvatarUrl).Build(); await ctx.Reply( $"{Emojis.Success} Member avatar changed to {user.Username}'s avatar! {Emojis.Warn} Please note that if {user.Username} changes their avatar, the member's avatar will need to be re-set.", embed : embed); } else if (ctx.RemainderOrNull() is string url) { await AvatarUtils.VerifyAvatarOrThrow(url); target.AvatarUrl = url; await _data.SaveMember(target); var embed = new EmbedBuilder().WithImageUrl(url).Build(); await ctx.Reply($"{Emojis.Success} Member avatar changed.", embed : embed); } else if (ctx.Message.Attachments.FirstOrDefault() is Attachment attachment) { await AvatarUtils.VerifyAvatarOrThrow(attachment.Url); target.AvatarUrl = attachment.Url; await _data.SaveMember(target); await ctx.Reply($"{Emojis.Success} Member avatar changed to attached image. Please note that if you delete the message containing the attachment, the avatar will stop working."); } // No-arguments no-attachment case covered by conditional at the very top }
public async Task ServerAvatar(Context ctx, PKMember target) { ctx.CheckGuildContext(); var guildData = await _data.GetMemberGuildSettings(target, ctx.Guild.Id); if (ctx.RemainderOrNull() == null && ctx.Message.Attachments.Count == 0) { if ((guildData.AvatarUrl?.Trim() ?? "").Length > 0) { var eb = new EmbedBuilder() .WithTitle($"{target.Name.SanitizeMentions()}'s server avatar (for {ctx.Guild.Name})") .WithImageUrl(guildData.AvatarUrl); if (target.System == ctx.System?.Id) { eb.WithDescription($"To clear, use `pk;member {target.Hid} serveravatar clear`."); } await ctx.Reply(embed : eb.Build()); } else { throw new PKError($"This member does not have a server avatar set. Type `pk;member {target.Hid} avatar` to see their global avatar."); } return; } if (ctx.System == null) { throw Errors.NoSystemError; } if (target.System != ctx.System.Id) { throw Errors.NotOwnMemberError; } if (ctx.Match("clear", "remove", "reset")) { guildData.AvatarUrl = null; await _data.SetMemberGuildSettings(target, ctx.Guild.Id, guildData); if (target.AvatarUrl != null) { await ctx.Reply($"{Emojis.Success} Member server avatar cleared. This member will now use the global avatar in this server (**{ctx.Guild.Name}**)."); } else { await ctx.Reply($"{Emojis.Success} Member server avatar cleared. This member now has no avatar."); } } else if (await ctx.MatchUser() is IUser user) { if (user.AvatarId == null) { throw Errors.UserHasNoAvatar; } guildData.AvatarUrl = user.GetAvatarUrl(ImageFormat.Png, size: 256); await _data.SetMemberGuildSettings(target, ctx.Guild.Id, guildData); var embed = new EmbedBuilder().WithImageUrl(guildData.AvatarUrl).Build(); await ctx.Reply( $"{Emojis.Success} Member server avatar changed to {user.Username}'s avatar! This avatar will now be used when proxying in this server (**{ctx.Guild.Name}**). {Emojis.Warn} Please note that if {user.Username} changes their avatar, the member's server avatar will need to be re-set.", embed : embed); } else if (ctx.RemainderOrNull() is string url) { await AvatarUtils.VerifyAvatarOrThrow(url); guildData.AvatarUrl = url; await _data.SetMemberGuildSettings(target, ctx.Guild.Id, guildData); var embed = new EmbedBuilder().WithImageUrl(url).Build(); await ctx.Reply($"{Emojis.Success} Member server avatar changed. This avatar will now be used when proxying in this server (**{ctx.Guild.Name}**).", embed : embed); } else if (ctx.Message.Attachments.FirstOrDefault() is Attachment attachment) { await AvatarUtils.VerifyAvatarOrThrow(attachment.Url); guildData.AvatarUrl = attachment.Url; await _data.SetMemberGuildSettings(target, ctx.Guild.Id, guildData); await ctx.Reply($"{Emojis.Success} Member server avatar changed to attached image. This avatar will now be used when proxying in this server (**{ctx.Guild.Name}**). Please note that if you delete the message containing the attachment, the avatar will stop working."); } // No-arguments no-attachment case covered by conditional at the very top }
public async Task GroupIcon(Context ctx, PKGroup target) { async Task ClearIcon() { ctx.CheckOwnGroup(target); await _db.Execute(c => _repo.UpdateGroup(c, target.Id, new GroupPatch { Icon = null })); await ctx.Reply($"{Emojis.Success} Group icon cleared."); } async Task SetIcon(ParsedImage img) { ctx.CheckOwnGroup(target); await AvatarUtils.VerifyAvatarOrThrow(img.Url); await _db.Execute(c => _repo.UpdateGroup(c, target.Id, new GroupPatch { Icon = img.Url })); var msg = img.Source switch { AvatarSource.User => $"{Emojis.Success} Group icon changed to {img.SourceUser?.Username}'s avatar!\n{Emojis.Warn} If {img.SourceUser?.Username} changes their avatar, the group icon will need to be re-set.", AvatarSource.Url => $"{Emojis.Success} Group icon changed to the image at the given URL.", AvatarSource.Attachment => $"{Emojis.Success} Group icon changed to attached image.\n{Emojis.Warn} If you delete the message containing the attachment, the group icon will stop working.", _ => throw new ArgumentOutOfRangeException() }; // The attachment's already right there, no need to preview it. var hasEmbed = img.Source != AvatarSource.Attachment; await(hasEmbed ? ctx.Reply(msg, embed: new DiscordEmbedBuilder().WithImageUrl(img.Url).Build()) : ctx.Reply(msg)); } async Task ShowIcon() { if ((target.Icon?.Trim() ?? "").Length > 0) { var eb = new DiscordEmbedBuilder() .WithTitle("Group icon") .WithImageUrl(target.Icon); if (target.System == ctx.System?.Id) { eb.WithDescription($"To clear, use `pk;group {target.Reference()} icon -clear`."); } await ctx.Reply(embed : eb.Build()); } else { throw new PKSyntaxError("This group does not have an icon set. Set one by attaching an image to this command, or by passing an image URL or @mention."); } } if (await ctx.MatchClear("this group's icon")) { await ClearIcon(); } else if (await ctx.MatchImage() is {} img) { await SetIcon(img); }
public async Task Avatar(Context ctx) { ctx.CheckSystem(); async Task ClearIcon() { await _db.Execute(c => c.UpdateSystem(ctx.System.Id, new SystemPatch { AvatarUrl = null })); await ctx.Reply($"{Emojis.Success} System icon cleared."); } async Task SetIcon(ParsedImage img) { if (img.Url.Length > Limits.MaxUriLength) { throw Errors.InvalidUrl(img.Url); } await AvatarUtils.VerifyAvatarOrThrow(img.Url); await _db.Execute(c => c.UpdateSystem(ctx.System.Id, new SystemPatch { AvatarUrl = img.Url })); var msg = img.Source switch { AvatarSource.User => $"{Emojis.Success} System icon changed to {img.SourceUser?.Username}'s avatar!\n{Emojis.Warn} If {img.SourceUser?.Username} changes their avatar, the system icon will need to be re-set.", AvatarSource.Url => $"{Emojis.Success} System icon changed to the image at the given URL.", AvatarSource.Attachment => $"{Emojis.Success} System icon changed to attached image.\n{Emojis.Warn} If you delete the message containing the attachment, the system icon will stop working.", _ => throw new ArgumentOutOfRangeException() }; // The attachment's already right there, no need to preview it. var hasEmbed = img.Source != AvatarSource.Attachment; await(hasEmbed ? ctx.Reply(msg, embed: new DiscordEmbedBuilder().WithImageUrl(img.Url).Build()) : ctx.Reply(msg)); } async Task ShowIcon() { if ((ctx.System.AvatarUrl?.Trim() ?? "").Length > 0) { var eb = new DiscordEmbedBuilder() .WithTitle("System icon") .WithImageUrl(ctx.System.AvatarUrl) .WithDescription("To clear, use `pk;system icon clear`."); await ctx.Reply(embed : eb.Build()); } else { throw new PKSyntaxError("This system does not have an icon set. Set one by attaching an image to this command, or by passing an image URL or @mention."); } } if (ctx.MatchClear()) { await ClearIcon(); } else if (await ctx.MatchImage() is {} img) { await SetIcon(img); }
public async Task NewMember(Context ctx) { if (ctx.System == null) { throw Errors.NoSystemError; } var memberName = ctx.RemainderOrNull() ?? throw new PKSyntaxError("You must pass a member name."); // Hard name length cap if (memberName.Length > Limits.MaxMemberNameLength) { throw Errors.MemberNameTooLongError(memberName.Length); } // Warn if there's already a member by this name var existingMember = await _db.Execute(c => _repo.GetMemberByName(c, ctx.System.Id, memberName)); if (existingMember != null) { var msg = $"{Emojis.Warn} You already have a member in your system with the name \"{existingMember.NameFor(ctx)}\" (with ID `{existingMember.Hid}`). Do you want to create another member with the same name?"; if (!await ctx.PromptYesNo(msg, "Create")) { throw new PKError("Member creation cancelled."); } } await using var conn = await _db.Obtain(); // Enforce per-system member limit var memberCount = await _repo.GetSystemMemberCount(conn, ctx.System.Id); var memberLimit = ctx.System.MemberLimitOverride ?? Limits.MaxMemberCount; if (memberCount >= memberLimit) { throw Errors.MemberLimitReachedError(memberLimit); } // Create the member var member = await _repo.CreateMember(conn, ctx.System.Id, memberName); memberCount++; // Try to match an image attached to the message var avatarArg = ctx.Message.Attachments.FirstOrDefault(); Exception imageMatchError = null; if (avatarArg != null) { try { await AvatarUtils.VerifyAvatarOrThrow(avatarArg.Url); await _db.Execute(conn => _repo.UpdateMember(conn, member.Id, new MemberPatch { AvatarUrl = avatarArg.Url })); } catch (Exception e) { imageMatchError = e; } } // Send confirmation and space hint await ctx.Reply($"{Emojis.Success} Member \"{memberName}\" (`{member.Hid}`) registered! Check out the getting started page for how to get a member up and running: https://pluralkit.me/start#create-a-member"); if (await _db.Execute(conn => conn.QuerySingleAsync <bool>("select has_private_members(@System)", new { System = ctx.System.Id }))) //if has private members { await ctx.Reply($"{Emojis.Warn} This member is currently **public**. To change this, use `pk;member {member.Hid} private`."); } if (avatarArg != null) { if (imageMatchError == null) { await ctx.Reply($"{Emojis.Success} Member avatar set to attached image.\n{Emojis.Warn} If you delete the message containing the attachment, the avatar will stop working."); } else { await ctx.Reply($"{Emojis.Error} Couldn't set avatar: {imageMatchError.Message}"); } } if (memberName.Contains(" ")) { await ctx.Reply($"{Emojis.Note} Note that this member's name contains spaces. You will need to surround it with \"double quotes\" when using commands referring to it, or just use the member's 5-character ID (which is `{member.Hid}`)."); } if (memberCount >= memberLimit) { await ctx.Reply($"{Emojis.Warn} You have reached the per-system member limit ({memberLimit}). You will be unable to create additional members until existing members are deleted."); } else if (memberCount >= Limits.MaxMembersWarnThreshold(memberLimit)) { await ctx.Reply($"{Emojis.Warn} You are approaching the per-system member limit ({memberCount} / {memberLimit} members). Please review your member list for unused or duplicate members."); } }