diff --git a/PluralKit.Core/Models/ModelTypes/APIVersion.cs b/PluralKit.Core/Models/ModelTypes/APIVersion.cs new file mode 100644 index 00000000..53670654 --- /dev/null +++ b/PluralKit.Core/Models/ModelTypes/APIVersion.cs @@ -0,0 +1,8 @@ +namespace PluralKit.Core +{ + public enum APIVersion + { + V1, + V2, + } +} \ No newline at end of file diff --git a/PluralKit.Core/Models/PKGroup.cs b/PluralKit.Core/Models/PKGroup.cs index a08425fe..e34b1585 100644 --- a/PluralKit.Core/Models/PKGroup.cs +++ b/PluralKit.Core/Models/PKGroup.cs @@ -61,12 +61,16 @@ namespace PluralKit.Core public static string? IconFor(this PKGroup group, LookupContext ctx) => group.IconPrivacy.Get(ctx, group.Icon?.TryGetCleanCdnUrl()); - public static JObject ToJson(this PKGroup group, LookupContext ctx, bool isExport = false) + public static JObject ToJson(this PKGroup group, LookupContext ctx, string? systemStr = null, bool isExport = false) { var o = new JObject(); o.Add("id", group.Hid); o.Add("name", group.Name); + + if (systemStr != null) + o.Add("system", systemStr); + o.Add("display_name", group.DisplayName); o.Add("description", group.DescriptionPrivacy.Get(ctx, group.Description)); o.Add("icon", group.Icon); diff --git a/PluralKit.Core/Models/PKMember.cs b/PluralKit.Core/Models/PKMember.cs index b8e555f0..26c41fc7 100644 --- a/PluralKit.Core/Models/PKMember.cs +++ b/PluralKit.Core/Models/PKMember.cs @@ -106,13 +106,17 @@ namespace PluralKit.Core public static int MessageCountFor(this PKMember member, LookupContext ctx) => member.MetadataPrivacy.Get(ctx, member.MessageCount); - public static JObject ToJson(this PKMember member, LookupContext ctx, bool needsLegacyProxyTags = false) + public static JObject ToJson(this PKMember member, LookupContext ctx, bool needsLegacyProxyTags = false, string systemStr = null, APIVersion v = APIVersion.V1) { var includePrivacy = ctx == LookupContext.ByOwner; var o = new JObject(); o.Add("id", member.Hid); o.Add("name", member.NameFor(ctx)); + + if (systemStr != null && v == APIVersion.V2) + o.Add("system", systemStr); + // o.Add("color", member.ColorPrivacy.CanAccess(ctx) ? member.Color : null); o.Add("color", member.Color); o.Add("display_name", member.NamePrivacy.CanAccess(ctx) ? member.DisplayName : null); @@ -121,32 +125,59 @@ namespace PluralKit.Core o.Add("avatar_url", member.AvatarFor(ctx).TryGetCleanCdnUrl()); o.Add("banner", member.DescriptionPrivacy.Get(ctx, member.BannerImage).TryGetCleanCdnUrl()); o.Add("description", member.DescriptionFor(ctx)); + o.Add("created", member.CreatedFor(ctx)?.FormatExport()); + o.Add("keep_proxy", member.KeepProxy); var tagArray = new JArray(); foreach (var tag in member.ProxyTags) tagArray.Add(new JObject { { "prefix", tag.Prefix }, { "suffix", tag.Suffix } }); o.Add("proxy_tags", tagArray); - o.Add("keep_proxy", member.KeepProxy); - - o.Add("privacy", includePrivacy ? (member.MemberVisibility.LevelName()) : null); - - o.Add("visibility", includePrivacy ? (member.MemberVisibility.LevelName()) : null); - o.Add("name_privacy", includePrivacy ? (member.NamePrivacy.LevelName()) : null); - o.Add("description_privacy", includePrivacy ? (member.DescriptionPrivacy.LevelName()) : null); - o.Add("birthday_privacy", includePrivacy ? (member.BirthdayPrivacy.LevelName()) : null); - o.Add("pronoun_privacy", includePrivacy ? (member.PronounPrivacy.LevelName()) : null); - o.Add("avatar_privacy", includePrivacy ? (member.AvatarPrivacy.LevelName()) : null); - // o.Add("color_privacy", ctx == LookupContext.ByOwner ? (member.ColorPrivacy.LevelName()) : null); - o.Add("metadata_privacy", includePrivacy ? (member.MetadataPrivacy.LevelName()) : null); - - o.Add("created", member.CreatedFor(ctx)?.FormatExport()); - - if (member.ProxyTags.Count > 0 && needsLegacyProxyTags) + switch (v) { - // Legacy compatibility only, TODO: remove at some point - o.Add("prefix", member.ProxyTags?.FirstOrDefault().Prefix); - o.Add("suffix", member.ProxyTags?.FirstOrDefault().Suffix); + case APIVersion.V1: + { + o.Add("privacy", includePrivacy ? (member.MemberVisibility.LevelName()) : null); + + o.Add("visibility", includePrivacy ? (member.MemberVisibility.LevelName()) : null); + o.Add("name_privacy", includePrivacy ? (member.NamePrivacy.LevelName()) : null); + o.Add("description_privacy", includePrivacy ? (member.DescriptionPrivacy.LevelName()) : null); + o.Add("birthday_privacy", includePrivacy ? (member.BirthdayPrivacy.LevelName()) : null); + o.Add("pronoun_privacy", includePrivacy ? (member.PronounPrivacy.LevelName()) : null); + o.Add("avatar_privacy", includePrivacy ? (member.AvatarPrivacy.LevelName()) : null); + // o.Add("color_privacy", ctx == LookupContext.ByOwner ? (member.ColorPrivacy.LevelName()) : null); + o.Add("metadata_privacy", includePrivacy ? (member.MetadataPrivacy.LevelName()) : null); + + if (member.ProxyTags.Count > 0 && needsLegacyProxyTags) + { + // Legacy compatibility only, TODO: remove at some point + o.Add("prefix", member.ProxyTags?.FirstOrDefault().Prefix); + o.Add("suffix", member.ProxyTags?.FirstOrDefault().Suffix); + } + + break; + } + case APIVersion.V2: + { + if (includePrivacy) + { + var p = new JObject(); + + p.Add("visibility", member.MemberVisibility.ToJsonString()); + p.Add("name_privacy", member.NamePrivacy.ToJsonString()); + p.Add("description_privacy", member.DescriptionPrivacy.ToJsonString()); + p.Add("birthday_privacy", member.BirthdayPrivacy.ToJsonString()); + p.Add("pronoun_privacy", member.PronounPrivacy.ToJsonString()); + p.Add("avatar_privacy", member.AvatarPrivacy.ToJsonString()); + p.Add("metadata_privacy", member.MetadataPrivacy.ToJsonString()); + + o.Add("privacy", p); + } + else + o.Add("privacy", null); + + break; + } } return o; diff --git a/PluralKit.Core/Models/PKSystem.cs b/PluralKit.Core/Models/PKSystem.cs index fb0c7d07..3e2f2b38 100644 --- a/PluralKit.Core/Models/PKSystem.cs +++ b/PluralKit.Core/Models/PKSystem.cs @@ -66,7 +66,7 @@ namespace PluralKit.Core public static string DescriptionFor(this PKSystem system, LookupContext ctx) => system.DescriptionPrivacy.Get(ctx, system.Description); - public static JObject ToJson(this PKSystem system, LookupContext ctx) + public static JObject ToJson(this PKSystem system, LookupContext ctx, APIVersion v = APIVersion.V1) { var o = new JObject(); o.Add("id", system.Hid); @@ -77,13 +77,42 @@ namespace PluralKit.Core o.Add("banner", system.DescriptionPrivacy.Get(ctx, system.BannerImage).TryGetCleanCdnUrl()); o.Add("color", system.Color); o.Add("created", system.Created.FormatExport()); - // todo: change this to "timezone" - o.Add("tz", system.UiTz); - // todo: just don't include these if not ByOwner - o.Add("description_privacy", ctx == LookupContext.ByOwner ? system.DescriptionPrivacy.ToJsonString() : null); - o.Add("member_list_privacy", ctx == LookupContext.ByOwner ? system.MemberListPrivacy.ToJsonString() : null); - o.Add("front_privacy", ctx == LookupContext.ByOwner ? system.FrontPrivacy.ToJsonString() : null); - o.Add("front_history_privacy", ctx == LookupContext.ByOwner ? system.FrontHistoryPrivacy.ToJsonString() : null); + + switch (v) + { + case APIVersion.V1: + { + o.Add("tz", system.UiTz); + + o.Add("description_privacy", ctx == LookupContext.ByOwner ? system.DescriptionPrivacy.ToJsonString() : null); + o.Add("member_list_privacy", ctx == LookupContext.ByOwner ? system.MemberListPrivacy.ToJsonString() : null); + o.Add("front_privacy", ctx == LookupContext.ByOwner ? system.FrontPrivacy.ToJsonString() : null); + o.Add("front_history_privacy", ctx == LookupContext.ByOwner ? system.FrontHistoryPrivacy.ToJsonString() : null); + + break; + } + case APIVersion.V2: + { + o.Add("timezone", system.UiTz); + + if (ctx == LookupContext.ByOwner) + { + var p = new JObject(); + + p.Add("description_privacy", system.DescriptionPrivacy.ToJsonString()); + p.Add("member_list_privacy", system.MemberListPrivacy.ToJsonString()); + p.Add("front_privacy", system.FrontPrivacy.ToJsonString()); + p.Add("front_history_privacy", system.FrontHistoryPrivacy.ToJsonString()); + + o.Add("privacy", p); + } + else + o.Add("privacy", null); + + break; + } + } + return o; } } diff --git a/PluralKit.Core/Models/Patch/MemberPatch.cs b/PluralKit.Core/Models/Patch/MemberPatch.cs index b69577cd..d18c8772 100644 --- a/PluralKit.Core/Models/Patch/MemberPatch.cs +++ b/PluralKit.Core/Models/Patch/MemberPatch.cs @@ -82,7 +82,7 @@ namespace PluralKit.Core #nullable disable - public static MemberPatch FromJSON(JObject o) + public static MemberPatch FromJSON(JObject o, APIVersion v = APIVersion.V1) { var patch = new MemberPatch(); @@ -108,38 +108,82 @@ namespace PluralKit.Core if (o.ContainsKey("description")) patch.Description = o.Value("description").NullIfEmpty(); if (o.ContainsKey("keep_proxy")) patch.KeepProxy = o.Value("keep_proxy"); - // legacy: used in old export files and APIv1 - if (o.ContainsKey("prefix") || o.ContainsKey("suffix") && !o.ContainsKey("proxy_tags")) - patch.ProxyTags = new[] { new ProxyTag(o.Value("prefix"), o.Value("suffix")) }; - else if (o.ContainsKey("proxy_tags")) - patch.ProxyTags = o.Value("proxy_tags") - .OfType().Select(o => new ProxyTag(o.Value("prefix"), o.Value("suffix"))) - .Where(p => p.Valid) - .ToArray(); - - if (o.ContainsKey("privacy")) //TODO: Deprecate this completely in api v2 + switch (v) { - var plevel = o.ParsePrivacy("privacy"); + case APIVersion.V1: + { + // legacy: used in old export files and APIv1 + if (o.ContainsKey("prefix") || o.ContainsKey("suffix") && !o.ContainsKey("proxy_tags")) + patch.ProxyTags = new[] { new ProxyTag(o.Value("prefix"), o.Value("suffix")) }; + else if (o.ContainsKey("proxy_tags")) + patch.ProxyTags = o.Value("proxy_tags") + .OfType().Select(o => new ProxyTag(o.Value("prefix"), o.Value("suffix"))) + .Where(p => p.Valid) + .ToArray(); - patch.Visibility = plevel; - patch.NamePrivacy = plevel; - patch.AvatarPrivacy = plevel; - patch.DescriptionPrivacy = plevel; - patch.BirthdayPrivacy = plevel; - patch.PronounPrivacy = plevel; - // member.ColorPrivacy = plevel; - patch.MetadataPrivacy = plevel; - } - else - { - if (o.ContainsKey("visibility")) patch.Visibility = o.ParsePrivacy("visibility"); - if (o.ContainsKey("name_privacy")) patch.NamePrivacy = o.ParsePrivacy("name_privacy"); - if (o.ContainsKey("description_privacy")) patch.DescriptionPrivacy = o.ParsePrivacy("description_privacy"); - if (o.ContainsKey("avatar_privacy")) patch.AvatarPrivacy = o.ParsePrivacy("avatar_privacy"); - if (o.ContainsKey("birthday_privacy")) patch.BirthdayPrivacy = o.ParsePrivacy("birthday_privacy"); - if (o.ContainsKey("pronoun_privacy")) patch.PronounPrivacy = o.ParsePrivacy("pronoun_privacy"); - // if (o.ContainsKey("color_privacy")) member.ColorPrivacy = o.ParsePrivacy("member"); - if (o.ContainsKey("metadata_privacy")) patch.MetadataPrivacy = o.ParsePrivacy("metadata_privacy"); + if (o.ContainsKey("privacy")) + { + var plevel = o.ParsePrivacy("privacy"); + + patch.Visibility = plevel; + patch.NamePrivacy = plevel; + patch.AvatarPrivacy = plevel; + patch.DescriptionPrivacy = plevel; + patch.BirthdayPrivacy = plevel; + patch.PronounPrivacy = plevel; + // member.ColorPrivacy = plevel; + patch.MetadataPrivacy = plevel; + } + else + { + if (o.ContainsKey("visibility")) patch.Visibility = o.ParsePrivacy("visibility"); + if (o.ContainsKey("name_privacy")) patch.NamePrivacy = o.ParsePrivacy("name_privacy"); + if (o.ContainsKey("description_privacy")) patch.DescriptionPrivacy = o.ParsePrivacy("description_privacy"); + if (o.ContainsKey("avatar_privacy")) patch.AvatarPrivacy = o.ParsePrivacy("avatar_privacy"); + if (o.ContainsKey("birthday_privacy")) patch.BirthdayPrivacy = o.ParsePrivacy("birthday_privacy"); + if (o.ContainsKey("pronoun_privacy")) patch.PronounPrivacy = o.ParsePrivacy("pronoun_privacy"); + // if (o.ContainsKey("color_privacy")) member.ColorPrivacy = o.ParsePrivacy("member"); + if (o.ContainsKey("metadata_privacy")) patch.MetadataPrivacy = o.ParsePrivacy("metadata_privacy"); + } + break; + } + case APIVersion.V2: + { + + if (o.ContainsKey("proxy_tags")) + patch.ProxyTags = o.Value("proxy_tags") + .OfType().Select(o => new ProxyTag(o.Value("prefix"), o.Value("suffix"))) + .Where(p => p.Valid) + .ToArray(); + + if (o.ContainsKey("privacy") && o["privacy"].Type != JTokenType.Null) + { + var privacy = o.Value("privacy"); + + if (privacy.ContainsKey("visibility")) + patch.Visibility = privacy.ParsePrivacy("visibility"); + + if (privacy.ContainsKey("name_privacy")) + patch.NamePrivacy = privacy.ParsePrivacy("name_privacy"); + + if (privacy.ContainsKey("description_privacy")) + patch.DescriptionPrivacy = privacy.ParsePrivacy("description_privacy"); + + if (privacy.ContainsKey("avatar_privacy")) + patch.AvatarPrivacy = privacy.ParsePrivacy("avatar_privacy"); + + if (privacy.ContainsKey("birthday_privacy")) + patch.BirthdayPrivacy = privacy.ParsePrivacy("birthday_privacy"); + + if (privacy.ContainsKey("pronoun_privacy")) + patch.PronounPrivacy = privacy.ParsePrivacy("pronoun_privacy"); + + if (privacy.ContainsKey("metadata_privacy")) + patch.MetadataPrivacy = privacy.ParsePrivacy("metadata_privacy"); + } + + break; + } } return patch; diff --git a/PluralKit.Core/Models/Patch/SystemPatch.cs b/PluralKit.Core/Models/Patch/SystemPatch.cs index a2ba3b3e..bff45971 100644 --- a/PluralKit.Core/Models/Patch/SystemPatch.cs +++ b/PluralKit.Core/Models/Patch/SystemPatch.cs @@ -72,7 +72,9 @@ namespace PluralKit.Core throw new ValidationError("avatar_url"); } - public static SystemPatch FromJSON(JObject o) +#nullable disable + + public static SystemPatch FromJSON(JObject o, APIVersion v = APIVersion.V1) { var patch = new SystemPatch(); if (o.ContainsKey("name")) patch.Name = o.Value("name").NullIfEmpty(); @@ -81,16 +83,44 @@ namespace PluralKit.Core if (o.ContainsKey("avatar_url")) patch.AvatarUrl = o.Value("avatar_url").NullIfEmpty(); if (o.ContainsKey("banner")) patch.BannerImage = o.Value("banner").NullIfEmpty(); if (o.ContainsKey("color")) patch.Color = o.Value("color").NullIfEmpty(); - if (o.ContainsKey("timezone")) patch.UiTz = o.Value("tz") ?? "UTC"; + if (o.ContainsKey("timezone")) patch.UiTz = o.Value("timezone") ?? "UTC"; - // legacy: APIv1 uses "tz" instead of "timezone" - // todo: remove in APIv2 - if (o.ContainsKey("tz")) patch.UiTz = o.Value("tz") ?? "UTC"; + switch (v) + { + case APIVersion.V1: + { + if (o.ContainsKey("tz")) patch.UiTz = o.Value("tz") ?? "UTC"; + + if (o.ContainsKey("description_privacy")) patch.DescriptionPrivacy = o.ParsePrivacy("description_privacy"); + if (o.ContainsKey("member_list_privacy")) patch.MemberListPrivacy = o.ParsePrivacy("member_list_privacy"); + if (o.ContainsKey("front_privacy")) patch.FrontPrivacy = o.ParsePrivacy("front_privacy"); + if (o.ContainsKey("front_history_privacy")) patch.FrontHistoryPrivacy = o.ParsePrivacy("front_history_privacy"); + + break; + } + case APIVersion.V2: + { + if (o.ContainsKey("privacy") && o["privacy"].Type != JTokenType.Null) + { + var privacy = o.Value("privacy"); + + if (privacy.ContainsKey("description_privacy")) + patch.DescriptionPrivacy = privacy.ParsePrivacy("description_privacy"); + + if (privacy.ContainsKey("member_list_privacy")) + patch.DescriptionPrivacy = privacy.ParsePrivacy("member_list_privacy"); + + if (privacy.ContainsKey("front_privacy")) + patch.DescriptionPrivacy = privacy.ParsePrivacy("front_privacy"); + + if (privacy.ContainsKey("front_history_privacy")) + patch.DescriptionPrivacy = privacy.ParsePrivacy("front_history_privacy"); + } + + break; + } + } - if (o.ContainsKey("description_privacy")) patch.DescriptionPrivacy = o.ParsePrivacy("description_privacy"); - if (o.ContainsKey("member_list_privacy")) patch.MemberListPrivacy = o.ParsePrivacy("member_list_privacy"); - if (o.ContainsKey("front_privacy")) patch.FrontPrivacy = o.ParsePrivacy("front_privacy"); - if (o.ContainsKey("front_history_privacy")) patch.FrontHistoryPrivacy = o.ParsePrivacy("front_history_privacy"); return patch; } }