Merge branch 'main' into proxyswitch-add

This commit is contained in:
Petal Ladenson 2024-12-05 18:18:04 -07:00 committed by GitHub
commit fbebe38afe
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
59 changed files with 1000 additions and 423 deletions

View file

@ -1,7 +1,7 @@
using System.Text;
using Humanizer;
using Myriad.Builders;
using NodaTime;
using NodaTime.Text;
using NodaTime.TimeZones;
@ -137,6 +137,25 @@ public class Config
ProxyMember.DefaultFormat
));
if (ctx.Guild == null)
{
items.Add(new(
"Server Name Format",
"Format string used to display a member's name in the current server",
"only available in servers",
"only available in servers"
));
}
else
{
items.Add(new(
"Server Name Format",
"Format string used to display a member's name in the current server",
(await ctx.Repository.GetSystemGuild(ctx.Guild.Id, ctx.System.Id)).NameFormat ?? "none set",
"none set"
));
}
await ctx.Paginate<PaginatedConfigItem>(
items.ToAsyncEnumerable(),
items.Count,
@ -599,6 +618,48 @@ public class Config
await ctx.Reply($"Member names are now formatted as `{formatString}`");
}
public async Task ServerNameFormat(Context ctx)
{
ctx.CheckGuildContext();
var clearFlag = ctx.MatchClear();
var format = ctx.MatchFormat();
// if there's nothing next or what's next is raw/plaintext and we're not clearing, it's a query
if ((!ctx.HasNext() || format != ReplyFormat.Standard) && !clearFlag)
{
var guildCfg = await ctx.Repository.GetSystemGuild(ctx.Guild.Id, ctx.System.Id);
if (guildCfg.NameFormat == null)
await ctx.Reply("You do not have a specific name format set for this server and member names are formatted with your global name format.");
else
switch (format)
{
case ReplyFormat.Raw:
await ctx.Reply($"`{guildCfg.NameFormat}`");
break;
case ReplyFormat.Plaintext:
var eb = new EmbedBuilder()
.Description($"Showing guild Name Format for system {ctx.System.DisplayHid(ctx.Config)}");
await ctx.Reply(guildCfg.NameFormat, eb.Build());
break;
default:
await ctx.Reply($"Your member names in this server are currently formatted as `{guildCfg.NameFormat}`");
break;
}
return;
}
string? formatString = null;
if (!clearFlag)
{
formatString = ctx.RemainderOrNull();
}
await ctx.Repository.UpdateSystemGuild(ctx.System.Id, ctx.Guild.Id, new() { NameFormat = formatString });
if (formatString == null)
await ctx.Reply($"Member names are now formatted with your global name format in this server.");
else
await ctx.Reply($"Member names are now formatted as `{formatString}` in this server.");
}
public Task LimitUpdate(Context ctx)
{
throw new PKError("You cannot update your own member or group limits. If you need a limit update, please join the " +

View file

@ -312,21 +312,28 @@ public class Groups
ctx.CheckSystemPrivacy(target.System, target.IconPrivacy);
if ((target.Icon?.Trim() ?? "").Length > 0)
{
var eb = new EmbedBuilder()
.Title("Group icon")
.Image(new Embed.EmbedImage(target.Icon.TryGetCleanCdnUrl()));
if (target.System == ctx.System?.Id)
eb.Description($"To clear, use `pk;group {target.Reference(ctx)} icon -clear`.");
await ctx.Reply(embed: eb.Build());
}
switch (ctx.MatchFormat())
{
case ReplyFormat.Raw:
await ctx.Reply($"`{target.Icon.TryGetCleanCdnUrl()}`");
break;
case ReplyFormat.Plaintext:
var ebP = new EmbedBuilder()
.Description($"Showing avatar for group {target.NameFor(ctx)} (`{target.DisplayHid(ctx.Config)}`)");
await ctx.Reply(text: $"<{target.Icon.TryGetCleanCdnUrl()}>", embed: ebP.Build());
break;
default:
var ebS = new EmbedBuilder()
.Title("Group icon")
.Image(new Embed.EmbedImage(target.Icon.TryGetCleanCdnUrl()));
if (target.System == ctx.System?.Id)
ebS.Description($"To clear, use `pk;group {target.Reference(ctx)} icon -clear`.");
await ctx.Reply(embed: ebS.Build());
break;
}
else
{
throw new PKSyntaxError(
"This group does not have an avatar set. Set one by attaching an image to this command, or by passing an image URL or @mention.");
}
}
if (ctx.MatchClear())
@ -378,22 +385,29 @@ public class Groups
{
ctx.CheckSystemPrivacy(target.System, target.BannerPrivacy);
if ((target.BannerImage?.Trim() ?? "").Length > 0)
{
var eb = new EmbedBuilder()
.Title("Group banner image")
.Image(new Embed.EmbedImage(target.BannerImage));
if (target.System == ctx.System?.Id)
eb.Description($"To clear, use `pk;group {target.Reference(ctx)} banner clear`.");
await ctx.Reply(embed: eb.Build());
}
if ((target.Icon?.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 group {target.NameFor(ctx)} (`{target.DisplayHid(ctx.Config)}`)");
await ctx.Reply(text: $"<{target.BannerImage.TryGetCleanCdnUrl()}>", embed: ebP.Build());
break;
default:
var ebS = new EmbedBuilder()
.Title("Group banner image")
.Image(new Embed.EmbedImage(target.BannerImage.TryGetCleanCdnUrl()));
if (target.System == ctx.System?.Id)
ebS.Description($"To clear, use `pk;group {target.Reference(ctx)} banner clear`.");
await ctx.Reply(embed: ebS.Build());
break;
}
else
{
throw new PKSyntaxError(
"This group does not have a banner image set. Set one by attaching an image to this command, or by passing an image URL.");
}
"This group 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 (ctx.MatchClear())

View file

@ -11,7 +11,7 @@ public class Help
{
Title = "PluralKit",
Description = "PluralKit is a bot designed for plural communities on Discord, and is open for anyone to use. It allows you to register systems, maintain system information, set up message proxying, log switches, and more.",
Footer = new("By @ske | Myriad design by @layl, art by @tedkalashnikov | GitHub: https://github.com/PluralKit/PluralKit/ | Website: https://pluralkit.me/"),
Footer = new("By @ske | Myriad design by @layl, icon by @tedkalashnikov, banner by @fulmine | GitHub: https://github.com/PluralKit/PluralKit/ | Website: https://pluralkit.me/"),
Color = DiscordUtils.Blue,
};

View file

@ -86,12 +86,28 @@ public class MemberAvatar
if (location == MemberAvatarLocation.Server)
field += $" (for {ctx.Guild.Name})";
var eb = new EmbedBuilder()
.Title($"{target.NameFor(ctx)}'s {field}")
.Image(new Embed.EmbedImage(currentValue?.TryGetCleanCdnUrl()));
if (target.System == ctx.System?.Id)
eb.Description($"To clear, use `pk;member {target.Reference(ctx)} {location.Command()} clear`.");
await ctx.Reply(embed: eb.Build());
var format = ctx.MatchFormat();
if (format == ReplyFormat.Raw)
{
await ctx.Reply($"`{currentValue?.TryGetCleanCdnUrl()}`");
}
else if (format == ReplyFormat.Plaintext)
{
var eb = new EmbedBuilder()
.Description($"Showing {field} link for member {target.NameFor(ctx)} (`{target.DisplayHid(ctx.Config)}`)");
await ctx.Reply($"<{currentValue?.TryGetCleanCdnUrl()}>", embed: eb.Build());
return;
}
else if (format == ReplyFormat.Standard)
{
var eb = new EmbedBuilder()
.Title($"{target.NameFor(ctx)}'s {field}")
.Image(new Embed.EmbedImage(currentValue?.TryGetCleanCdnUrl()));
if (target.System == ctx.System?.Id)
eb.Description($"To clear, use `pk;member {target.Reference(ctx)} {location.Command()} clear`.");
await ctx.Reply(embed: eb.Build());
}
else throw new PKError("Format Not Recognized");
}
public async Task ServerAvatar(Context ctx, PKMember target)

View file

@ -133,7 +133,7 @@ public class MemberEdit
{
var noPronounsSetMessage = "This member does not have pronouns set.";
if (ctx.System?.Id == target.System)
noPronounsSetMessage += $"To set some, type `pk;member {target.Reference(ctx)} pronouns <pronouns>`.";
noPronounsSetMessage += $" To set some, type `pk;member {target.Reference(ctx)} pronouns <pronouns>`.";
ctx.CheckSystemPrivacy(target.System, target.PronounPrivacy);
@ -194,16 +194,18 @@ public class MemberEdit
public async Task BannerImage(Context ctx, PKMember target)
{
ctx.CheckOwnMember(target);
async Task ClearBannerImage()
{
ctx.CheckOwnMember(target);
await ctx.ConfirmClear("this member's banner image");
await ctx.Repository.UpdateMember(target.Id, new MemberPatch { BannerImage = null });
await ctx.Reply($"{Emojis.Success} Member banner image cleared.");
}
async Task SetBannerImage(ParsedImage img)
{
ctx.CheckOwnMember(target);
img = await _avatarHosting.TryRehostImage(img, AvatarHostingService.RehostedImageType.Banner, ctx.Author.Id, ctx.System);
await AvatarUtils.VerifyAvatarOrThrow(_client, img.Url, true);
@ -229,21 +231,31 @@ public class MemberEdit
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.Reference(ctx)} banner clear`.");
await ctx.Reply(embed: eb.Build());
}
switch (ctx.MatchFormat())
{
case ReplyFormat.Raw:
await ctx.Reply($"`{target.BannerImage.TryGetCleanCdnUrl()}`");
break;
case ReplyFormat.Plaintext:
var ebP = new EmbedBuilder()
.Description($"Showing banner for member {target.NameFor(ctx)} (`{target.DisplayHid(ctx.Config)}`)");
await ctx.Reply(text: $"<{target.BannerImage.TryGetCleanCdnUrl()}>", embed: ebP.Build());
break;
default:
var ebS = new EmbedBuilder()
.Title($"{target.NameFor(ctx)}'s banner image")
.Image(new Embed.EmbedImage(target.BannerImage.TryGetCleanCdnUrl()));
if (target.System == ctx.System?.Id)
ebS.Description($"To clear, use `pk;member {target.Reference(ctx)} banner clear`.");
await ctx.Reply(embed: ebS.Build());
break;
}
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.");
}
"This member does not have a banner image set." + ((target.System == ctx.System?.Id) ? " Set one by attaching an image to this command, or by passing an image URL." : ""));
}
if (ctx.MatchClear() && await ctx.ConfirmClear("this member's banner image"))
if (ctx.MatchClear())
await ClearBannerImage();
else if (await ctx.MatchImage() is { } img)
await SetBannerImage(img);

View file

@ -118,7 +118,8 @@ public class ProxiedMessage
// Should we clear embeds?
var clearEmbeds = ctx.MatchFlag("clear-embed", "ce");
if (clearEmbeds && newContent == null)
var clearAttachments = ctx.MatchFlag("clear-attachments", "ca");
if ((clearEmbeds || clearAttachments) && newContent == null)
newContent = originalMsg.Content!;
if (newContent == null)
@ -218,7 +219,7 @@ public class ProxiedMessage
try
{
var editedMsg =
await _webhookExecutor.EditWebhookMessage(msg.Guild ?? 0, msg.Channel, msg.Mid, newContent, clearEmbeds);
await _webhookExecutor.EditWebhookMessage(msg.Guild ?? 0, msg.Channel, msg.Mid, newContent, clearEmbeds, clearAttachments);
if (ctx.Guild == null)
await _rest.CreateReaction(ctx.Channel.Id, ctx.Message.Id, new Emoji { Name = Emojis.Success });

View file

@ -181,7 +181,7 @@ public class ServerConfig
await ctx.Reply(
$"{Emojis.Success} Message logging for the given channels {(enable ? "enabled" : "disabled")}." +
(logChannel == null
? $"\n{Emojis.Warn} Please note that no logging channel is set, so there is nowhere to log messages to. You can set a logging channel using `pk;log channel #your-log-channel`."
? $"\n{Emojis.Warn} Please note that no logging channel is set, so there is nowhere to log messages to. You can set a logging channel using `pk;serverconfig log channel #your-log-channel`."
: ""));
}
@ -351,7 +351,10 @@ public class ServerConfig
await ctx.Repository.UpdateGuild(ctx.Guild.Id, new GuildPatch { LogBlacklist = blacklist.ToArray() });
await ctx.Reply(
$"{Emojis.Success} Channels {(shouldAdd ? "added to" : "removed from")} the logging blacklist.");
$"{Emojis.Success} Channels {(shouldAdd ? "added to" : "removed from")} the logging blacklist." +
(guild.LogChannel == null
? $"\n{Emojis.Warn} Please note that no logging channel is set, so there is nowhere to log messages to. You can set a logging channel using `pk;serverconfig log channel #your-log-channel`."
: ""));
}
public async Task SetLogCleanup(Context ctx)

