diff --git a/Pleroma/ASObject.cs b/Pleroma/ASObject.cs
index a10e33a..af8140d 100644
--- a/Pleroma/ASObject.cs
+++ b/Pleroma/ASObject.cs
@@ -1,10 +1,36 @@
-namespace Uwaa.Pleroma;
+using System.Buffers.Binary;
+using System.Collections;
+
+namespace Uwaa.Pleroma;
///
/// Base class for ActivityStreams objects.
///
public class ASObject
{
+ public static UInt128 FromBase62(string id)
+ {
+ //TODO: Optimize
+ const string table = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+
+ BitArray bits = new BitArray(id.Length * 6);
+ for (int i = 0; i < id.Length; i++)
+ {
+ int index = table.IndexOf(id[^(i + 1)]);
+ bits[i * 6] = (index & 1) != 0;
+ bits[i * 6 + 1] = (index & 2) != 0;
+ bits[i * 6 + 2] = (index & 4) != 0;
+ bits[i * 6 + 3] = (index & 8) != 0;
+ bits[i * 6 + 4] = (index & 16) != 0;
+ bits[i * 6 + 5] = (index & 32) != 0;
+ }
+
+ byte[] bytes = new byte[16];
+ bits.CopyTo(bytes, 0);
+
+ return BinaryPrimitives.ReadUInt128LittleEndian(bytes);
+ }
+
///
/// Pleroma uses 128-bit ids as opposed to Mastodon's 64 bits. However just like Mastodon's ids they are lexically sortable strings
///
diff --git a/Pleroma/Models/Account.cs b/Pleroma/Models/Account.cs
index 33d9a32..dd52bf0 100644
--- a/Pleroma/Models/Account.cs
+++ b/Pleroma/Models/Account.cs
@@ -17,9 +17,6 @@ public class Account : ASObject
[JsonPropertyName("following_count")]
public uint Following { get; set; }
- [JsonPropertyName("local")]
- public bool Local { get; set; }
-
[JsonPropertyName("locked")]
public bool Locked { get; set; }
diff --git a/Pleroma/Pleroma.cs b/Pleroma/Pleroma.cs
index e77736a..8ee56c4 100644
--- a/Pleroma/Pleroma.cs
+++ b/Pleroma/Pleroma.cs
@@ -171,28 +171,28 @@ public class Pleroma
int offset = 0,
int limit = 20)
{
- return Retry(() => new HttpRequestMessage(HttpMethod.Get, $"/api/v1/accounts/{account.ID}/following" + CreateQuery(addPair =>
+ string path = $"/api/v1/accounts/{account.ID}/following" + CreateQuery(addPair =>
{
if (max_id != null) addPair("max_id", max_id);
if (min_id != null) addPair("min_id", min_id);
if (since_id != null) addPair("since_id", since_id);
if (offset > 0) addPair("offset", offset.ToString());
if (limit != 20) addPair("limit", limit.ToString());
- })))!;
+ });
+ return Retry(() => new HttpRequestMessage(HttpMethod.Get, path))!;
}
///
/// Sets a private note for the given account.
///
- /// Account ID
+ /// Account ID
/// Account note body
- ///
+ /// The new relationship with the given account.
public Task SetUserNote(AccountID account, string comment)
{
MemoryStream mem = new MemoryStream();
-
+ using (Utf8JsonWriter writer = new Utf8JsonWriter(mem, new JsonWriterOptions() { SkipValidation = true }))
{
- Utf8JsonWriter writer = new Utf8JsonWriter(mem, new JsonWriterOptions() { SkipValidation = true });
writer.WriteStartObject();
writer.WritePropertyName("comment");
@@ -215,7 +215,7 @@ public class Pleroma
///
/// Gets the account's relationship with the given account.
///
- /// Account ID
+ /// Account ID
/// A relationship object for the requested account, if the account exists.
public async Task GetRelationship(AccountID account)
{
@@ -246,7 +246,7 @@ public class Pleroma
bool sensitive = false,
int? expiresIn = null,
StatusID? replyTo = null,
- string? quoting = null,
+ StatusID? quoting = null,
string? language = null,
MediaID[]? attachments = null,
string[]? to = null,
@@ -256,9 +256,8 @@ public class Pleroma
throw new ArgumentException("Cannot post nothing. Content and/or attachments must be provided.");
MemoryStream mem = new MemoryStream();
-
+ using (Utf8JsonWriter writer = new Utf8JsonWriter(mem, new JsonWriterOptions() { SkipValidation = true }))
{
- Utf8JsonWriter writer = new Utf8JsonWriter(mem, new JsonWriterOptions() { SkipValidation = true });
writer.WriteStartObject();
if (content != null)
@@ -288,10 +287,10 @@ public class Pleroma
writer.WriteStringValue(replyTo.Value.ID);
}
- if (quoting != null)
+ if (quoting.HasValue)
{
writer.WritePropertyName("quote_id");
- writer.WriteStringValue(quoting);
+ writer.WriteStringValue(quoting.Value.ID);
}
if (language != null)
@@ -434,8 +433,8 @@ public class Pleroma
{
MemoryStream mem = new MemoryStream();
+ using (Utf8JsonWriter writer = new Utf8JsonWriter(mem, new JsonWriterOptions() { SkipValidation = true }))
{
- Utf8JsonWriter writer = new Utf8JsonWriter(mem, new JsonWriterOptions() { SkipValidation = true });
writer.WriteStartObject();
if (visibility.HasValue)
@@ -532,7 +531,7 @@ public class Pleroma
throw new ArgumentException("Invalid timeline", nameof(timeline));
}
- return Retry(() => new HttpRequestMessage(HttpMethod.Get, $"/api/v1/timelines/{timelineName}" + CreateQuery(addPair =>
+ string path = $"/api/v1/timelines/{timelineName}" + CreateQuery(addPair =>
{
if (local) addPair("local", "true");
if (instance != null) addPair("instance", instance);
@@ -545,7 +544,8 @@ public class Pleroma
if (since_id != null) addPair("since_id", since_id);
if (offset > 0) addPair("offset", offset.ToString());
if (limit != 20) addPair("limit", limit.ToString());
- })))!;
+ });
+ return Retry(() => new HttpRequestMessage(HttpMethod.Get, path))!;
}
@@ -579,8 +579,7 @@ public class Pleroma
int offset = 0,
int limit = 20)
{
-
- return Retry(() => new HttpRequestMessage(HttpMethod.Get, $"/api/v1/accounts/{WebUtility.UrlEncode(account.ID)}/statuses" + CreateQuery(addPair =>
+ string path = $"/api/v1/accounts/{WebUtility.UrlEncode(account.ID)}/statuses" + CreateQuery(addPair =>
{
if (pinned) addPair("pinned", "true");
if (tagged != null) addPair("tagged", tagged);
@@ -596,7 +595,8 @@ public class Pleroma
if (since_id != null) addPair("since_id", since_id);
if (offset > 0) addPair("offset", offset.ToString());
if (limit != 20) addPair("limit", limit.ToString());
- })));
+ });
+ return Retry(() => new HttpRequestMessage(HttpMethod.Get, path));
}
///
@@ -638,7 +638,7 @@ public class Pleroma
int offset = 0,
int limit = 20)
{
- return Retry(() => new HttpRequestMessage(HttpMethod.Get, "/api/v2/search" + CreateQuery(addPair =>
+ string path = "/api/v2/search" + CreateQuery(addPair =>
{
addPair("q", query);
if (account_id != null) addPair("account_id", account_id);
@@ -650,7 +650,8 @@ public class Pleroma
if (since_id != null) addPair("since_id", since_id);
if (offset > 0) addPair("offset", offset.ToString());
if (limit != 20) addPair("limit", limit.ToString());
- })))!;
+ });
+ return Retry(() => new HttpRequestMessage(HttpMethod.Get, path))!;
}
///
@@ -692,7 +693,7 @@ public class Pleroma
int offset = 0,
int limit = 20)
{
- return Retry(() => new HttpRequestMessage(HttpMethod.Get, "/api/v1/notifications" + CreateQuery(addPair =>
+ string path = "/api/v1/notifications" + CreateQuery(addPair =>
{
if (exclude_types != null)
foreach (NotificationType type in exclude_types)
@@ -714,7 +715,8 @@ public class Pleroma
if (since_id != null) addPair("since_id", since_id);
if (offset > 0) addPair("offset", offset.ToString());
if (limit != 20) addPair("limit", limit.ToString());
- })))!;
+ });
+ return Retry(() => new HttpRequestMessage(HttpMethod.Get, path))!;
}
///
@@ -723,7 +725,7 @@ public class Pleroma
/// Notification ID
public Task GetNotification(NotificationID id)
{
- return Retry(() => new HttpRequestMessage(HttpMethod.Get, $"/api/v1/notifications/{id}"));
+ return Retry(() => new HttpRequestMessage(HttpMethod.Get, $"/api/v1/notifications/{WebUtility.UrlEncode(id.ID)}"));
}
///
@@ -732,7 +734,7 @@ public class Pleroma
/// Notification ID
public Task Dismiss(NotificationID notification)
{
- return Retry(() => new HttpRequestMessage(HttpMethod.Post, $"/api/v1/notifications/{notification}/dismiss"));
+ return Retry(() => new HttpRequestMessage(HttpMethod.Post, $"/api/v1/notifications/{WebUtility.UrlEncode(notification.ID)}/dismiss"));
}
///
@@ -744,11 +746,12 @@ public class Pleroma
if (notifications.Length == 0)
return Task.CompletedTask;
- return Retry(() => new HttpRequestMessage(HttpMethod.Delete, $"/api/v1/notifications/destroy_multiple" + CreateQuery(addPair =>
+ string path = $"/api/v1/notifications/destroy_multiple" + CreateQuery(addPair =>
{
foreach (NotificationID id in notifications)
addPair("ids[]", id.ID);
- })));
+ });
+ return Retry(() => new HttpRequestMessage(HttpMethod.Delete, path));
}
///
@@ -760,11 +763,33 @@ public class Pleroma
if (notifications.Length == 0)
return Task.CompletedTask;
- return Retry(() => new HttpRequestMessage(HttpMethod.Delete, $"/api/v1/notifications/destroy_multiple" + CreateQuery(addPair =>
+ string path = $"/api/v1/notifications/destroy_multiple" + CreateQuery(addPair =>
{
foreach (NotificationID id in notifications)
addPair("ids[]", id.ID);
- })));
+ });
+ return Retry(() => new HttpRequestMessage(HttpMethod.Delete, path));
+ }
+
+ ///
+ /// Adds a unicode reaction to a status.
+ ///
+ /// The status to react to.
+ /// The unicode emoji.
+ /// The new state of the status, if it exists.
+ public Task React(StatusID status, char emoji) => ReactInline(status, emoji.ToString());
+
+ ///
+ /// Adds a custom reaction to a status.
+ ///
+ /// The status to react to.
+ /// The custom emoji's name.
+ /// The new state of the status, if it exists.
+ public Task React(StatusID status, string emojiName) => ReactInline(status, $":{emojiName}:");
+
+ Task ReactInline(StatusID status, string emoji)
+ {
+ return Retry(() => new HttpRequestMessage(HttpMethod.Put, $"/api/v1/pleroma/statuses/{WebUtility.UrlEncode(status.ID)}/reactions/{WebUtility.UrlEncode(emoji)}"));
}
}
@@ -799,7 +824,7 @@ public class PleromaAdmin : Pleroma
ActorType[]? actor_types = null,
string[]? tags = null)
{
- return Retry(() => new HttpRequestMessage(HttpMethod.Get, $"/api/v1/pleroma/admin/users" + CreateQuery(addPair =>
+ string path = "/api/v1/pleroma/admin/users" + CreateQuery(addPair =>
{
string? typeStr = type switch
{
@@ -835,15 +860,22 @@ public class PleromaAdmin : Pleroma
if (tags != null)
foreach (string tag in tags)
addPair("tags[]", tag);
- })))!;
+ });
+ return Retry(() => new HttpRequestMessage(HttpMethod.Get, path))!;
}
- public Task ChangeScope(StatusID status, bool? sensitive = null, StatusVisibility? visibility = null)
+ ///
+ /// Modifies the sensitive and/or visibility of a status.
+ ///
+ /// The status ID to modify.
+ /// If non-null, the status will be made sensitive (if true) or not sensitive (if false).
+ /// If non-null, the status's visibility will be set to this.
+ /// Returns the new state of the status. Returns null if the status doesn't exist.
+ public Task ChangeScope(StatusID status, bool? sensitive = null, StatusVisibility? visibility = null)
{
MemoryStream mem = new MemoryStream();
-
+ using (Utf8JsonWriter writer = new Utf8JsonWriter(mem, new JsonWriterOptions() { SkipValidation = true }))
{
- Utf8JsonWriter writer = new Utf8JsonWriter(mem, new JsonWriterOptions() { SkipValidation = true });
writer.WriteStartObject();
if (sensitive.HasValue)
@@ -865,19 +897,50 @@ public class PleromaAdmin : Pleroma
return Retry(() =>
{
mem.Position = 0;
- HttpRequestMessage req = new HttpRequestMessage(HttpMethod.Put, $"/api/v1/pleroma/admin/statuses{WebUtility.UrlEncode(status.ID)}");
+ HttpRequestMessage req = new HttpRequestMessage(HttpMethod.Put, $"/api/v1/pleroma/admin/statuses/{WebUtility.UrlEncode(status.ID)}");
req.Content = new StreamContent(mem);
req.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
return req;
- })!;
+ });
}
public Task GetModerationLog(int page = 1)
{
- return Retry(() => new HttpRequestMessage(HttpMethod.Get, $"/api/v1/pleroma/admin/moderation_log" + CreateQuery(addPair =>
+ string path = "/api/v1/pleroma/admin/moderation_log" + CreateQuery(addPair =>
{
addPair("page", page.ToString());
- })))!;
+ });
+ return Retry(() => new HttpRequestMessage(HttpMethod.Get, path))!;
+ }
+
+ ///
+ /// Deactivates or deletes one or more users by nickname.
+ ///
+ /// If the user is pending approval, this will delete the user entirely. If the user is active, the user will be deactivated. Does nothing if the user is already deactivated.
+ /// The nicknames of the users to deactivate or delete.
+ /// An array of nicknames which were successfully deactivated or deleted.
+ public Task DeleteUsers(params string[] nicknames)
+ {
+ MemoryStream mem = new MemoryStream();
+ using (Utf8JsonWriter writer = new Utf8JsonWriter(mem, new JsonWriterOptions() { SkipValidation = true }))
+ {
+ writer.WriteStartObject();
+ writer.WriteStartArray("nicknames");
+ foreach (string nickname in nicknames)
+ writer.WriteStringValue(nickname);
+ writer.WriteEndArray();
+ writer.WriteEndObject();
+ writer.Flush();
+ }
+
+ return Retry(() =>
+ {
+ mem.Position = 0;
+ HttpRequestMessage req = new HttpRequestMessage(HttpMethod.Delete, "/api/v1/pleroma/admin/users");
+ req.Content = new StreamContent(mem);
+ req.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
+ return req;
+ })!;
}
}