251 lines
7.5 KiB
C#
251 lines
7.5 KiB
C#
using System.Text.Json;
|
|
using System.Threading.Tasks;
|
|
using Uwaa.HTTP;
|
|
using Uwaa.Pleroma.API;
|
|
|
|
namespace Uwaa.Pleroma;
|
|
|
|
/// <summary>
|
|
/// A pleroma client.
|
|
/// </summary>
|
|
public class Pleroma
|
|
{
|
|
static readonly JsonSerializerOptions SerializerOptions = new()
|
|
{
|
|
PropertyNameCaseInsensitive = true,
|
|
NumberHandling = System.Text.Json.Serialization.JsonNumberHandling.AllowReadingFromString,
|
|
};
|
|
|
|
static readonly MIMEType JsonMIMEType = new("application", "json");
|
|
|
|
|
|
/// <summary>
|
|
/// The hostname of the pleroma instance.
|
|
/// </summary>
|
|
public string Host;
|
|
|
|
/// <summary>
|
|
/// The full token, including the "Bearer" string.
|
|
/// </summary>
|
|
public string Authorization;
|
|
|
|
/// <summary>
|
|
/// The user agent string.
|
|
/// </summary>
|
|
public string UserAgent = "Uwaa.Pleroma/0.0";
|
|
|
|
public Pleroma(string host, string authorization)
|
|
{
|
|
Host = host;
|
|
Authorization = authorization;
|
|
}
|
|
|
|
async Task RequestJSONRetry(HttpRequest req)
|
|
{
|
|
while (true)
|
|
{
|
|
try
|
|
{
|
|
await RequestJSON(req);
|
|
return;
|
|
}
|
|
catch (PleromaException e)
|
|
{
|
|
if (e.Text == "Throttled")
|
|
{
|
|
await Task.Delay(5000);
|
|
continue;
|
|
}
|
|
else
|
|
throw;
|
|
}
|
|
}
|
|
}
|
|
|
|
async Task<T?> RequestJSONRetry<T>(HttpRequest req) where T : class
|
|
{
|
|
while (true)
|
|
{
|
|
try
|
|
{
|
|
return await RequestJSON<T>(req);
|
|
}
|
|
catch (PleromaException e)
|
|
{
|
|
if (e.Text == "Throttled")
|
|
{
|
|
await Task.Delay(5000);
|
|
continue;
|
|
}
|
|
else
|
|
throw;
|
|
}
|
|
}
|
|
}
|
|
|
|
async Task RequestJSON(HttpRequest req)
|
|
{
|
|
req.Fields.UserAgent = UserAgent;
|
|
req.Fields.Authorization = Authorization;
|
|
|
|
HttpResponse res = await HttpClient.Request(Host, true, req);
|
|
|
|
if (res.StatusCode == 404)
|
|
return;
|
|
|
|
if (!res.Fields.ContentType.HasValue || !res.Fields.ContentType.Value.Match(JsonMIMEType))
|
|
throw new HttpException("Server did not respond with JSON" + (res.Content.HasValue ? ", got: " + res.Content.Value.AsText : null));
|
|
|
|
if (!res.Content.HasValue)
|
|
throw new HttpException("Server responded with no content");
|
|
|
|
string text = res.Content.Value.AsText;
|
|
|
|
if (res.StatusCode is >= 400 and < 600)
|
|
{
|
|
try
|
|
{
|
|
PleromaException? err = JsonSerializer.Deserialize<PleromaException>(text, SerializerOptions);
|
|
if (err != null && err.Text != null)
|
|
throw err;
|
|
}
|
|
catch (JsonException)
|
|
{
|
|
//Not an error
|
|
}
|
|
}
|
|
|
|
if (res.StatusCode is not >= 200 or not < 300)
|
|
throw new HttpException("Unknown error occurred");
|
|
}
|
|
|
|
async Task<T?> RequestJSON<T>(HttpRequest req) where T : class
|
|
{
|
|
req.Fields.UserAgent = UserAgent;
|
|
req.Fields.Authorization = Authorization;
|
|
|
|
HttpResponse res = await HttpClient.Request(Host, true, req);
|
|
|
|
if (res.StatusCode == 404)
|
|
return null;
|
|
|
|
if (!res.Fields.ContentType.HasValue || !res.Fields.ContentType.Value.Match(JsonMIMEType))
|
|
throw new HttpException("Server did not respond with JSON" + (res.Content.HasValue ? ", got: " + res.Content.Value.AsText : null));
|
|
|
|
if (!res.Content.HasValue)
|
|
throw new HttpException("Server responded with no content");
|
|
|
|
string text = res.Content.Value.AsText;
|
|
|
|
if (res.StatusCode is >= 400 and < 600)
|
|
{
|
|
try
|
|
{
|
|
PleromaException? err = JsonSerializer.Deserialize<PleromaException>(text, SerializerOptions);
|
|
if (err != null && err.Text != null)
|
|
throw err;
|
|
}
|
|
catch (JsonException)
|
|
{
|
|
//Not an error
|
|
}
|
|
}
|
|
|
|
if (res.StatusCode is >= 200 and < 300)
|
|
return JsonSerializer.Deserialize<T>(text, SerializerOptions) ?? throw new HttpException("Couldn't deserialize response");
|
|
else
|
|
throw new HttpException("Unknown error occurred");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Posts a status.
|
|
/// </summary>
|
|
/// <param name="status">The status data to send, including the content, visibility, etc.</param>
|
|
/// <returns>The status, if posting was successful.</returns>
|
|
public Task<Status> PostStatus(PublishStatus status)
|
|
{
|
|
HttpRequest req = new HttpRequest(HttpMethod.POST, "/api/v1/statuses");
|
|
req.Content = new HttpContent(JsonMIMEType, JsonSerializer.SerializeToUtf8Bytes(status, SerializerOptions));
|
|
req.Fields.Accept = [JsonMIMEType];
|
|
return RequestJSONRetry<Status>(req)!;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Fetches the latest statuses from the public timeline.
|
|
/// </summary>
|
|
public Task<Status[]> GetTimeline()
|
|
{
|
|
//TODO: Parameters and selecting different timelines (home, public, bubble)
|
|
HttpRequest req = new HttpRequest(HttpMethod.GET, "/api/v1/timelines/public");
|
|
req.Fields.Accept = [ JsonMIMEType ];
|
|
return RequestJSONRetry<Status[]>(req)!;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Fetches the latest statuses from a user's timeline.
|
|
/// </summary>
|
|
public Task<Status[]> GetTimeline(Account account)
|
|
=> GetTimeline(account.ID);
|
|
|
|
/// <summary>
|
|
/// Fetches the latest statuses from a user's timeline.
|
|
/// </summary>
|
|
public Task<Status[]> GetTimeline(string account_id)
|
|
{
|
|
HttpRequest req = new HttpRequest(HttpMethod.GET, $"/api/v1/accounts/{account_id}/statuses");
|
|
req.Fields.Accept = [JsonMIMEType];
|
|
return RequestJSONRetry<Status[]>(req)!;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Fetches the account of the pleroma client.
|
|
/// </summary>
|
|
public Task<Account> GetAccount()
|
|
{
|
|
HttpRequest req = new HttpRequest(HttpMethod.GET, "/api/v1/accounts/verify_credentials");
|
|
req.Fields.Accept = [JsonMIMEType];
|
|
return RequestJSONRetry<Account>(req)!;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Fetches an account.
|
|
/// </summary>
|
|
public Task<Account?> GetAccount(string id)
|
|
{
|
|
HttpRequest req = new HttpRequest(HttpMethod.GET, $"/api/v1/accounts/{id}");
|
|
req.Fields.Accept = [JsonMIMEType];
|
|
return RequestJSONRetry<Account>(req);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Fetches the context of a status.
|
|
/// </summary>
|
|
public Task<Context?> GetContext(Status status)
|
|
=> GetContext(status.ID);
|
|
|
|
/// <summary>
|
|
/// Fetches the context of a status.
|
|
/// </summary>
|
|
public Task<Context?> GetContext(string status_id)
|
|
{
|
|
HttpRequest req = new HttpRequest(HttpMethod.GET, $"/api/v1/statuses/{status_id}/context");
|
|
req.Fields.Accept = [JsonMIMEType];
|
|
return RequestJSONRetry<Context>(req);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Deletes a status.
|
|
/// </summary>
|
|
public Task Delete(Status status)
|
|
=> Delete(status.ID);
|
|
|
|
/// <summary>
|
|
/// Deletes a status by ID.
|
|
/// </summary>
|
|
public Task Delete(string status_id)
|
|
{
|
|
HttpRequest req = new HttpRequest(HttpMethod.DELETE, $"/api/v1/statuses/{status_id}");
|
|
req.Fields.Accept = [JsonMIMEType];
|
|
return RequestJSONRetry(req);
|
|
}
|
|
}
|