View file

@ -565,19 +565,28 @@ public class SystemEdit
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());
}
switch (ctx.MatchFormat())
{
case ReplyFormat.Raw:
await ctx.Reply($"`{target.AvatarUrl.TryGetCleanCdnUrl()}`");
break;
case ReplyFormat.Plaintext:
var ebP = new EmbedBuilder()
.Description($"Showing icon for system {target.NameFor(ctx)} (`{target.DisplayHid(ctx.Config)}`)");
await ctx.Reply(text: $"<{target.AvatarUrl.TryGetCleanCdnUrl()}>", embed: ebP.Build());
break;
default:
var ebS = new EmbedBuilder()
.Title("System icon")
.Image(new Embed.EmbedImage(target.AvatarUrl.TryGetCleanCdnUrl()));
if (target.Id == ctx.System?.Id)
ebS.Description("To clear, use `pk;system icon clear`.");
await ctx.Reply(embed: ebS.Build());
break;
}
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)
@ -639,19 +648,28 @@ public class SystemEdit
var settings = await ctx.Repository.GetSystemGuild(ctx.Guild.Id, target.Id);
if ((settings.AvatarUrl?.Trim() ?? "").Length > 0)
{
var eb = new EmbedBuilder()
.Title("System server icon")
.Image(new Embed.EmbedImage(settings.AvatarUrl.TryGetCleanCdnUrl()));
if (target.Id == ctx.System?.Id)
eb.Description("To clear, use `pk;system servericon clear`.");
await ctx.Reply(embed: eb.Build());
}
switch (ctx.MatchFormat())
{
case ReplyFormat.Raw:
await ctx.Reply($"`{settings.AvatarUrl.TryGetCleanCdnUrl()}`");
break;
case ReplyFormat.Plaintext:
var ebP = new EmbedBuilder()
.Description($"Showing icon for system {target.NameFor(ctx)} (`{target.DisplayHid(ctx.Config)}`)");
await ctx.Reply(text: $"<{settings.AvatarUrl.TryGetCleanCdnUrl()}>", embed: ebP.Build());
break;
default:
var ebS = new EmbedBuilder()
.Title("System server icon")
.Image(new Embed.EmbedImage(settings.AvatarUrl.TryGetCleanCdnUrl()));
if (target.Id == ctx.System?.Id)
ebS.Description("To clear, use `pk;system servericon clear`.");
await ctx.Reply(embed: ebS.Build());
break;
}
else
{
throw new PKSyntaxError(
"This system does not have a icon specific to this server. Set one by attaching an image to this command, or by passing an image URL or @mention.");
}
}
ctx.CheckGuildContext();
@ -676,24 +694,31 @@ public class SystemEdit
var isOwnSystem = target.Id == ctx.System?.Id;
if (!ctx.HasNext() && ctx.Message.Attachments.Length == 0)
if ((!ctx.HasNext() && ctx.Message.Attachments.Length == 0) || ctx.PeekMatchFormat() != ReplyFormat.Standard)
{
if ((target.BannerImage?.Trim() ?? "").Length > 0)
{
var eb = new EmbedBuilder()
.Title("System banner image")
.Image(new Embed.EmbedImage(target.BannerImage));
if (isOwnSystem)
eb.Description("To clear, use `pk;system banner clear`.");
await ctx.Reply(embed: eb.Build());
}
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 `pk;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;
}
@ -842,7 +867,7 @@ public class SystemEdit
.Field(new Embed.Field("Current fronter(s)", target.FrontPrivacy.Explanation()))
.Field(new Embed.Field("Front/switch history", target.FrontHistoryPrivacy.Explanation()))
.Description(
"To edit privacy settings, use the command:\n`pk;system privacy <subject> <level>`\n\n- `subject` is one of `name`, `avatar`, `description`, `banner`, `list`, `front`, `fronthistory`, `groups`, or `all` \n- `level` is either `public` or `private`.");
"To edit privacy settings, use the command:\n`pk;system privacy <subject> <level>`\n\n- `subject` is one of `name`, `avatar`, `description`, `banner`, `pronouns`, `list`, `front`, `fronthistory`, `groups`, or `all` \n- `level` is either `public` or `private`.");
return ctx.Reply(embed: eb.Build());
}