-
Notifications
You must be signed in to change notification settings - Fork 0
LogsParserClient
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.
public sealed class LogsParserClientNamespace: 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.
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 | 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.
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. |
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.
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. |
LogsPage — Entries (IReadOnlyList<LogEntry>), MetaInfo (LogPageMetaInfo? with Start, End, Total)
and Account (LogsAccount? read from the page layout). Full field reference in Models.
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
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.
| 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 |
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}");
}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. |
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.
/ — the catalogue is read out of the logs page layout, not from a dedicated endpoint.
When the filter <select> is absent, the parser returns a catalogue with an empty Filters list rather than
throwing.
| Exception | When |
|---|---|
| — | only the exceptions in Exceptions shared by every method; this method validates nothing itself |
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}");
}
}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. |
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.
/ — the same page the filter catalogue is read from.
| Exception | When |
|---|---|
| — | only the exceptions in Exceptions shared by every method |
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.
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. |
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.
/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
| 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 |
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");
}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. |
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.
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.
| 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 |
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}");
}| 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".
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.
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.
LogsParser · CC BY-NC 4.0 — non-commercial use only / только некоммерческое использование
English
- Home
- Getting Started
- LogsParserClient
- Requests and URI Builder
- Models
- Parsers
- Transport and Authentication
- Cookie Storage
- Dependency Injection
- Logging
- Exceptions
Русский
- Главная
- Начало работы
- LogsParserClient
- Запросы и построитель URI
- Модели
- Парсеры
- Транспорт и авторизация
- Хранение cookies
- Внедрение зависимостей
- Логирование
- Исключения