Skip to content

Getting Started

Алексей . edited this page Jul 26, 2026 · 2 revisions

Getting Started

English Русский

Installation, supported target frameworks, the namespace map, and the first working request in both manual and dependency-injection mode.

What the library is

LogsParser is a .NET scraping and integration library for arizonarp.logsparser.info — the Arizona RP game-server log viewer. It performs the site's login and TOTP two-factor flow, fetches pages over HTTP, and turns the returned HTML into immutable typed records. There is no official API behind it: every model is produced by hand-rolled parsing of the site's markup.

Package id LogsParser
Version 1.2.7
Assembly / root namespace LogsParser
Repository / solution / project folder LogTools
Licence CC BY-NC 4.0 — non-commercial use only

The package id and every namespace are LogsParser; the repository, solution and project folder are LogTools. The two names are never interchangeable.

Installation

From NuGet:

dotnet add package LogsParser

As an explicit PackageReference:

<ItemGroup>
  <PackageReference Include="LogsParser" Version="1.2.7" />
</ItemGroup>

Or, when consuming the sources directly from a checkout of the repository, as a ProjectReference to LogTools.csproj (note the project file name — it is not LogsParser.csproj):

<ItemGroup>
  <ProjectReference Include="..\LogTools\LogTools.csproj" />
</ItemGroup>

Target frameworks

The library multi-targets four frameworks and is built with these compiler settings:

Property Value
TargetFrameworks net7.0;net8.0;net9.0;net10.0
LangVersion latest
Nullable enable
ImplicitUsings enable
GenerateDocumentationFile true

Because net7.0 is the floor, every BCL API used by the library must exist there; newer APIs are polyfilled internally instead of raising the floor. Nullable reference types are enabled throughout, so optional values are declared as nullable (LogsParserCredentials?, ICookieStorage?, LogPageMetaInfo?) and non-nullable members are guaranteed to be non-null — collections are Array.Empty<T>() rather than null when absent.

Dependencies

Exactly two, both pinned per target framework:

Target framework Microsoft.Extensions.DependencyInjection Microsoft.Extensions.Logging.Abstractions
net7.0 7.0.0 7.0.1
net8.0 8.0.1 8.0.2
net9.0 9.0.3 9.0.3
net10.0 10.0.5 10.0.5

No HTML parsing library is referenced. All markup handling is hand-rolled regex work over the site's real markup — that is a deliberate design decision, not an omission. Note that only the logging abstractions are referenced: to call AddLogging() or a concrete provider you add Microsoft.Extensions.Logging (or Microsoft.Extensions.Hosting) to your own application.

Namespace map

Folder names inside the repository deliberately do not match namespaces. Never infer a namespace from a file path — take it from the namespace statement, or from this table.

Namespace What lives there
LogsParser LogsParserClient, LogsHtmlParser, LogsRequestUriBuilder, ParserRequest, ParserCookie
LogsParser.Abstractions ILogsParserDataSource, ICookieStorage, MemoryCookieStorage
LogsParser.Models every request, response and configuration record — LogsQuery, AdminActivityQuery, TopOperationsQuery, LogsPage, LogEntry, LogsAccount, LogsFilterCatalog, AdminActivityReport, TopOperationsReport, LogsParserCredentials, LogsParserHttpOptions, LogsParserRegistrationOptions
LogsParser.Net LogsParserHttpDataSource
LogsParser.Parsing LogsFilterCatalogParser
LogsParser.Diagnostics LogsParserLogging
LogsParser.DependencyInjection LogsParserServiceCollectionExtensions
LogsParser.Exceptions every exception type, in one flat namespace

Three examples of the mismatch: Contracts/ICookieStorage.cs is LogsParser.Abstractions, Infrastructure/Http/LogsParserHttpDataSource.cs is LogsParser.Net, and Parsing/LogsHtmlParser.cs is the root LogsParser while every other file in Parsing/ is LogsParser.Parsing.

First request, manual mode

Three objects are involved: a cookie store, a data source that owns the HTTP transport, and the client facade that turns HTML into models.

public LogsParserHttpDataSource(
    LogsParserCredentials? credentials = null,
    ICookieStorage? cookieStorage = null,
    LogsParserHttpOptions? options = null,
    HttpClient? httpClient = null,
    ILoggerFactory? loggerFactory = null)
using LogsParser;
using LogsParser.Abstractions;
using LogsParser.Models;
using LogsParser.Net;

var cookies = new MemoryCookieStorage();

using var dataSource = new LogsParserHttpDataSource(
    credentials: new LogsParserCredentials(
        Login: "my_login",
        Password: "my_password",
        TotpSecret: "BASE32SECRET"),
    cookieStorage: cookies,
    options: new LogsParserHttpOptions
    {
        BaseUri = new Uri("https://arizonarp.logsparser.info/"),
        MaxRetryAttempts = 5,
        WaitForRateLimitReset = true
    });

var client = new LogsParserClient(dataSource);

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

Console.WriteLine($"{page.Entries.Count} entries, account: {page.Account?.Nickname}");

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

LogsParserHttpDataSource implements IDisposable; using var matters because the data source creates and owns an internal HttpClient whenever httpClient is left null. If you pass your own HttpClient, the data source does not dispose it, and its handler must have AllowAutoRedirect = false — see Transport and Authentication.

LogsParserCredentials and LogsParserHttpOptions defaults:

public sealed record LogsParserCredentials(string Login, string Password, string TotpSecret);
Property of LogsParserHttpOptions Type Default Description
BaseUri Uri new("https://arizonarp.logsparser.info/") Base address applied to the internal HttpClient
UserAgent string a Chrome 128 desktop UA string Value of the User-Agent request header
Accept string the browser-like text/html,… list Value of the Accept request header
MaxRetryAttempts int 5 Bound on retries for transient failures
WaitForRateLimitReset bool true On 429, wait until the reset time instead of throwing

