using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Forms;
using Neighbourhood.omg.lol.Models;
using System;
using System.Diagnostics;
using System.Net.Http.Json;
using System.Text;
using System.Text.Json;
using System.Threading;
namespace Neighbourhood.omg.lol
{
public class ApiService {
HttpClient _client;
JsonSerializerOptions _serializerOptions;
public const string BaseUrl = "https://api.omg.lol";
public ApiService(string? token = null) {
_client = new HttpClient();
_client.BaseAddress = new Uri(BaseUrl);
_client.DefaultRequestHeaders.UserAgent.Add(new System.Net.Http.Headers.ProductInfoHeaderValue(App.Name, App.Version));
_serializerOptions = new JsonSerializerOptions {
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower,
#if DEBUG
WriteIndented = true
#else
WriteIndented = false
#endif
};
AddToken(token);
}
///
/// Deserialize json convenience function with default serializer options
///
/// The type to deserialize
/// The string to deserialize
/// The deserialized object if successful, otherwise default
public T? Deserialize(string str) {
T? responseObj = default;
try {
responseObj = JsonSerializer.Deserialize(str, _serializerOptions);
}
catch (JsonException ex) {
Debug.WriteLine(@"\tERROR {0}", ex.Message);
Debug.WriteLine(str);
}
return responseObj;
}
#region Base Requests
///
/// Decode the response from an API call
///
/// The type of response object we are trying to get
/// The raw Http Response Message
/// A cancellation token to cancel the operation
/// The decoded object if successfull, otherwise default
private async Task DecodeResponse(HttpResponseMessage response, CancellationToken cancellationToken = default)
where TResponse : IOmgLolResponseData
{
TResponse? responseData = default;
try {
string str = await response.Content.ReadAsStringAsync();
if (response.IsSuccessStatusCode) {
OmgLolResponse? responseObj = Deserialize>(str);
if (responseObj?.Request == null || (responseObj?.Request?.Success ?? false)) {
responseData = responseObj!.Response;
}
}
else {
OmgLolResponse? responseObj = Deserialize>(str);
throw responseObj == null ? new OmgLolApiException(str) : new OmgLolApiException(responseObj);
}
}
catch (Exception ex) {
Debug.WriteLine(@"\tERROR {0}", ex.Message);
}
return responseData;
}
///
/// Performs a request for the supplied uri, with the supplied Http Method,
/// with the supplied data in the body (if present)
///
/// The type of response we are expecting
/// The type of data we are sending
/// The uri to request
/// The Http Method to use for the request
/// The data to send in the body of the request
/// A FileResult for the file to send in the body of the request as binary data
/// A cancellation token
/// The returned data if successful, otherwise default
private async Task Request(string uri, HttpMethod method, TData? data = default, FileResult? file = null, CancellationToken cancellationToken = default)
where TResponse : IOmgLolResponseData
{
TResponse? responseData = default;
try {
HttpRequestMessage request = new HttpRequestMessage(method, uri);
Stream? fileStream = null;
if (file != null) {
// append "binary" query parameter (if not already present)
Uri url = new Uri(_client.BaseAddress?.AbsoluteUri + uri);
if (string.IsNullOrEmpty(url.Query)) uri += "?binary";
else if (!url.Query.Contains("binary")) uri += "&binary";
request = new HttpRequestMessage(method, uri);
fileStream = await file.OpenReadAsync();
HttpContent fileStreamContent = new StreamContent(fileStream);
fileStreamContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(file.ContentType ?? "application/octet-stream");
fileStreamContent.Headers.ContentLength = fileStream.Length;
request.Content = fileStreamContent;
}
else if (data != null) {
string json = JsonSerializer.Serialize(data, _serializerOptions);
request.Content = new StringContent(json, Encoding.UTF8, "application/json");
}
HttpResponseMessage response = await _client.SendAsync(request, cancellationToken: cancellationToken);
responseData = await DecodeResponse(response, cancellationToken);
fileStream?.Dispose();
}
catch (Exception ex) {
Debug.WriteLine(@"\tERROR {0}", ex.Message);
}
return responseData;
}
// GET request
private async Task Get(string uri, CancellationToken cancellationToken = default)
where TResponse : IOmgLolResponseData
=> await Request(uri, HttpMethod.Get, cancellationToken: cancellationToken);
// POST request
private async Task Post(string uri, TData data, CancellationToken cancellationToken = default)
where TResponse : IOmgLolResponseData
=> await Request(uri, HttpMethod.Post, data: data, cancellationToken: cancellationToken);
// POST request, but with a file as binary data
private async Task PostBinary(string uri, FileResult? fileResult = null, CancellationToken cancellationToken = default)
where TResponse : IOmgLolResponseData
=> await Request(uri, HttpMethod.Post, file: fileResult, cancellationToken: cancellationToken);
// PUT request
private async Task Put(string uri, TData data, CancellationToken cancellationToken = default)
where TResponse : IOmgLolResponseData
=> await Request(uri, HttpMethod.Put, data: data, cancellationToken: cancellationToken);
// PATCH request
private async Task Patch(string uri, TData data, CancellationToken cancellationToken = default)
where TResponse : IOmgLolResponseData
=> await Request(uri, HttpMethod.Patch, data: data, cancellationToken: cancellationToken);
// Delete request
private async Task Delete(string uri, CancellationToken cancellationToken = default)
where TResponse : IOmgLolResponseData
=> await Request(uri, HttpMethod.Delete, cancellationToken: cancellationToken);
#endregion
#region Specific Requests
public async Task> StatuslogLatest() =>
(await Get("/statuslog/latest"))?.Statuses ?? new List();
public async Task> Statuslog(string address) =>
(await Get($"/address/{address}/statuses"))?.Statuses ?? new List();
public async Task StatuslogBio(string address) {
StatusBioResponseData? responseData = await Get($"/address/{address}/statuses/bio");
return Utilities.MdToHtmlMarkup(responseData?.Bio ?? "");
}
public async Task AccountInfo() =>
await Get("/account/application/info");
public async Task Addresses() =>
await Get("/account/application/addresses");
public async Task StatusPost(string address, StatusPost statusPost) =>
await Post($"/address/{address}/statuses", statusPost);
public async Task> SomePics() =>
(await Get("/pics"))?.Pics ?? new List();
public async Task> SomePics(string address) =>
(await Get($"/address/{address}/pics"))?.Pics ?? new List();
public async Task PutPic(string address, string base64Image) =>
(await Put($"/address/{address}/pics/upload", new PutPic { Pic = base64Image }));
public async Task PutPic(string address, byte[] bytes) =>
await PutPic(address, Convert.ToBase64String(bytes));
public async Task PostPicDescription(string address, string id, string? description) =>
(await Post($"/address/{address}/pics/{id}", new PostPic { Description = description }));
public async Task DeletePic(string address, string id) =>
(await Delete($"/address/{address}/pics/{id}"));
public async Task PatchStatus(string address, string id, string content, string? emoji) =>
(await Patch($"/address/{address}/statuses/", new PatchStatus { Id = id, Content = content, Emoji = emoji }));
public async Task DeleteStatus(string address, string id) =>
(await Delete($"/address/{address}/statuses/{id}"));
public async Task?> NowGarden() =>
(await Get($"/now/garden"))?.Garden ?? new List();
public async Task?> Directory() =>
(await Get($"/directory"))?.Directory ?? new List();
public async Task GetNowPage(string address) =>
(await Get($"/address/{address}/now"))?.Now;
public async Task PostNowPage(string address, string content, bool listed) =>
await Post($"/address/{address}/now", new NowContentData { Content = content, Listed = listed ? 1 : 0 });
public async Task> Ephemeral() =>
(await Get($"/ephemeral"))?.Content?.Select(s => (MarkupString)s)?.ToList() ?? new List();
public async Task PostEphemeral(string content) =>
await Post("/ephemeral", new EphemeralData { Content = content });
public async Task GetProfile(string address) =>
await Get($"/address/{address}/web");
public async Task PostProfile(string address, string content, bool publish = true) =>
await Post($"/address/{address}/web", new PostProfile { Content = content, Publish = publish });
public async Task PostProfile(string address, PostProfile data) =>
await Post($"/address/{address}/web", data);
public async Task?> GetThemes() =>
(await Get($"/theme/list"))?.Themes;
public async Task GetThemePreview(string theme) =>
(MarkupString)((await Get($"/theme/{theme}/preview"))?.Html ?? string.Empty);
public async Task PostProfilePic(string address, FileResult image) =>
await PostBinary($"/address/{address}/pfp", fileResult: image);
#endregion
#region Auth
///
/// Add the api token into the default headers
///
/// The api token
public void AddToken(string? token = null) {
if (token == null) token = Task.Run(() => SecureStorage.GetAsync("accounttoken")).GetAwaiter().GetResult();
if (token != null) _client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
}
///
/// Remove the api token from the default headers
///
public void RemoveToken() {
_client.DefaultRequestHeaders.Remove("Authorization");
}
public async Task OAuth(string code, string client_id, string client_secret, string redirect_uri) {
string? token = null;
string uri = $"/oauth/?code={code}&client_id={client_id}&client_secret={client_secret}&redirect_uri={redirect_uri}&scope=everything";
try {
HttpResponseMessage response = await _client.GetAsync(uri);
if (response.IsSuccessStatusCode) {
TokenResponseData? responseObj = await response.Content.ReadFromJsonAsync(_serializerOptions);
if (responseObj != null && !string.IsNullOrEmpty(responseObj.AccessToken)) {
token = responseObj.AccessToken;
}
}
}
catch (Exception ex) {
Debug.WriteLine(@"\tERROR {0}", ex.Message);
}
return token;
}
#endregion
public async Task GetHtml(string url) {
string? raw = null;
try {
HttpResponseMessage response = await _client.GetAsync(url);
if (response.IsSuccessStatusCode) {
raw = await response.Content.ReadAsStringAsync();
}
}
catch (Exception ex) {
Debug.WriteLine(@"\tERROR {0}", ex.Message);
}
return string.IsNullOrEmpty(raw) ? null : (MarkupString)raw;
}
}
}