Skip to content

LogsParserClient

Алексей . edited this page Jul 26, 2026 · 1 revision

LogsParserClient

English Русский

LogsParserClient is the high-level facade of the library: one method per page of arizonarp.logsparser.info, each building a relative URI, asking an ILogsParserDataSource for the HTML and handing that HTML to a parser. This page documents the class, its constructor and all five methods.

Declaration

public sealed class LogsParserClient

Namespace: LogsParser. The class holds nothing but the data source it was constructed with, so a single instance can be reused for as long as that data source lives. It is not IDisposable; the transport (LogsParserHttpDataSource) is.

Constructor

public LogsParserClient(ILogsParserDataSource dataSource)
Parameter Type Default Description
dataSource LogsParser.Abstractions.ILogsParserDataSource — The transport that turns a ParserRequest into HTML. Stored as-is; the client never disposes it.

Throws

Exception When
ArgumentNullException dataSource is null
using LogsParser;
using LogsParser.Abstractions;
using LogsParser.Models;
using LogsParser.Net;

using var dataSource = new LogsParserHttpDataSource(
    credentials: new LogsParserCredentials("my_login", "my_password", "BASE32SECRET"),
    cookieStorage: new MemoryCookieStorage());

var client = new LogsParserClient(dataSource);

To let the container build both objects instead, see Dependency Injection.

Method overview

Method Returns Site page it reads Parser used
GetLogsAsync Task<LogsPage> the logs page — query string from LogsRequestUriBuilder.BuildLogsUri(query) LogsHtmlParser.ParseLogs
GetLogsFilterCatalogAsync Task<LogsFilterCatalog> / LogsFilterCatalogParser.Parse
GetCurrentAccountAsync Task<LogsAccount?> / LogsAccountParser (internal)
GetAdminActivityAsync Task<AdminActivityReport> /admins?… — from LogsRequestUriBuilder.BuildAdminActivityUri(query) LogsHtmlParser.ParseAdminActivity
GetTopOperationsAsync Task<TopOperationsReport> top?… — from LogsRequestUriBuilder.BuildTopOperationsUri(query) LogsHtmlParser.ParseTopOperations

All relative URIs are resolved by the transport against its base URI, https://arizonarp.logsparser.info/ by default. LogsAccountParser is an internal implementation detail and is not part of the public API — the account it produces is reachable through GetCurrentAccountAsync, LogsPage.Account and LogsFilterCatalog.Account.

Parameters shared by every method

Every method ends with the same two optional parameters.

Parameter Type Default Description
cookieStorage ICookieStorage? null Overrides the data source's default cookie storage for this one request. When null, the data source uses the storage it was constructed with. Passed through as new ParserRequest(uri, cookieStorage).
cancellationToken CancellationToken default Forwarded to ILogsParserDataSource.GetContentAsync.

Exceptions shared by every method

Nothing in these lists is caught by the client — everything the transport or the parser raises propagates to the caller unchanged. The exceptions below come from the shipped LogsParserHttpDataSource; a custom ILogsParserDataSource may raise its own.

Exception When
AuthenticationRequiredException the service asked for login or two-factor confirmation and no LogsParserCredentials were configured
AuthenticationFailedException login did not stabilise within the three internal auth attempts
TwoFactorAuthenticationException two-factor confirmation did not stabilise, or the TOTP timezone could not be resolved
CsrfTokenNotFoundException the login or authenticator page carried no <meta name="csrf-token">
AccountConfigurationException the service redirected to /profile — the account is not configured for log access
ReactShieldBypassException the React anti-DDoS challenge could not be solved
RateLimitExceededException HTTP 429 while WaitForRateLimitReset is false; carries RetryAfterSeconds and ResetAt
LogsParserHttpException a non-success status or a transport failure that survived every retry attempt
OperationCanceledException cancellationToken was cancelled
ArgumentNullException / ArgumentException the data source returned null, empty or whitespace-only HTML — the parsers validate their input (paramName is html)

All of these except the Argument* family derive from LogsParserException; see Exceptions.

GetLogsAsync

public async Task<LogsPage> GetLogsAsync(
    LogsQuery query,
    ICookieStorage? cookieStorage = null,
    CancellationToken cancellationToken = default)
Parameter Type Default Description
query LogsParser.Models.LogsQuery — Server, filters, period, player/target/IP, paging, sorting and dynamic[n] parameters. See Requests and URI Builder.
cookieStorage ICookieStorage? null Per-request storage override.
cancellationToken CancellationToken default Cancellation.

Returns

LogsPage — Entries (IReadOnlyList<LogEntry>), MetaInfo (LogPageMetaInfo? with Start, End, Total) and Account (LogsAccount? read from the page layout). Full field reference in Models.

Requested URI

LogsRequestUriBuilder.BuildLogsUri(query) — a query-string-only relative URI that always starts with ?, for example:

?server_number=201&sort=desc&limit=1000&page=1&type%5B%5D=warn&type%5B%5D=mute&min_period=2026-07-25%2000%3A00%3A00

Missing markup

