diff --git a/Pleroma/Models/Notification.cs b/Pleroma/Models/Notification.cs
new file mode 100644
index 0000000..4f6b795
--- /dev/null
+++ b/Pleroma/Models/Notification.cs
@@ -0,0 +1,212 @@
+namespace Uwaa.Pleroma;
+
+public class Notification : ASObject
+{
+ ///
+ /// The account that performed the action that generated the notification.
+ ///
+ [JsonPropertyName("account")]
+ public Account Account { get; set; } = null!;
+
+ [JsonPropertyName("created_at")]
+ public DateTime CreatedAt { get; set; }
+
+ ///
+ /// Group key shared by similar notifications
+ ///
+ [JsonPropertyName("group_key")]
+ public string GroupKey { get; set; } = null!;
+
+ ///
+ /// Status that was the object of the notification, e.g. in mentions, reblogs, favourites, or polls.
+ ///
+ [JsonPropertyName("status")]
+ public Status? Status { get; set; }
+
+ ///
+ /// The type of event that resulted in the notification.
+ ///
+ [JsonPropertyName("type")]
+ public NotificationType Type { get; set; }
+
+ [JsonConstructor()]
+ internal Notification()
+ {
+ }
+}
+
+[JsonConverter(typeof(NotificationIDConverter))]
+public readonly struct NotificationID(string id) : IEquatable
+{
+ public static implicit operator NotificationID(string id) => new NotificationID(id);
+
+ public static implicit operator NotificationID(Notification notification) => new NotificationID(notification.ID);
+
+ public readonly string ID = id;
+
+ public override string ToString() => ID;
+
+
+ public static bool operator ==(NotificationID left, NotificationID right) => left.Equals(right);
+
+ public static bool operator !=(NotificationID left, NotificationID right) => !(left == right);
+
+ public override bool Equals(object? obj) => (obj is NotificationID id && Equals(id)) || (obj is Notification notification && Equals(notification));
+
+ public bool Equals(NotificationID other) => ID == other.ID;
+
+ public override int GetHashCode() => ID.GetHashCode();
+}
+
+class NotificationIDConverter : JsonConverter
+{
+ public override bool CanConvert(Type type) => type.IsAssignableTo(typeof(NotificationID));
+
+ public override NotificationID Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ return new NotificationID(reader.GetString() ?? throw new NullReferenceException("Expected a string, got null"));
+ }
+
+ public override void Write(Utf8JsonWriter writer, NotificationID value, JsonSerializerOptions options)
+ {
+ writer.WriteStringValue(value.ID);
+ }
+}
+
+[JsonConverter(typeof(NotificationTypeConverter))]
+public enum NotificationType
+{
+ Unknown,
+
+ ///
+ /// Someone followed you
+ ///
+ Follow,
+
+ ///
+ /// Someone mentioned you in their status
+ ///
+ Mention,
+
+ ///
+ /// Someone boosted one of your statuses
+ ///
+ Reblog,
+
+ ///
+ /// Someone favourited one of your statuses
+ ///
+ Favourite,
+
+ ///
+ /// A poll you have voted in or created has ended
+ ///
+ Poll,
+
+ ///
+ /// Someone moved their account
+ ///
+ Move,
+
+ ///
+ /// Someone reacted with emoji to your status
+ ///
+ EmojiReaction,
+
+ ///
+ /// Someone mentioned you in a chat message
+ ///
+ ChatMention,
+
+ ///
+ /// Someone was reported
+ ///
+ Report,
+
+ ///
+ /// Someone you are subscribed to created a status
+ ///
+ Status,
+
+ ///
+ /// A status you boosted has been edited
+ ///
+ Update,
+
+ ///
+ /// Someone signed up (optionally sent to admins)
+ ///
+ Admin_SignUp,
+
+ ///
+ /// A new report has been filed
+ ///
+ Admin_Report,
+}
+
+///
+/// Helper functions for .
+///
+public static class NotificationTypes
+{
+ public static NotificationType Parse(string typeStr)
+ {
+ return typeStr switch
+ {
+ "follow" => NotificationType.Follow,
+ "mention" => NotificationType.Mention,
+ "reblog" => NotificationType.Reblog,
+ "favourite" => NotificationType.Favourite,
+ "poll" => NotificationType.Poll,
+ "move" => NotificationType.Move,
+ "pleroma:emoji_reaction" => NotificationType.EmojiReaction,
+ "pleroma:chat_mention" => NotificationType.ChatMention,
+ "pleroma:report" => NotificationType.Report,
+ "status" => NotificationType.Status,
+ "update" => NotificationType.Update,
+ "admin.sign_up" => NotificationType.Admin_SignUp,
+ "admin.report" => NotificationType.Admin_Report,
+ _ => NotificationType.Unknown,
+ };
+ }
+
+ public static string ToString(this NotificationType type)
+ {
+ return type switch
+ {
+ NotificationType.Unknown => throw new NotImplementedException("Invalid notification type"),
+ NotificationType.Follow => "follow",
+ NotificationType.Mention => "mention",
+ NotificationType.Reblog => "reblog",
+ NotificationType.Favourite => "favourite",
+ NotificationType.Poll => "poll",
+ NotificationType.Move => "move",
+ NotificationType.EmojiReaction => "pleroma:emoji_reaction",
+ NotificationType.ChatMention => "pleroma:chat_mention",
+ NotificationType.Report => "pleroma:report",
+ NotificationType.Status => "status",
+ NotificationType.Update => "update",
+ NotificationType.Admin_SignUp => "admin.sign_up",
+ NotificationType.Admin_Report => "admin.report",
+ _ => throw new NotImplementedException("Unknown notification type"),
+ };
+ }
+}
+
+///
+/// Converts to and from enum values in lowercase.
+///
+class NotificationTypeConverter : JsonConverter
+{
+ public override bool CanConvert(Type typeToConvert) => typeToConvert.IsEnum;
+
+ public override NotificationType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ return NotificationTypes.Parse(reader.GetString() ?? throw new NullReferenceException("Expected a string, got null"));
+ }
+
+ public override void Write(Utf8JsonWriter writer, NotificationType value, JsonSerializerOptions options)
+ {
+ writer.WriteStringValue(NotificationTypes.ToString(value));
+ }
+}
\ No newline at end of file
diff --git a/Pleroma/Pleroma.cs b/Pleroma/Pleroma.cs
index 4bdb275..5284846 100644
--- a/Pleroma/Pleroma.cs
+++ b/Pleroma/Pleroma.cs
@@ -51,6 +51,11 @@ public class Pleroma
HttpClient.DefaultRequestHeaders.Authorization = AuthenticationHeaderValue.Parse(authorization);
}
+ Task Retry(Func reqFactory)
+ {
+ return Retry