feat: implement system privacy commands (yay system edit done)

This commit is contained in:
dusk 2025-04-04 06:14:17 +09:00
parent cb0a9eaf9f
commit 3eece261fd
No known key found for this signature in database
10 changed files with 154 additions and 75 deletions

View file

@ -100,6 +100,9 @@ public partial class CommandTree
Commands.SystemShowProxy(var param, _) => ctx.Execute<SystemEdit>(SystemProxy, m => m.ShowSystemProxy(ctx, param.target)),
Commands.SystemToggleProxyCurrent(var param, _) => ctx.Execute<SystemEdit>(SystemProxy, m => m.ToggleSystemProxy(ctx, ctx.Guild, param.toggle)),
Commands.SystemToggleProxy(var param, _) => ctx.Execute<SystemEdit>(SystemProxy, m => m.ToggleSystemProxy(ctx, param.target, param.toggle)),
Commands.SystemShowPrivacy(var param, _) => ctx.Execute<SystemEdit>(SystemPrivacy, m => m.ShowSystemPrivacy(ctx, ctx.System)),
Commands.SystemChangePrivacyAll(var param, _) => ctx.Execute<SystemEdit>(SystemPrivacy, m => m.ChangeSystemPrivacyAll(ctx, ctx.System, param.level)),
Commands.SystemChangePrivacy(var param, _) => ctx.Execute<SystemEdit>(SystemPrivacy, m => m.ChangeSystemPrivacy(ctx, ctx.System, param.privacy, param.level)),
_ =>
// this should only ever occur when deving if commands are not implemented...
ctx.Reply(
@ -363,8 +366,6 @@ public partial class CommandTree
await ctx.CheckSystem(target).Execute<SystemFront>(SystemFrontPercent, m => m.FrontPercent(ctx, system: target));
else if (ctx.Match("groups", "gs"))
await ctx.CheckSystem(target).Execute<Groups>(GroupList, g => g.ListSystemGroups(ctx, target));
else if (ctx.Match("privacy"))
await ctx.CheckSystem(target).Execute<SystemEdit>(SystemPrivacy, m => m.SystemPrivacy(ctx, target));
else if (ctx.Match("id"))
await ctx.CheckSystem(target).Execute<System>(SystemId, m => m.DisplayId(ctx, target));
else if (ctx.Match("random", "rand", "r"))

View file

@ -36,7 +36,7 @@ public static class ContextFlagsExt
);
}
public static async Task<string?> FlagResolvePrivacyLevel(this Context ctx, string param_name)
public static async Task<PrivacyLevel?> FlagResolvePrivacyLevel(this Context ctx, string param_name)
{
return await ctx.Parameters.ResolveFlag(
ctx, param_name,

View file

@ -36,7 +36,15 @@ public static class ContextParametersExt
);
}
public static async Task<string?> ParamResolvePrivacyLevel(this Context ctx, string param_name)
public static async Task<SystemPrivacySubject?> ParamResolveSystemPrivacyTarget(this Context ctx, string param_name)
{
return await ctx.Parameters.ResolveParameter(
ctx, param_name,
param => (param as Parameter.SystemPrivacyTarget)?.target
);
}
public static async Task<PrivacyLevel?> ParamResolvePrivacyLevel(this Context ctx, string param_name)
{
return await ctx.Parameters.ResolveParameter(
ctx, param_name,

View file

@ -1,4 +1,3 @@
using System.Diagnostics;
using Myriad.Types;
using PluralKit.Core;
using uniffi.commands;
@ -12,7 +11,8 @@ public abstract record Parameter()
public record SystemRef(PKSystem system): Parameter;
public record GuildRef(Guild guild): Parameter;
public record MemberPrivacyTarget(MemberPrivacySubject target): Parameter;
public record PrivacyLevel(string level): Parameter;
public record SystemPrivacyTarget(SystemPrivacySubject target): Parameter;
public record PrivacyLevel(Core.PrivacyLevel level): Parameter;
public record Toggle(bool value): Parameter;
public record Opaque(string value): Parameter;
public record Avatar(ParsedImage avatar): Parameter;
@ -72,12 +72,16 @@ public class Parameters
);
case uniffi.commands.Parameter.MemberPrivacyTarget memberPrivacyTarget:
// this should never really fail...
// todo: we shouldn't have *three* different MemberPrivacyTarget types (rust, ffi, c#) syncing the cases will be annoying...
if (!MemberPrivacyUtils.TryParseMemberPrivacy(memberPrivacyTarget.target, out var target))
if (!MemberPrivacyUtils.TryParseMemberPrivacy(memberPrivacyTarget.target, out var memberPrivacy))
throw new PKError($"Invalid member privacy target {memberPrivacyTarget.target}");
return new Parameter.MemberPrivacyTarget(target);
return new Parameter.MemberPrivacyTarget(memberPrivacy);
case uniffi.commands.Parameter.SystemPrivacyTarget systemPrivacyTarget:
// this should never really fail...
if (!SystemPrivacyUtils.TryParseSystemPrivacy(systemPrivacyTarget.target, out var systemPrivacy))
throw new PKError($"Invalid system privacy target {systemPrivacyTarget.target}");
return new Parameter.SystemPrivacyTarget(systemPrivacy);
case uniffi.commands.Parameter.PrivacyLevel privacyLevel:
return new Parameter.PrivacyLevel(privacyLevel.level);
return new Parameter.PrivacyLevel(privacyLevel.level == "public" ? PrivacyLevel.Public : privacyLevel.level == "private" ? PrivacyLevel.Private : throw new PKError($"Invalid privacy level {privacyLevel.level}"));
case uniffi.commands.Parameter.Toggle toggle:
return new Parameter.Toggle(toggle.toggle);
case uniffi.commands.Parameter.OpaqueString opaque:

View file

@ -874,79 +874,72 @@ public class SystemEdit
$"Proxying in {serverText} is currently **disabled** for your system. To enable it, type `{ctx.DefaultPrefix}system proxy on`.");
}
public async Task SystemPrivacy(Context ctx, PKSystem target)
public async Task ShowSystemPrivacy(Context ctx, PKSystem target)
{
ctx.CheckSystem().CheckOwnSystem(target);
Task PrintEmbed()
var eb = new EmbedBuilder()
.Title("Current privacy settings for your system")
.Field(new Embed.Field("Name", target.NamePrivacy.Explanation()))
.Field(new Embed.Field("Avatar", target.AvatarPrivacy.Explanation()))
.Field(new Embed.Field("Description", target.DescriptionPrivacy.Explanation()))
.Field(new Embed.Field("Banner", target.BannerPrivacy.Explanation()))
.Field(new Embed.Field("Pronouns", target.PronounPrivacy.Explanation()))
.Field(new Embed.Field("Member list", target.MemberListPrivacy.Explanation()))
.Field(new Embed.Field("Group list", target.GroupListPrivacy.Explanation()))
.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`{ctx.DefaultPrefix}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`.");
await ctx.Reply(embed: eb.Build());
}
public async Task ChangeSystemPrivacy(Context ctx, PKSystem target, SystemPrivacySubject subject, PrivacyLevel level)
{
ctx.CheckSystem().CheckOwnSystem(target);
await ctx.Repository.UpdateSystem(target.Id, new SystemPatch().WithPrivacy(subject, level));
var levelExplanation = level switch
{
var eb = new EmbedBuilder()
.Title("Current privacy settings for your system")
.Field(new Embed.Field("Name", target.NamePrivacy.Explanation()))
.Field(new Embed.Field("Avatar", target.AvatarPrivacy.Explanation()))
.Field(new Embed.Field("Description", target.DescriptionPrivacy.Explanation()))
.Field(new Embed.Field("Banner", target.BannerPrivacy.Explanation()))
.Field(new Embed.Field("Pronouns", target.PronounPrivacy.Explanation()))
.Field(new Embed.Field("Member list", target.MemberListPrivacy.Explanation()))
.Field(new Embed.Field("Group list", target.GroupListPrivacy.Explanation()))
.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`{ctx.DefaultPrefix}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());
}
PrivacyLevel.Public => "be able to query",
PrivacyLevel.Private => "*not* be able to query",
_ => ""
};
async Task SetLevel(SystemPrivacySubject subject, PrivacyLevel level)
var subjectStr = subject switch
{
await ctx.Repository.UpdateSystem(target.Id, new SystemPatch().WithPrivacy(subject, level));
SystemPrivacySubject.Name => "name",
SystemPrivacySubject.Avatar => "avatar",
SystemPrivacySubject.Description => "description",
SystemPrivacySubject.Banner => "banner",
SystemPrivacySubject.Pronouns => "pronouns",
SystemPrivacySubject.Front => "front",
SystemPrivacySubject.FrontHistory => "front history",
SystemPrivacySubject.MemberList => "member list",
SystemPrivacySubject.GroupList => "group list",
_ => ""
};
var levelExplanation = level switch
{
PrivacyLevel.Public => "be able to query",
PrivacyLevel.Private => "*not* be able to query",
_ => ""
};
var msg = $"System {subjectStr} privacy has been set to **{level.LevelName()}**. Other accounts will now {levelExplanation} your system {subjectStr}.";
await ctx.Reply($"{Emojis.Success} {msg}");
}
var subjectStr = subject switch
{
SystemPrivacySubject.Name => "name",
SystemPrivacySubject.Avatar => "avatar",
SystemPrivacySubject.Description => "description",
SystemPrivacySubject.Banner => "banner",
SystemPrivacySubject.Pronouns => "pronouns",
SystemPrivacySubject.Front => "front",
SystemPrivacySubject.FrontHistory => "front history",
SystemPrivacySubject.MemberList => "member list",
SystemPrivacySubject.GroupList => "group list",
_ => ""
};
public async Task ChangeSystemPrivacyAll(Context ctx, PKSystem target, PrivacyLevel level)
{
ctx.CheckSystem().CheckOwnSystem(target);
var msg =
$"System {subjectStr} privacy has been set to **{level.LevelName()}**. Other accounts will now {levelExplanation} your system {subjectStr}.";
await ctx.Reply($"{Emojis.Success} {msg}");
}
await ctx.Repository.UpdateSystem(target.Id, new SystemPatch().WithAllPrivacy(level));
async Task SetAll(PrivacyLevel level)
var msg = level switch
{
await ctx.Repository.UpdateSystem(target.Id, new SystemPatch().WithAllPrivacy(level));
PrivacyLevel.Private =>
$"All system privacy settings have been set to **{level.LevelName()}**. Other accounts will now not be able to view your member list, group list, front history, or system description.",
PrivacyLevel.Public =>
$"All system privacy settings have been set to **{level.LevelName()}**. Other accounts will now be able to view everything.",
_ => ""
};
var msg = level switch
{
PrivacyLevel.Private =>
$"All system privacy settings have been set to **{level.LevelName()}**. Other accounts will now not be able to view your member list, group list, front history, or system description.",
PrivacyLevel.Public =>
$"All system privacy settings have been set to **{level.LevelName()}**. Other accounts will now be able to view everything.",
_ => ""
};
await ctx.Reply($"{Emojis.Success} {msg}");
}
if (!ctx.HasNext())
await PrintEmbed();
else if (ctx.Match("all"))
await SetAll(ctx.PopPrivacyLevel());
else
await SetLevel(ctx.PopSystemPrivacySubject(), ctx.PopPrivacyLevel());
await ctx.Reply($"{Emojis.Success} {msg}");
}
}

View file

@ -202,12 +202,25 @@ pub fn edit() -> impl Iterator<Item = Command> {
let system_proxy = tokens!(system, "proxy");
let system_proxy_cmd = [
command!(system_proxy => "system_show_proxy_current").help("Shows your system's proxy setting for the guild you are in"),
command!(system_proxy => "system_show_proxy_current")
.help("Shows your system's proxy setting for the guild you are in"),
command!(system_proxy, Toggle => "system_toggle_proxy_current")
.help("Toggle your system's proxy for the guild you are in"),
command!(system_proxy, GuildRef => "system_show_proxy").help("Shows your system's proxy setting for a guild"),
command!(system_proxy, GuildRef => "system_show_proxy")
.help("Shows your system's proxy setting for a guild"),
command!(system_proxy, GuildRef, Toggle => "system_toggle_proxy")
.help("Toggle your system's proxy for a guild"),
]
.into_iter();
let system_privacy = tokens!(system, ("privacy", ["priv"]));
let system_privacy_cmd = [
command!(system_privacy => "system_show_privacy")
.help("Shows your system's privacy settings"),
command!(system_privacy, ("all", ["a"]), ("level", PrivacyLevel) => "system_change_privacy_all")
.help("Changes all privacy settings for your system"),
command!(system_privacy, ("privacy", SystemPrivacyTarget), ("level", PrivacyLevel) => "system_change_privacy")
.help("Changes a specific privacy setting for your system"),
].into_iter();
system_new_cmd
@ -222,6 +235,7 @@ pub fn edit() -> impl Iterator<Item = Command> {
.chain(system_server_avatar_self_cmd)
.chain(system_banner_self_cmd)
.chain(system_delete)
.chain(system_privacy_cmd)
.chain(system_proxy_cmd)
.chain(system_name_cmd)
.chain(system_server_name_cmd)

View file

@ -14,6 +14,7 @@ pub enum ParameterValue {
SystemRef(String),
GuildRef(String),
MemberPrivacyTarget(String),
SystemPrivacyTarget(String),
PrivacyLevel(String),
Toggle(bool),
Avatar(String),
@ -45,6 +46,7 @@ impl Display for Parameter {
ParameterKind::SystemRef => write!(f, "<target system>"),
ParameterKind::GuildRef => write!(f, "<target guild>"),
ParameterKind::MemberPrivacyTarget => write!(f, "<privacy target>"),
ParameterKind::SystemPrivacyTarget => write!(f, "<privacy target>"),
ParameterKind::PrivacyLevel => write!(f, "[privacy level]"),
ParameterKind::Toggle => write!(f, "on/off"),
ParameterKind::Avatar => write!(f, "<url|@mention>"),
@ -78,6 +80,7 @@ pub enum ParameterKind {
SystemRef,
GuildRef,
MemberPrivacyTarget,
SystemPrivacyTarget,
PrivacyLevel,
Toggle,
Avatar,
@ -92,6 +95,7 @@ impl ParameterKind {
ParameterKind::SystemRef => "target",
ParameterKind::GuildRef => "target",
ParameterKind::MemberPrivacyTarget => "member_privacy_target",
ParameterKind::SystemPrivacyTarget => "system_privacy_target",
ParameterKind::PrivacyLevel => "privacy_level",
ParameterKind::Toggle => "toggle",
ParameterKind::Avatar => "avatar",
@ -112,6 +116,9 @@ impl ParameterKind {
ParameterKind::SystemRef => Ok(ParameterValue::SystemRef(input.into())),
ParameterKind::MemberPrivacyTarget => MemberPrivacyTargetKind::from_str(input)
.map(|target| ParameterValue::MemberPrivacyTarget(target.as_ref().into())),
ParameterKind::SystemPrivacyTarget => SystemPrivacyTargetKind::from_str(input).map(
|target| ParameterValue::SystemPrivacyTarget(target.as_ref().into()),
),
ParameterKind::PrivacyLevel => PrivacyLevelKind::from_str(input)
.map(|level| ParameterValue::PrivacyLevel(level.as_ref().into())),
ParameterKind::Toggle => {
@ -176,6 +183,53 @@ impl FromStr for MemberPrivacyTargetKind {
}
}
pub enum SystemPrivacyTargetKind {
Name,
Avatar,
Description,
Banner,
Pronouns,
MemberList,
GroupList,
Front,
FrontHistory,
}
impl AsRef<str> for SystemPrivacyTargetKind {
fn as_ref(&self) -> &str {
match self {
Self::Name => "name",
Self::Avatar => "avatar",
Self::Description => "description",
Self::Banner => "banner",
Self::Pronouns => "pronouns",
Self::MemberList => "members",
Self::GroupList => "groups",
Self::Front => "front",
Self::FrontHistory => "fronthistory",
}
}
}
impl FromStr for SystemPrivacyTargetKind {
type Err = SmolStr;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_lowercase().as_str() {
"name" => Ok(Self::Name),
"avatar" | "pfp" | "pic" | "icon" => Ok(Self::Avatar),
"description" | "desc" | "bio" | "info" => Ok(Self::Description),
"banner" | "splash" | "cover" => Ok(Self::Banner),
"pronouns" | "prns" | "pn" => Ok(Self::Pronouns),
"members" | "memberlist" | "list" => Ok(Self::MemberList),
"groups" | "gs" => Ok(Self::GroupList),
"front" | "fronter" | "fronters" => Ok(Self::Front),
"fronthistory" | "fh" | "switches" => Ok(Self::FrontHistory),
_ => Err("invalid system privacy target".into()),
}
}
}
pub enum PrivacyLevelKind {
Public,
Private,

View file

@ -169,7 +169,8 @@ fn get_param_ty(kind: ParameterKind) -> &'static str {
ParameterKind::MemberRef => "PKMember",
ParameterKind::SystemRef => "PKSystem",
ParameterKind::MemberPrivacyTarget => "MemberPrivacySubject",
ParameterKind::PrivacyLevel => "string",
ParameterKind::SystemPrivacyTarget => "SystemPrivacySubject",
ParameterKind::PrivacyLevel => "PrivacyLevel",
ParameterKind::Toggle => "bool",
ParameterKind::Avatar => "ParsedImage",
ParameterKind::GuildRef => "Guild",
@ -182,6 +183,7 @@ fn get_param_param_ty(kind: ParameterKind) -> &'static str {
ParameterKind::MemberRef => "Member",
ParameterKind::SystemRef => "System",
ParameterKind::MemberPrivacyTarget => "MemberPrivacyTarget",
ParameterKind::SystemPrivacyTarget => "SystemPrivacyTarget",
ParameterKind::PrivacyLevel => "PrivacyLevel",
ParameterKind::Toggle => "Toggle",
ParameterKind::Avatar => "Avatar",

View file

@ -12,6 +12,7 @@ interface Parameter {
SystemRef(string system);
GuildRef(string guild);
MemberPrivacyTarget(string target);
SystemPrivacyTarget(string target);
PrivacyLevel(string level);
OpaqueString(string raw);
Toggle(boolean toggle);

View file

@ -26,6 +26,7 @@ pub enum Parameter {
SystemRef { system: String },
GuildRef { guild: String },
MemberPrivacyTarget { target: String },
SystemPrivacyTarget { target: String },
PrivacyLevel { level: String },
OpaqueString { raw: String },
Toggle { toggle: bool },
@ -38,6 +39,7 @@ impl From<ParameterValue> for Parameter {
ParameterValue::MemberRef(member) => Self::MemberRef { member },
ParameterValue::SystemRef(system) => Self::SystemRef { system },
ParameterValue::MemberPrivacyTarget(target) => Self::MemberPrivacyTarget { target },
ParameterValue::SystemPrivacyTarget(target) => Self::SystemPrivacyTarget { target },
ParameterValue::PrivacyLevel(level) => Self::PrivacyLevel { level },
ParameterValue::OpaqueString(raw) => Self::OpaqueString { raw },
ParameterValue::Toggle(toggle) => Self::Toggle { toggle },