When the page contains no <tbody>, the parser returns a LogsPage with an empty Entries list — plus whatever meta info and account markup it did find. No exception is raised.

Throws

Exception When
ArgumentNullException query is null
ArgumentOutOfRangeException query.ServerId ≤ 0, query.Page ≤ 0 or query.Limit ≤ 0
ArgumentException query.Sort is neither "desc" nor "asc"; an AdditionalParameters key is reserved or does not match ^dynamic\[\d+\]$
— plus every exception in Exceptions shared by every method

Example

using LogsParser;
using LogsParser.Models;

var page = await client.GetLogsAsync(new LogsQuery(
    ServerId: 201,
    Filters: ["warn", "mute"],
    PeriodFrom: DateTime.UtcNow.AddDays(-7),
    PeriodTo: DateTime.UtcNow,
    Page: 1,
    Limit: 1000));

Console.WriteLine($"{page.Entries.Count} entries of {page.MetaInfo?.Total}");

foreach (var entry in page.Entries)
{
    Console.WriteLine($"{entry.Timestamp:u} {entry.Text}");
}

GetLogsFilterCatalogAsync

public async Task<LogsFilterCatalog> GetLogsFilterCatalogAsync(
    ICookieStorage? cookieStorage = null,
    CancellationToken cancellationToken = default)
Parameter Type Default Description
cookieStorage ICookieStorage? null Per-request storage override.
cancellationToken CancellationToken default Cancellation.

Returns

LogsFilterCatalog — Filters (IReadOnlyList<LogsFilterDefinition>, each with Code, Name and its AdditionalParameters of QueryKey / Label) and Account (LogsAccount?). See Models.

The Code values are what LogsQuery.Filters expects; the QueryKey values are exactly the dynamic[n] keys LogsQuery.AdditionalParameters accepts.

Requested URI

/ — the catalogue is read out of the logs page layout, not from a dedicated endpoint.

Missing markup

When the filter <select> is absent, the parser returns a catalogue with an empty Filters list rather than throwing.

Throws

Exception When
— only the exceptions in Exceptions shared by every method; this method validates nothing itself

Example

var catalog = await client.GetLogsFilterCatalogAsync();

foreach (var filter in catalog.Filters)
{
    Console.WriteLine($"{filter.Code} — {filter.Name}");

    foreach (var parameter in filter.AdditionalParameters)
    {
        Console.WriteLine($"    {parameter.QueryKey} = {parameter.Label}");
    }
}

GetCurrentAccountAsync

public async Task<LogsAccount?> GetCurrentAccountAsync(
    ICookieStorage? cookieStorage = null,
    CancellationToken cancellationToken = default)
Parameter Type Default Description
cookieStorage ICookieStorage? null Per-request storage override.
cancellationToken CancellationToken default Cancellation.

Returns

LogsAccount? — Nickname, Badges (IReadOnlyList<LogsAccountBadge>) and AvailableServers (IReadOnlyList<LogsAccountServer> with Id, Name, DisplayName, IsSelected). See Models.

The result is null when the layout carries no account markup. That is a normal return value, not an error: the method logs a warning and returns null. Because the request still goes through the authenticating transport, a null here means "the page did not contain the account block", not "not logged in" — a genuine auth problem surfaces as an exception instead.

Requested URI

/ — the same page the filter catalogue is read from.

Throws

Exception When
— only the exceptions in Exceptions shared by every method

Example

var account = await client.GetCurrentAccountAsync();

if (account is null)
{
    Console.WriteLine("no account block in the layout");
}
else
{
    Console.WriteLine(account.Nickname);

    foreach (var server in account.AvailableServers)
    {
        Console.WriteLine($"{server.Id} {server.DisplayName}{(server.IsSelected ? " (selected)" : "")}");
    }
}

Both LogsPage.Account and LogsFilterCatalog.Account are filled from the same markup, so a call that already fetched logs or the catalogue does not need a separate GetCurrentAccountAsync round trip.

GetAdminActivityAsync

public async Task<AdminActivityReport> GetAdminActivityAsync(
    AdminActivityQuery query,
    ICookieStorage? cookieStorage = null,
    CancellationToken cancellationToken = default)
Parameter Type Default Description
query LogsParser.Models.AdminActivityQuery — PeriodFrom and PeriodTo, both required DateTime values.
cookieStorage ICookieStorage? null Per-request storage override.
cancellationToken CancellationToken default Cancellation.

Returns

AdminActivityReport — Period ((DateTime From, DateTime To) read back from the page), Entries (IReadOnlyList<AdminActivityEntry>, each carrying per-day Details) and MetaInfo (AdminActivityMetaInfo with AdminCount, PeriodDays, TotalReports, TotalBans). See Models.

Requested URI

/admins?min_period=…&max_period=…, with both bounds formatted as yyyy-MM-dd HH:mm:ss in the invariant culture and URL-encoded, for example:

/admins?min_period=2026-07-19%2012%3A00%3A00&max_period=2026-07-26%2012%3A00%3A00

Throws

