using System.Net; using decePubClient.Extensions; using decePubClient.Models; using decePubClient.Models.Mastodon; using PrivaPub.ClientModels; namespace decePubClient.Services { /// /// What this server says of itself to anyone, through the Mastodon instance API, never with credentials: its title, /// descriptions, contact and rules (/api/v2/instance, read once a session and forgotten when the administrator saves), /// its long description, privacy policy and terms of service (sanitized HTML from the administrator's Markdown), and /// the domain blocks it publishes. Values come back null and lists empty on failure; nothing throws. /// public class AboutService(IHttpService httpService, ILoggingService logger, CoalescingStringLocalizer localizer) { InstanceV2 instance; /// The instance's description, kept for the session; read again when `fresh`. public async Task Instance(bool fresh = false, CancellationToken cancellationToken = default) { if (instance is not null && !fresh) return instance; using var response = await httpService.GetTotallyAnon(APIs.Mastodon.GET_InstanceV2, cancellationToken: cancellationToken); if (!response.IsSuccessStatusCode) { await LogFailure(response, nameof(Instance), cancellationToken); return instance; } return instance = await response.MastodonReadFromJsonAsync(cancellationToken) ?? instance; } /// Drops what was read, after the administrator changed it. public void Forget() => instance = null; /// The server's rules in their order (not the ones its administrator removed). public async Task> Rules(CancellationToken cancellationToken = default) { using var response = await httpService.GetTotallyAnon(APIs.Mastodon.GET_InstanceRules, cancellationToken: cancellationToken); if (response.IsSuccessStatusCode) return await response.MastodonReadFromJsonAsync>(cancellationToken) ?? []; await LogFailure(response, nameof(Rules), cancellationToken); return []; } public Task ExtendedDescription(CancellationToken cancellationToken = default) => Document(APIs.Mastodon.GET_InstanceExtendedDescription, nameof(ExtendedDescription), cancellationToken); public Task PrivacyPolicy(CancellationToken cancellationToken = default) => Document(APIs.Mastodon.GET_InstancePrivacyPolicy, nameof(PrivacyPolicy), cancellationToken); /// The terms of service, or null while the server has none (its 404). public async Task Terms(CancellationToken cancellationToken = default) { using var response = await httpService.GetTotallyAnon(APIs.Mastodon.GET_InstanceTermsOfService, cancellationToken: cancellationToken); if (response.IsSuccessStatusCode) return await response.MastodonReadFromJsonAsync(cancellationToken); if (response.StatusCode is not HttpStatusCode.NotFound) await LogFailure(response, nameof(Terms), cancellationToken); return null; } /// The blocks the administrator published, as the server shows them (an obfuscated one partly starred). public async Task> DomainBlocks(CancellationToken cancellationToken = default) { using var response = await httpService.GetTotallyAnon(APIs.Mastodon.GET_InstanceDomainBlocks, cancellationToken: cancellationToken); if (response.IsSuccessStatusCode) return await response.MastodonReadFromJsonAsync>(cancellationToken) ?? []; await LogFailure(response, nameof(DomainBlocks), cancellationToken); return []; } async Task Document(string route, string caller, CancellationToken cancellationToken) { using var response = await httpService.GetTotallyAnon(route, cancellationToken: cancellationToken); if (response.IsSuccessStatusCode) return await response.MastodonReadFromJsonAsync(cancellationToken); await LogFailure(response, caller, cancellationToken); return null; } async Task LogFailure(HttpResponseMessage response, string caller, CancellationToken cancellationToken) { var result = await response.Content.ReadWebResult(response.StatusCode, localizer, cancellationToken); if (result.StatusCode is not ((int)HttpStatusCode.Gone or (int)HttpStatusCode.NotFound)) await logger.ProcessWarning(result.ToString(localizer), $"{nameof(AboutService)}.{caller}", cancellationToken); } } }