Files
decePubClient/Services/AboutService.cs
T
thepraandClaude Opus 5.5 4c88980374
Build / Build (push) Successful in 1m5s
R5 the server's own settings in decePub: instance, rules, announcements, relays, media limits, the staff's log, About
Administration's Pleroma placeholders give way to real sections: Instance (title, descriptions, contact, thumbnail,
privacy policy, terms, the weekly activity), Rules, Announcements, Relays, Media limits and the Moderation log, next to
Reports, blocked servers (now published or obfuscated), statistics and backups; moderators see Reports and the log. Every
save sends the whole settings form built from the latest read, since PrivaPub replaces what it carries. /about shows the
server's documents to anyone. The report dialog picks the rules a violation breaks, and reports show them. Announcements
show above the feed tabs, dismissed and reacted to, live from the user stream. Login names the server.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WpVCoDeM1qUTRp4fXQdfof
2026-10-17 04:32:57 +02:00

97 lines
4.5 KiB
C#

using System.Net;
using decePubClient.Extensions;
using decePubClient.Models;
using decePubClient.Models.Mastodon;
using PrivaPub.ClientModels;
namespace decePubClient.Services
{
/// <summary>
/// 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.
/// </summary>
public class AboutService(IHttpService httpService, ILoggingService logger, CoalescingStringLocalizer localizer)
{
InstanceV2 instance;
/// <summary>The instance's description, kept for the session; read again when `fresh`.</summary>
public async Task<InstanceV2> 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<InstanceV2>(cancellationToken) ?? instance;
}
/// <summary>Drops what was read, after the administrator changed it.</summary>
public void Forget() => instance = null;
/// <summary>The server's rules in their order (not the ones its administrator removed).</summary>
public async Task<List<InstanceRule>> Rules(CancellationToken cancellationToken = default)
{
using var response = await httpService.GetTotallyAnon(APIs.Mastodon.GET_InstanceRules, cancellationToken: cancellationToken);
if (response.IsSuccessStatusCode)
return await response.MastodonReadFromJsonAsync<List<InstanceRule>>(cancellationToken) ?? [];
await LogFailure(response, nameof(Rules), cancellationToken);
return [];
}
public Task<InstanceDocument> ExtendedDescription(CancellationToken cancellationToken = default) =>
Document(APIs.Mastodon.GET_InstanceExtendedDescription, nameof(ExtendedDescription), cancellationToken);
public Task<InstanceDocument> PrivacyPolicy(CancellationToken cancellationToken = default) =>
Document(APIs.Mastodon.GET_InstancePrivacyPolicy, nameof(PrivacyPolicy), cancellationToken);
/// <summary>The terms of service, or null while the server has none (its 404).</summary>
public async Task<TermsOfService> Terms(CancellationToken cancellationToken = default)
{
using var response = await httpService.GetTotallyAnon(APIs.Mastodon.GET_InstanceTermsOfService, cancellationToken: cancellationToken);
if (response.IsSuccessStatusCode)
return await response.MastodonReadFromJsonAsync<TermsOfService>(cancellationToken);
if (response.StatusCode is not HttpStatusCode.NotFound)
await LogFailure(response, nameof(Terms), cancellationToken);
return null;
}
/// <summary>The blocks the administrator published, as the server shows them (an obfuscated one partly starred).</summary>
public async Task<List<PublicDomainBlock>> DomainBlocks(CancellationToken cancellationToken = default)
{
using var response = await httpService.GetTotallyAnon(APIs.Mastodon.GET_InstanceDomainBlocks, cancellationToken: cancellationToken);
if (response.IsSuccessStatusCode)
return await response.MastodonReadFromJsonAsync<List<PublicDomainBlock>>(cancellationToken) ?? [];
await LogFailure(response, nameof(DomainBlocks), cancellationToken);
return [];
}
async Task<InstanceDocument> Document(string route, string caller, CancellationToken cancellationToken)
{
using var response = await httpService.GetTotallyAnon(route, cancellationToken: cancellationToken);
if (response.IsSuccessStatusCode)
return await response.MastodonReadFromJsonAsync<InstanceDocument>(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);
}
}
}