Exception When
NullReferenceException query is null — the method reads query.PeriodFrom and query.PeriodTo in its debug log call before the URI builder gets a chance to null-check
HtmlParsingException the min_period / max_period inputs are missing ("Admin activity period was not found.") or the table is missing ("Admin activity table was not found.")
— plus every exception in Exceptions shared by every method

Example

var activity = await client.GetAdminActivityAsync(
    new AdminActivityQuery(
        PeriodFrom: DateTime.UtcNow.AddDays(-7),
        PeriodTo: DateTime.UtcNow));

Console.WriteLine($"{activity.MetaInfo.AdminCount} admins over {activity.MetaInfo.PeriodDays} days");

foreach (var admin in activity.Entries)
{
    Console.WriteLine($"{admin.Nickname} [{admin.Id}] {admin.TotalReports} reports, {admin.TotalOnline:g} online");
}

GetTopOperationsAsync

public async Task<TopOperationsReport> GetTopOperationsAsync(
    TopOperationsQuery query,
    ICookieStorage? cookieStorage = null,
    CancellationToken cancellationToken = default)
Parameter Type Default Description
query LogsParser.Models.TopOperationsQuery — Filter (string?, sent as type) and Date (DateTime?), both optional.
cookieStorage ICookieStorage? null Per-request storage override.
cancellationToken CancellationToken default Cancellation.

Returns

TopOperationsReport — Date (DateOnly read back from the page), Entries (IReadOnlyList<TopOperationsEntry> with Nickname, Id, Ip, RegistrationIp, TotalTransactions, Sum) and MetaInfo (TopOperationsMetaInfo with PlayerCount, TotalTransactions, TotalSum). See Models.

Requested URI

top?type=…&date=… — note that this is the one relative URI in the client that begins with a path segment instead of / or ?. Empty or whitespace parts are omitted, so a new TopOperationsQuery() produces the bare top?. Date is formatted as yyyy-MM-dd HH:mm:ss in the invariant culture and URL-encoded.

Throws

Exception When
NullReferenceException query is null — the method reads query.Filter and query.Date in its debug log call before the URI builder gets a chance to null-check
HtmlParsingException the date marker is missing ("Top operations date was not found.") or the table is missing ("Top operations table was not found.")
— plus every exception in Exceptions shared by every method

Example

var top = await client.GetTopOperationsAsync(
    new TopOperationsQuery(
        Filter: "bank",
        Date: DateTime.UtcNow.Date));

Console.WriteLine($"{top.Date:yyyy-MM-dd}: {top.MetaInfo.TotalTransactions} transactions, {top.MetaInfo.TotalSum} total");

foreach (var entry in top.Entries)
{
    Console.WriteLine($"{entry.Nickname} [{entry.Id}] {entry.Sum} from {entry.Ip}");
}

Missing markup: an intentional asymmetry

Call Markup absent Result
GetLogsAsync no <tbody> LogsPage with an empty Entries list
GetLogsFilterCatalogAsync no filter <select> LogsFilterCatalog with an empty Filters list
GetCurrentAccountAsync no account block null
GetAdminActivityAsync no period inputs or no table HtmlParsingException
GetTopOperationsAsync no date marker or no table HtmlParsingException

The difference is deliberate. A logs page with no rows is a legitimate answer — a narrow filter or an empty period produces exactly that markup, and callers should not have to catch an exception to poll for new entries. The admin activity and top operations reports, by contrast, always carry their table and their period on a healthy page, so absence there means the markup changed or the response is not the page that was requested — a condition worth failing on rather than reporting as "zero admins".

Per-request cookie storage

The cookieStorage argument replaces the data source's own storage for a single call, which lets one client serve several sessions:

using LogsParser;
using LogsParser.Abstractions;
using LogsParser.Models;
using LogsParser.Net;

var alice = new MemoryCookieStorage();
var bob = new MemoryCookieStorage();

using var dataSource = new LogsParserHttpDataSource(
    credentials: new LogsParserCredentials("my_login", "my_password", "BASE32SECRET"),
    cookieStorage: new MemoryCookieStorage());

var client = new LogsParserClient(dataSource);

var aliceLogs = await client.GetLogsAsync(new LogsQuery(ServerId: 201), cookieStorage: alice);
var bobLogs = await client.GetLogsAsync(new LogsQuery(ServerId: 202), cookieStorage: bob);

Session cookies and the anti-DDoS token live in the same flat store, so persisting a storage persists both. See Cookie Storage.

The client is a thin facade

LogsParserClient adds no behaviour of its own beyond logging: it builds a URI, calls the data source and calls a parser. Each of those three pieces is public and usable on its own — build a URI with LogsRequestUriBuilder, fetch HTML through any ILogsParserDataSource, parse a string you already have with the static parsers:

using LogsParser;
using LogsParser.Abstractions;
using LogsParser.Models;

var uri = LogsRequestUriBuilder.BuildLogsUri(new LogsQuery(ServerId: 201, Limit: 500));
var html = await dataSource.GetContentAsync(new ParserRequest(uri));
var page = LogsHtmlParser.ParseLogs(html);

The parsers are pure static functions over strings with no HTTP dependency, so cached or captured HTML can be parsed without touching the network at all.

See also

Clone this wiki locally