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(_client, avatarArg.Value.Url); await UpdateAvatar(location, ctx, target, avatarArg.Value.Url); await PrintResponse(location, ctx, target, avatarArg.Value, guildData); }
public async Task BannerImage(Context ctx, PKMember target) { ctx.CheckOwnMember(target); async Task ClearBannerImage() { await ctx.Repository.UpdateMember(target.Id, new MemberPatch { BannerImage = null }); await ctx.Reply($"{Emojis.Success} Member banner image cleared."); } async Task SetBannerImage(ParsedImage img) { await AvatarUtils.VerifyAvatarOrThrow(_client, img.Url, true); await ctx.Repository.UpdateMember(target.Id, new MemberPatch { BannerImage = img.Url }); var msg = img.Source switch { AvatarSource.Url => $"{Emojis.Success} Member banner image changed to the image at the given URL.", AvatarSource.Attachment => $"{Emojis.Success} Member banner image changed to attached image.\n{Emojis.Warn} If you delete the message containing the attachment, the banner image will stop working.", AvatarSource.User => throw new PKError("Cannot set a banner image to an user's avatar."), _ => 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, new EmbedBuilder().Image(new Embed.EmbedImage(img.Url)).Build()) : ctx.Reply(msg)); } async Task ShowBannerImage() { if ((target.BannerImage?.Trim() ?? "").Length > 0) { var eb = new EmbedBuilder() .Title($"{target.NameFor(ctx)}'s banner image") .Image(new Embed.EmbedImage(target.BannerImage)) .Description($"To clear, use `pk;member {target.Hid} banner clear`."); await ctx.Reply(embed : eb.Build()); } else { throw new PKSyntaxError( "This member does not have a banner image set. Set one by attaching an image to this command, or by passing an image URL or @mention."); } } if (await ctx.MatchClear("this member's banner image")) { await ClearBannerImage(); } else if (await ctx.MatchImage() is { } img) { await SetBannerImage(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.StringTooLongError("Member name", memberName.Length, Limits.MaxMemberNameLength); } // Warn if there's already a member by this name var existingMember = await ctx.Repository.GetMemberByName(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 ctx.Database.Obtain(); // Enforce per-system member limit var memberCount = await ctx.Repository.GetSystemMemberCount(ctx.System.Id); var memberLimit = ctx.Config.MemberLimitOverride ?? Limits.MaxMemberCount; if (memberCount >= memberLimit) { throw Errors.MemberLimitReachedError(memberLimit); } // Create the member var member = await ctx.Repository.CreateMember(ctx.System.Id, memberName, conn); memberCount++; JObject dispatchData = new JObject(); dispatchData.Add("name", memberName); if (ctx.Config.MemberDefaultPrivate) { var patch = new MemberPatch().WithAllPrivacy(PrivacyLevel.Private); await ctx.Repository.UpdateMember(member.Id, patch, conn); dispatchData.Merge(patch.ToJson()); } // 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(_client, avatarArg.Url); await ctx.Repository.UpdateMember(member.Id, new MemberPatch { AvatarUrl = avatarArg.Url }, conn); dispatchData.Add("avatar_url", avatarArg.Url); } catch (Exception e) { imageMatchError = e; } } _ = _dispatch.Dispatch(member.Id, new UpdateDispatchData { Event = DispatchEvent.CREATE_MEMBER, EventData = dispatchData, }); // 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"); // todo: move this to ModelRepository if (await ctx.Database.Execute(conn => conn.QuerySingleAsync <bool>("select has_private_members(@System)", new { System = ctx.System.Id })) && !ctx.Config.MemberDefaultPrivate) //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.WarnThreshold(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."); } }
public async Task Avatar(Context ctx, PKSystem target) { async Task ClearIcon() { ctx.CheckOwnSystem(target); await ctx.Repository.UpdateSystem(target.Id, new SystemPatch { AvatarUrl = null }); await ctx.Reply($"{Emojis.Success} System icon cleared."); } async Task SetIcon(ParsedImage img) { ctx.CheckOwnSystem(target); await AvatarUtils.VerifyAvatarOrThrow(_client, img.Url); await ctx.Repository.UpdateSystem(target.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, new EmbedBuilder().Image(new Embed.EmbedImage(img.Url)).Build()) : ctx.Reply(msg)); } async Task ShowIcon() { if ((target.AvatarUrl?.Trim() ?? "").Length > 0) { var eb = new EmbedBuilder() .Title("System icon") .Image(new Embed.EmbedImage(target.AvatarUrl.TryGetCleanCdnUrl())); if (target.Id == ctx.System?.Id) { eb.Description("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 (target != null && target?.Id != ctx.System?.Id) { await ShowIcon(); return; } if (await ctx.MatchClear("your system's icon")) { await ClearIcon(); } else if (await ctx.MatchImage() is { } img) { await SetIcon(img); }