feat: implement system banner commands

This commit is contained in:
dusk 2025-04-04 04:27:28 +09:00
parent 6a840f768f
commit 9e74835e4b
No known key found for this signature in database
3 changed files with 92 additions and 57 deletions

View file

@ -82,6 +82,19 @@ public partial class CommandTree
Commands.SystemShowServerAvatar(var param, var flags) => ctx.Execute<SystemEdit>(SystemServerAvatar, m => m.ShowServerAvatar(ctx, param.target, flags.GetReplyFormat())), Commands.SystemShowServerAvatar(var param, var flags) => ctx.Execute<SystemEdit>(SystemServerAvatar, m => m.ShowServerAvatar(ctx, param.target, flags.GetReplyFormat())),
Commands.SystemClearServerAvatar(var param, var flags) => ctx.Execute<SystemEdit>(SystemServerAvatar, m => m.ClearServerAvatar(ctx, ctx.System, flags.yes)), Commands.SystemClearServerAvatar(var param, var flags) => ctx.Execute<SystemEdit>(SystemServerAvatar, m => m.ClearServerAvatar(ctx, ctx.System, flags.yes)),
Commands.SystemChangeServerAvatar(var param, _) => ctx.Execute<SystemEdit>(SystemServerAvatar, m => m.ChangeServerAvatar(ctx, ctx.System, param.avatar)), Commands.SystemChangeServerAvatar(var param, _) => ctx.Execute<SystemEdit>(SystemServerAvatar, m => m.ChangeServerAvatar(ctx, ctx.System, param.avatar)),
Commands.SystemShowBannerSelf(_, var flags) => ((Func<Task>)(() =>
{
// we want to change banner if an attached image is passed
// we can't have a separate parsed command for this since the parser can't be aware of any attachments
var attachedImage = ctx.ExtractImageFromAttachment();
if (attachedImage is { } image)
return ctx.Execute<SystemEdit>(SystemBannerImage, m => m.ChangeBannerImage(ctx, ctx.System, image));
// if no attachment show the banner like intended
return ctx.Execute<SystemEdit>(SystemBannerImage, m => m.ShowBannerImage(ctx, ctx.System, flags.GetReplyFormat()));
}))(),
Commands.SystemShowBanner(var param, var flags) => ctx.Execute<SystemEdit>(SystemBannerImage, m => m.ShowBannerImage(ctx, param.target, flags.GetReplyFormat())),
Commands.SystemClearBanner(var param, var flags) => ctx.Execute<SystemEdit>(SystemBannerImage, m => m.ClearBannerImage(ctx, ctx.System, flags.yes)),
Commands.SystemChangeBanner(var param, _) => ctx.Execute<SystemEdit>(SystemBannerImage, m => m.ChangeBannerImage(ctx, ctx.System, param.banner)),
_ => _ =>
// this should only ever occur when deving if commands are not implemented... // this should only ever occur when deving if commands are not implemented...
ctx.Reply( ctx.Reply(
@ -330,9 +343,7 @@ public partial class CommandTree
private async Task HandleSystemCommandTargeted(Context ctx, PKSystem target) private async Task HandleSystemCommandTargeted(Context ctx, PKSystem target)
{ {
if (ctx.Match("banner", "splash", "cover")) if (ctx.Match("list", "l", "members", "ls"))
await ctx.CheckSystem(target).Execute<SystemEdit>(SystemBannerImage, m => m.BannerImage(ctx, target));
else if (ctx.Match("list", "l", "members", "ls"))
await ctx.CheckSystem(target).Execute<SystemList>(SystemList, m => m.MemberList(ctx, target)); await ctx.CheckSystem(target).Execute<SystemList>(SystemList, m => m.MemberList(ctx, target));
else if (ctx.Match("find", "search", "query", "fd", "s")) else if (ctx.Match("find", "search", "query", "fd", "s"))
await ctx.CheckSystem(target).Execute<SystemList>(SystemFind, m => m.MemberList(ctx, target)); await ctx.CheckSystem(target).Execute<SystemList>(SystemFind, m => m.MemberList(ctx, target));

View file

@ -701,72 +701,77 @@ public class SystemEdit
: ctx.Reply(msg)); : ctx.Reply(msg));
} }
public async Task BannerImage(Context ctx, PKSystem target) public async Task ClearBannerImage(Context ctx, PKSystem target, bool flagConfirmYes)
{ {
ctx.CheckSystemPrivacy(target.Id, target.BannerPrivacy); ctx.CheckSystemPrivacy(target.Id, target.BannerPrivacy);
var isOwnSystem = target.Id == ctx.System?.Id;
if ((!ctx.HasNext() && ctx.Message.Attachments.Length == 0) || ctx.PeekMatchFormat() != ReplyFormat.Standard)
{
if ((target.BannerImage?.Trim() ?? "").Length > 0)
switch (ctx.MatchFormat())
{
case ReplyFormat.Raw:
await ctx.Reply($"`{target.BannerImage.TryGetCleanCdnUrl()}`");
break;
case ReplyFormat.Plaintext:
var ebP = new EmbedBuilder()
.Description($"Showing banner for system {target.NameFor(ctx)} (`{target.DisplayHid(ctx.Config)}`)");
await ctx.Reply(text: $"<{target.BannerImage.TryGetCleanCdnUrl()}>", embed: ebP.Build());
break;
default:
var ebS = new EmbedBuilder()
.Title("System banner image")
.Image(new Embed.EmbedImage(target.BannerImage.TryGetCleanCdnUrl()));
if (target.Id == ctx.System?.Id)
ebS.Description($"To clear, use `{ctx.DefaultPrefix}system banner clear`.");
await ctx.Reply(embed: ebS.Build());
break;
}
else
throw new PKSyntaxError("This system does not have a banner image set."
+ (isOwnSystem ? "Set one by attaching an image to this command, or by passing an image URL or @mention." : ""));
return;
}
ctx.CheckSystem().CheckOwnSystem(target); ctx.CheckSystem().CheckOwnSystem(target);
if (ctx.MatchClear() && await ctx.ConfirmClear("your system's banner image")) if (await ctx.ConfirmClear("your system's banner image", flagConfirmYes))
{ {
await ctx.Repository.UpdateSystem(target.Id, new SystemPatch { BannerImage = null }); await ctx.Repository.UpdateSystem(target.Id, new SystemPatch { BannerImage = null });
await ctx.Reply($"{Emojis.Success} System banner image cleared."); await ctx.Reply($"{Emojis.Success} System banner image cleared.");
} }
}
else if (await ctx.MatchImage() is { } img) public async Task ShowBannerImage(Context ctx, PKSystem target, ReplyFormat format)
{
ctx.CheckSystemPrivacy(target.Id, target.BannerPrivacy);
var isOwnSystem = target.Id == ctx.System?.Id;
if ((target.BannerImage?.Trim() ?? "").Length > 0)
{ {
img = await _avatarHosting.TryRehostImage(img, AvatarHostingService.RehostedImageType.Banner, ctx.Author.Id, ctx.System); switch (format)
await _avatarHosting.VerifyAvatarOrThrow(img.Url, true);
await ctx.Repository.UpdateSystem(target.Id, new SystemPatch { BannerImage = img.CleanUrl ?? img.Url });
var msg = img.Source switch
{ {
AvatarSource.Url => $"{Emojis.Success} System banner image changed to the image at the given URL.", case ReplyFormat.Raw:
AvatarSource.HostedCdn => $"{Emojis.Success} System banner image changed to attached image.", await ctx.Reply($"`{target.BannerImage.TryGetCleanCdnUrl()}`");
AvatarSource.Attachment => break;
$"{Emojis.Success} System banner image changed to attached image.\n{Emojis.Warn} If you delete the message containing the attachment, the banner image will stop working.", case ReplyFormat.Plaintext:
AvatarSource.User => throw new PKError("Cannot set a banner image to an user's avatar."), var ebP = new EmbedBuilder()
_ => throw new ArgumentOutOfRangeException() .Description($"Showing banner for system {target.NameFor(ctx)} (`{target.DisplayHid(ctx.Config)}`)");
}; await ctx.Reply(text: $"<{target.BannerImage.TryGetCleanCdnUrl()}>", embed: ebP.Build());
break;
// The attachment's already right there, no need to preview it. default:
var hasEmbed = img.Source != AvatarSource.Attachment && img.Source != AvatarSource.HostedCdn; var ebS = new EmbedBuilder()
await (hasEmbed .Title("System banner image")
? ctx.Reply(msg, new EmbedBuilder().Image(new Embed.EmbedImage(img.Url)).Build()) .Image(new Embed.EmbedImage(target.BannerImage.TryGetCleanCdnUrl()));
: ctx.Reply(msg)); if (target.Id == ctx.System?.Id)
ebS.Description($"To clear, use `{ctx.DefaultPrefix}system banner clear`.");
await ctx.Reply(embed: ebS.Build());
break;
}
} }
else
{
throw new PKSyntaxError("This system does not have a banner image set."
+ (isOwnSystem ? " Set one by attaching an image to this command, or by passing an image URL or @mention." : ""));
}
}
public async Task ChangeBannerImage(Context ctx, PKSystem target, ParsedImage img)
{
ctx.CheckSystemPrivacy(target.Id, target.BannerPrivacy);
ctx.CheckSystem().CheckOwnSystem(target);
img = await _avatarHosting.TryRehostImage(img, AvatarHostingService.RehostedImageType.Banner, ctx.Author.Id, ctx.System);
await _avatarHosting.VerifyAvatarOrThrow(img.Url, true);
await ctx.Repository.UpdateSystem(target.Id, new SystemPatch { BannerImage = img.CleanUrl ?? img.Url });
var msg = img.Source switch
{
AvatarSource.Url => $"{Emojis.Success} System banner image changed to the image at the given URL.",
AvatarSource.HostedCdn => $"{Emojis.Success} System banner image changed to attached image.",
AvatarSource.Attachment =>
$"{Emojis.Success} System 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 && img.Source != AvatarSource.HostedCdn;
await (hasEmbed
? ctx.Reply(msg, new EmbedBuilder().Image(new Embed.EmbedImage(img.Url)).Build())
: ctx.Reply(msg));
} }
public async Task Delete(Context ctx, PKSystem target) public async Task Delete(Context ctx, PKSystem target)

View file

@ -175,6 +175,23 @@ pub fn edit() -> impl Iterator<Item = Command> {
] ]
.into_iter(); .into_iter();
let system_banner = tokens!(system_target, ("banner", ["cover"]));
let system_banner_cmd =
[command!(system_banner => "system_show_banner").help("Shows the system's banner")]
.into_iter();
let system_banner_self = tokens!(system, ("banner", ["cover"]));
let system_banner_self_cmd = [
command!(system_banner_self => "system_show_banner_self")
.help("Shows your system's banner"),
command!(system_banner_self, ("clear", ["c"]) => "system_clear_banner")
.flag(("yes", ["y"]))
.help("Clears your system's banner"),
command!(system_banner_self, ("banner", Avatar) => "system_change_banner")
.help("Changes your system's banner"),
]
.into_iter();
system_new_cmd system_new_cmd
.chain(system_name_self_cmd) .chain(system_name_self_cmd)
.chain(system_server_name_self_cmd) .chain(system_server_name_self_cmd)
@ -185,6 +202,7 @@ pub fn edit() -> impl Iterator<Item = Command> {
.chain(system_pronouns_self_cmd) .chain(system_pronouns_self_cmd)
.chain(system_avatar_self_cmd) .chain(system_avatar_self_cmd)
.chain(system_server_avatar_self_cmd) .chain(system_server_avatar_self_cmd)
.chain(system_banner_self_cmd)
.chain(system_name_cmd) .chain(system_name_cmd)
.chain(system_server_name_cmd) .chain(system_server_name_cmd)
.chain(system_description_cmd) .chain(system_description_cmd)
@ -194,5 +212,6 @@ pub fn edit() -> impl Iterator<Item = Command> {
.chain(system_pronouns_cmd) .chain(system_pronouns_cmd)
.chain(system_avatar_cmd) .chain(system_avatar_cmd)
.chain(system_server_avatar_cmd) .chain(system_server_avatar_cmd)
.chain(system_banner_cmd)
.chain(system_info_cmd) .chain(system_info_cmd)
} }