Credentials are optional. Constructing the data source without them is legitimate for a session that is already authenticated through a restored cookie store; if the service then asks for authentication, the request fails with AuthenticationRequiredException.

Throws

Exception When
AuthenticationRequiredException The service demanded login or two-factor confirmation and no LogsParserCredentials were configured
AuthenticationFailedException The login flow did not stabilise after 3 attempts
TwoFactorAuthenticationException TOTP generation, timezone resolution, or two-factor confirmation failed
CsrfTokenNotFoundException An auth page contained no <meta name="csrf-token">
AccountConfigurationException The account is not configured and the service redirected to /profile
ReactShieldBypassException The anti-DDoS challenge kept being served after 3 solved tokens
RateLimitExceededException 429 with WaitForRateLimitReset = false, or the retry budget was spent
LogsParserHttpException A non-success status code, or a transient failure that outlived MaxRetryAttempts

All of them derive from LogsParserException, so a single catch (LogsParserException) covers the library. See Exceptions.

First request, DI mode

public static IServiceCollection AddLogsParser(
    this IServiceCollection services,
    Action<LogsParserRegistrationOptions> configure)
using LogsParser;
using LogsParser.DependencyInjection;
using LogsParser.Models;
using Microsoft.Extensions.DependencyInjection;

var services = new ServiceCollection();

services.AddLogging();

services.AddLogsParser(options =>
{
    options.Credentials = new LogsParserCredentials(
        Login: "my_login",
        Password: "my_password",
        TotpSecret: "BASE32SECRET");

    options.HttpOptions = new LogsParserHttpOptions
    {
        MaxRetryAttempts = 5,
        WaitForRateLimitReset = true
    };
});

await using var provider = services.BuildServiceProvider();
using var scope = provider.CreateScope();

var client = scope.ServiceProvider.GetRequiredService<LogsParserClient>();

var page = await client.GetLogsAsync(new LogsQuery(ServerId: 201));

AddLogging() is registered first so that the ILoggerFactory exists when the data source is constructed; the registration reads it out of the provider and installs it into LogsParserLogging. Without it, the library falls back to NullLoggerFactory and stays silent. AddLogging itself comes from the Microsoft.Extensions.Logging package, which the library does not reference on your behalf.

LogsParserRegistrationOptions:

Property Type Default Description
Credentials LogsParserCredentials? null Registered as a singleton only when non-null
HttpOptions LogsParserHttpOptions new() Registered as a singleton
CookieStorageFactory Func<IServiceProvider, ICookieStorage>? null Replaces the default singleton MemoryCookieStorage
DataSourceFactory Func<IServiceProvider, ILogsParserDataSource>? null Replaces the default transient LogsParserHttpDataSource

Resolve the client through a scope, as above: ILogsParserDataSource is registered as transient and LogsParserHttpDataSource is IDisposable, so the container disposes each instance together with the scope that resolved it. A parameterless AddLogsParser() overload exists as well and registers the same graph with default options and no credentials. Full details in Dependency Injection.

How authentication happens

There is no LoginAsync, no SignInAsync, and no "connect first" step — authentication is purely reactive. LogsParserHttpDataSource sends the data request first, with AllowAutoRedirect = false, and reads the 302 Location header the service replies with as a protocol signal:

Redirect target What the data source does
/login Full login: fetch the CSRF token, POST login and password, then fall through to two-factor confirmation
/authenticator Two-factor confirmation only — the password is not re-submitted (a session whose 2FA window lapsed)
/profile Throws AccountConfigurationException — the account itself is not usable

After a successful authentication the original request is retried automatically, so the call the caller made is the call that returns data. Practical consequence: the very first GetLogsAsync of a process may perform the entire login plus TOTP flow and therefore take noticeably longer than the ones after it. Persist the cookie store between runs to skip it — see Cookie Storage.

The TotpSecret is the raw Base32 secret from the authenticator app. The library does not extract it from a QR code or a migration URI; it only consumes the final string. The full state machine, retry counters, rate-limit behaviour and anti-DDoS challenge handling are documented in Transport and Authentication.

Parsing without HTTP

The parsers are pure static functions over strings and have no dependency on the transport, so HTML obtained elsewhere — a file, a proxy, another service — can be parsed directly.

public static LogsPage ParseLogs(string html)
using LogsParser;
using LogsParser.Models;

string html = await File.ReadAllTextAsync("logs.html");

LogsPage page = LogsHtmlParser.ParseLogs(html);

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

LogsHtmlParser lives in the root LogsParser namespace, not in LogsParser.Parsing. ParseLogs throws ArgumentNullException / ArgumentException when html is null, empty or whitespace, and degrades to an empty result when the expected markup is simply absent — unlike ParseAdminActivity and ParseTopOperations, which throw HtmlParsingException. See Parsers.

Where to go next

Page What it covers
Home Overview of the library and the wiki index
LogsParserClient The high-level facade: every method, parameter and return type
Requests and URI Builder LogsQuery, AdminActivityQuery, TopOperationsQuery and LogsRequestUriBuilder normalisation rules
Models Reference for every public record returned by the library
Parsers LogsHtmlParser and LogsFilterCatalogParser used standalone
Transport and Authentication ILogsParserDataSource, the auth state machine, retries, rate limiting, the React challenge
Cookie Storage The ICookieStorage contract and persisting a session across restarts
Dependency Injection AddLogsParser, registration options, service lifetimes
Logging LogsParserLogging, categories and the level taxonomy
Exceptions The exception hierarchy and how to handle it

See also

Clone this wiki locally