mirror of
https://github.com/PluralKit/PluralKit.git
synced 2026-02-08 06:47:56 +00:00
Merge branch 'main' into proxyswitch-add
This commit is contained in:
commit
fbebe38afe
59 changed files with 1000 additions and 423 deletions
|
|
@ -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 " +
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 });
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue