diff --git a/Pleroma/Models/Account.cs b/Pleroma/Models/Account.cs index 8ce75d4..33d9a32 100644 --- a/Pleroma/Models/Account.cs +++ b/Pleroma/Models/Account.cs @@ -79,6 +79,8 @@ public readonly struct AccountID(string id) public static implicit operator AccountID(Relationship relationship) => new AccountID(relationship.ID); + public static implicit operator AccountID(AccountInfo userInfo) => new AccountID(userInfo.ID); + public readonly string ID = id; public override string ToString() => ID; diff --git a/Pleroma/Models/AccountInfo.cs b/Pleroma/Models/AccountInfo.cs new file mode 100644 index 0000000..cd74ede --- /dev/null +++ b/Pleroma/Models/AccountInfo.cs @@ -0,0 +1,63 @@ +namespace Uwaa.Pleroma; + +/// +/// Admin information about an account. +/// +public class AccountInfo : ASObject +{ + [JsonPropertyName("actor_type")] + [JsonConverter(typeof(EnumLowerCaseConverter))] + public ActorType Type { get; set; } + + [JsonPropertyName("avatar")] + public string Avatar { get; set; } = null!; + + [JsonPropertyName("display_name")] + public string DisplayName { get; set; } = null!; + + [JsonPropertyName("email")] + public string? Email { get; set; } + + [JsonPropertyName("is_active")] + public bool IsActive { get; set; } + + [JsonPropertyName("is_approved")] + public bool IsApproved { get; set; } + + [JsonPropertyName("is_confirmed")] + public bool IsConfirmed { get; set; } + + [JsonPropertyName("local")] + public bool Local { get; set; } + + [JsonPropertyName("nickname")] + public string Nickname { get; set; } = null!; + + [JsonPropertyName("registration_reason")] + public string? RegistrationReason { get; set; } + + [JsonPropertyName("roles")] + public AccountRoles Roles { get; set; } = null!; + + [JsonPropertyName("url")] + public string URL { get; set; } = null!; + + [JsonPropertyName("tags")] + public string[] Tags { get; set; } = null!; + + [JsonConstructor()] + internal AccountInfo() + { + } + + public override string ToString() => $"@{Nickname}"; +} + +public class AccountRoles +{ + [JsonPropertyName("admin")] + public bool Admin { get; set; } + + [JsonPropertyName("moderator")] + public bool Moderator { get; set; } +} \ No newline at end of file diff --git a/Pleroma/Models/Page.cs b/Pleroma/Models/Page.cs new file mode 100644 index 0000000..4d563e7 --- /dev/null +++ b/Pleroma/Models/Page.cs @@ -0,0 +1,16 @@ +namespace Uwaa.Pleroma; + +public class Page +{ + [JsonPropertyName("count")] + public int Count { get; set; } + + [JsonPropertyName("page_size")] + public int PageSize { get; set; } +} + +public class UsersPage +{ + [JsonPropertyName("users")] + public AccountInfo[] Users { get; set; } = null!; +} \ No newline at end of file diff --git a/Pleroma/Pleroma.cs b/Pleroma/Pleroma.cs index 2a0cc0f..d9e8745 100644 --- a/Pleroma/Pleroma.cs +++ b/Pleroma/Pleroma.cs @@ -17,7 +17,7 @@ public class Pleroma NumberHandling = JsonNumberHandling.AllowReadingFromString, }; - static string CreateQuery(Action generator) + internal static string CreateQuery(Action generator) { StringBuilder sb = new StringBuilder(); void addPair(string key, string value) @@ -51,12 +51,17 @@ public class Pleroma HttpClient.DefaultRequestHeaders.Authorization = AuthenticationHeaderValue.Parse(authorization); } - Task Retry(Func reqFactory) + public Pleroma(HttpClient client) + { + HttpClient = client; + } + + internal Task Retry(Func reqFactory) { return Retry(reqFactory); } - async Task Retry(Func reqFactory) + internal async Task Retry(Func reqFactory) { while (true) { @@ -729,3 +734,134 @@ public class Pleroma }))); } } + +/// +/// A pleroma client permitted to use the admin scope. +/// +public class PleromaAdmin : Pleroma +{ + public PleromaAdmin(string host, string? authorization, string? userAgent = "Uwaa.Pleroma/0.0") : base(host, authorization, userAgent) + { + } + + /// + /// Gets users known to the instance. + /// + /// Filter by local/external type + /// Filter by account status + /// Search users query + /// Search by display name + /// Search by email + /// Page Number + /// Number of users to return per page + /// Filter by actor type + /// Filter by tags + public Task GetUsers(AccountType? type = null, + AccountStatus? status = null, + string? query = null, + string? name = null, + string? email = null, + int? page = null, + int? page_size = null, + ActorType[]? actor_types = null, + string[]? tags = null) + { + return Retry(() => new HttpRequestMessage(HttpMethod.Get, $"/api/v1/pleroma/admin/users" + CreateQuery(addPair => + { + string? typeStr = type switch + { + AccountType.Local => "local", + AccountType.External => "external", + null => null, + _ => throw new ArgumentException("Invalid account type", nameof(type)), + }; + string? statusStr = status switch + { + AccountStatus.Active => "active", + AccountStatus.Deactivated => "deactivated", + AccountStatus.PendingApproval => "need_approval", + AccountStatus.Unconfirmed => "unconfirmed", + null => null, + _ => throw new ArgumentException("Invalid account status", nameof(type)), + }; + if (type != null && status != null) + addPair("filters", typeStr + "," + statusStr); + else if (typeStr != null) + addPair("filters", typeStr); + else if (statusStr != null) + addPair("filters", statusStr); + + if (query != null) addPair("query", query); + if (name != null) addPair("name", name); + if (email != null) addPair("email", email); + if (page != null) addPair("page", page.Value.ToString()); + if (page_size != null) addPair("page_size", page_size.Value.ToString()); + if (actor_types != null) + foreach (ActorType type in actor_types) + addPair("actor_types[]", type.ToString()); + if (tags != null) + foreach (string tag in tags) + addPair("tags[]", tag); + })))!; + } + + public Task ChangeScope(StatusID id, bool? sensitive = null, StatusVisibility? visibility = null) + { + MemoryStream mem = new MemoryStream(); + + { + Utf8JsonWriter writer = new Utf8JsonWriter(mem, new JsonWriterOptions() { SkipValidation = true }); + writer.WriteStartObject(); + + if (sensitive.HasValue) + { + writer.WritePropertyName("sensitive"); + writer.WriteBooleanValue(sensitive.Value); + } + + if (visibility.HasValue) + { + writer.WritePropertyName("visibility"); + writer.WriteStringValue(visibility.Value.ToString().ToLowerInvariant()); + } + + writer.WriteEndObject(); + writer.Flush(); + } + + return Retry(() => + { + mem.Position = 0; + HttpRequestMessage req = new HttpRequestMessage(HttpMethod.Put, $"/api/v1/pleroma/admin/statuses/{id}"); + req.Content = new StreamContent(mem); + req.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json"); + return req; + })!; + } +} + +[JsonConverter(typeof(EnumLowerCaseConverter))] +public enum ActorType +{ + Application, + Group, + Organization, + Person, + Service +} + +[JsonConverter(typeof(EnumLowerCaseConverter))] +public enum AccountType +{ + Local, + External, +} + +[JsonConverter(typeof(EnumLowerCaseConverter))] +public enum AccountStatus +{ + Active, + Deactivated, + PendingApproval, + Unconfirmed, +} \ No newline at end of file