public async Task <Embed> CreateGroupEmbed(Context ctx, PKSystem system, PKGroup target) { await using var conn = await _db.Obtain(); var pctx = ctx.LookupContextFor(system); var memberCount = ctx.MatchPrivateFlag(pctx) ? await _repo.GetGroupMemberCount(conn, target.Id, PrivacyLevel.Public) : await _repo.GetGroupMemberCount(conn, target.Id); var nameField = target.Name; if (system.Name != null) { nameField = $"{nameField} ({system.Name})"; } var eb = new EmbedBuilder() .Author(new(nameField, IconUrl: DiscordUtils.WorkaroundForUrlBug(target.IconFor(pctx)))) .Footer(new($"System ID: {system.Hid} | Group ID: {target.Hid} | Created on {target.Created.FormatZoned(system)}")); if (target.DisplayName != null) { eb.Field(new("Display Name", target.DisplayName)); } if (target.ListPrivacy.CanAccess(pctx)) { if (memberCount == 0 && pctx == LookupContext.ByOwner) { // Only suggest the add command if this is actually the owner lol eb.Field(new("Members (0)", $"Add one with `pk;group {target.Reference()} add <member>`!", true)); } else { eb.Field(new($"Members ({memberCount})", $"(see `pk;group {target.Reference()} list`)", true)); } } if (target.DescriptionFor(pctx) is { } desc) { eb.Field(new("Description", desc)); } if (target.IconFor(pctx) is {} icon) { eb.Thumbnail(new(icon)); } return(eb.Build()); }
public async Task GroupDisplayName(Context ctx, PKGroup target) { if (await ctx.MatchClear("this group's display name")) { ctx.CheckOwnGroup(target); var patch = new GroupPatch { DisplayName = Partial <string> .Null() }; await _db.Execute(conn => _repo.UpdateGroup(conn, target.Id, patch)); await ctx.Reply($"{Emojis.Success} Group display name cleared."); } else if (!ctx.HasNext()) { // No perms check, display name isn't covered by member privacy var eb = new DiscordEmbedBuilder() .AddField("Name", target.Name) .AddField("Display Name", target.DisplayName ?? "*(none)*"); if (ctx.System?.Id == target.System) { eb.WithDescription($"To change display name, type `pk;group {target.Reference()} displayname <display name>`.\nTo clear it, type `pk;group {target.Reference()} displayname -clear`."); } await ctx.Reply(embed : eb.Build()); } else { ctx.CheckOwnGroup(target); var newDisplayName = ctx.RemainderOrNull(); var patch = new GroupPatch { DisplayName = Partial <string> .Present(newDisplayName) }; await _db.Execute(conn => _repo.UpdateGroup(conn, target.Id, patch)); await ctx.Reply($"{Emojis.Success} Group display name changed."); } }
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 GroupDescription(Context ctx, PKGroup target) { if (await ctx.MatchClear("this group's description")) { ctx.CheckOwnGroup(target); var patch = new GroupPatch { Description = Partial <string> .Null() }; await _db.Execute(conn => _repo.UpdateGroup(conn, target.Id, patch)); await ctx.Reply($"{Emojis.Success} Group description cleared."); } else if (!ctx.HasNext()) { if (target.Description == null) { if (ctx.System?.Id == target.System) { await ctx.Reply($"This group does not have a description set. To set one, type `pk;group {target.Reference()} description <description>`."); } else { await ctx.Reply("This group does not have a description set."); } } else if (ctx.MatchFlag("r", "raw")) { await ctx.Reply($"```\n{target.Description}\n```"); } else { await ctx.Reply(embed : new DiscordEmbedBuilder() .WithTitle("Group description") .WithDescription(target.Description) .AddField("\u200B", $"To print the description with formatting, type `pk;group {target.Reference()} description -raw`." + (ctx.System?.Id == target.System ? $" To clear it, type `pk;group {target.Reference()} description -clear`." : "")) .Build()); } } else { ctx.CheckOwnGroup(target); var description = ctx.RemainderOrNull().NormalizeLineEndSpacing(); if (description.IsLongerThan(Limits.MaxDescriptionLength)) { throw Errors.DescriptionTooLongError(description.Length); } var patch = new GroupPatch { Description = Partial <string> .Present(description) }; await _db.Execute(conn => _repo.UpdateGroup(conn, target.Id, patch)); await ctx.Reply($"{Emojis.Success} Group description changed."); } }
public async Task <Embed> CreateGroupEmbed(Context ctx, PKSystem system, PKGroup target) { var pctx = ctx.LookupContextFor(system.Id); var countctx = LookupContext.ByNonOwner; if (ctx.MatchFlag("a", "all")) { if (system.Id == ctx.System.Id) { countctx = LookupContext.ByOwner; } else { throw Errors.LookupNotAllowed; } } var memberCount = await _repo.GetGroupMemberCount(target.Id, countctx == LookupContext.ByOwner?null : PrivacyLevel.Public); var nameField = target.NamePrivacy.Get(pctx, target.Name, target.DisplayName ?? target.Name); if (system.Name != null) { nameField = $"{nameField} ({system.Name})"; } uint color; try { color = target.Color?.ToDiscordColor() ?? DiscordUtils.Gray; } catch (ArgumentException) { // There's no API for group colors yet, but defaulting to a blank color regardless color = DiscordUtils.Gray; } var eb = new EmbedBuilder() .Author(new Embed.EmbedAuthor(nameField, IconUrl: target.IconFor(pctx), Url: $"https://dash.pluralkit.me/profile/g/{target.Hid}")) .Color(color); eb.Footer(new Embed.EmbedFooter($"System ID: {system.Hid} | Group ID: {target.Hid}{(target.MetadataPrivacy.CanAccess(pctx) ? $" | Created on {target.Created.FormatZoned(ctx.Zone)}" : "")}")); if (target.DescriptionPrivacy.CanAccess(pctx)) { eb.Image(new Embed.EmbedImage(target.BannerImage)); } if (target.NamePrivacy.CanAccess(pctx) && target.DisplayName != null) { eb.Field(new Embed.Field("Display Name", target.DisplayName, true)); } if (!target.Color.EmptyOrNull()) { eb.Field(new Embed.Field("Color", $"#{target.Color}", true)); } if (target.ListPrivacy.CanAccess(pctx)) { if (memberCount == 0 && pctx == LookupContext.ByOwner) { // Only suggest the add command if this is actually the owner lol eb.Field(new Embed.Field("Members (0)", $"Add one with `pk;group {target.Reference(ctx)} add <member>`!")); } else { var name = pctx == LookupContext.ByOwner ? target.Reference(ctx) : target.Hid; eb.Field(new Embed.Field($"Members ({memberCount})", $"(see `pk;group {name} list`)")); } } if (target.DescriptionFor(pctx) is { } desc) { eb.Field(new Embed.Field("Description", desc)); } if (target.IconFor(pctx) is { } icon) { eb.Thumbnail(new Embed.EmbedThumbnail(icon.TryGetCleanCdnUrl())); } return(eb.Build()); }
public async Task GroupDescription(Context ctx, PKGroup target) { ctx.CheckSystemPrivacy(target.System, target.DescriptionPrivacy); var noDescriptionSetMessage = "This group does not have a description set."; if (ctx.System?.Id == target.System) { noDescriptionSetMessage += $" To set one, type `pk;group {target.Reference(ctx)} description <description>`."; } if (ctx.MatchRaw()) { if (target.Description == null) { await ctx.Reply(noDescriptionSetMessage); } else { await ctx.Reply($"```\n{target.Description}\n```"); } return; } if (!ctx.HasNext(false)) { if (target.Description == null) { await ctx.Reply(noDescriptionSetMessage); } else { await ctx.Reply(embed : new EmbedBuilder() .Title("Group description") .Description(target.Description) .Field(new Embed.Field("\u200B", $"To print the description with formatting, type `pk;group {target.Reference(ctx)} description -raw`." + (ctx.System?.Id == target.System ? $" To clear it, type `pk;group {target.Reference(ctx)} description -clear`." : ""))) .Build()); } return; } ctx.CheckOwnGroup(target); if (await ctx.MatchClear("this group's description")) { var patch = new GroupPatch { Description = Partial <string> .Null() }; await ctx.Repository.UpdateGroup(target.Id, patch); await ctx.Reply($"{Emojis.Success} Group description cleared."); } else { var description = ctx.RemainderOrNull(false).NormalizeLineEndSpacing(); if (description.IsLongerThan(Limits.MaxDescriptionLength)) { throw Errors.StringTooLongError("Description", description.Length, Limits.MaxDescriptionLength); } var patch = new GroupPatch { Description = Partial <string> .Present(description) }; await ctx.Repository.UpdateGroup(target.Id, patch); await ctx.Reply($"{Emojis.Success} Group description changed."); } }
public async Task GroupDisplayName(Context ctx, PKGroup target) { var noDisplayNameSetMessage = "This group does not have a display name set."; if (ctx.System?.Id == target.System) { noDisplayNameSetMessage += $" To set one, type `pk;group {target.Reference(ctx)} displayname <display name>`."; } // No perms check, display name isn't covered by member privacy if (ctx.MatchRaw()) { if (target.DisplayName == null) { await ctx.Reply(noDisplayNameSetMessage); } else { await ctx.Reply($"```\n{target.DisplayName}\n```"); } return; } if (!ctx.HasNext(false)) { if (target.DisplayName == null) { await ctx.Reply(noDisplayNameSetMessage); } else { var eb = new EmbedBuilder() .Field(new Embed.Field("Name", target.Name)) .Field(new Embed.Field("Display Name", target.DisplayName)); var reference = target.Reference(ctx); if (ctx.System?.Id == target.System) { eb.Description( $"To change display name, type `pk;group {reference} displayname <display name>`." + $"To clear it, type `pk;group {reference} displayname -clear`." + $"To print the raw display name, type `pk;group {reference} displayname -raw`."); } await ctx.Reply(embed : eb.Build()); } return; } ctx.CheckOwnGroup(target); if (await ctx.MatchClear("this group's display name")) { var patch = new GroupPatch { DisplayName = Partial <string> .Null() }; await ctx.Repository.UpdateGroup(target.Id, patch); await ctx.Reply($"{Emojis.Success} Group display name cleared."); if (target.NamePrivacy == PrivacyLevel.Private) { await ctx.Reply($"{Emojis.Warn} Since this group no longer has a display name set, their name privacy **can no longer take effect**."); } } else { var newDisplayName = ctx.RemainderOrNull(false).NormalizeLineEndSpacing(); var patch = new GroupPatch { DisplayName = Partial <string> .Present(newDisplayName) }; await ctx.Repository.UpdateGroup(target.Id, patch); await ctx.Reply($"{Emojis.Success} Group display name changed."); } }