Skip to content

LogsParserClient RU

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

LogsParserClient

English Русский

LogsParserClient — высокоуровневый фасад библиотеки: по одному методу на каждую страницу arizonarp.logsparser.info. Каждый метод строит относительный URI, запрашивает HTML у ILogsParserDataSource и передаёт этот HTML парсеру. Страница описывает класс, его конструктор и все пять методов.

Объявление

public sealed class LogsParserClient

Пространство имён: LogsParser. Класс хранит только источник данных, с которым был создан, поэтому один экземпляр можно переиспользовать столько, сколько живёт этот источник. Сам класс не реализует IDisposable — это делает транспорт (LogsParserHttpDataSource).

Конструктор

public LogsParserClient(ILogsParserDataSource dataSource)
Параметр Тип По умолчанию Описание
dataSource LogsParser.Abstractions.ILogsParserDataSource — Транспорт, превращающий ParserRequest в HTML. Сохраняется как есть; клиент никогда его не освобождает.

Исключения

Исключение Когда
ArgumentNullException dataSource равен 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);

Чтобы оба объекта создавал контейнер, см. Внедрение зависимостей.

Обзор методов

Метод Возвращает Читаемая страница сайта Используемый парсер
GetLogsAsync Task<LogsPage> страница логов — строка запроса из LogsRequestUriBuilder.BuildLogsUri(query) LogsHtmlParser.ParseLogs
GetLogsFilterCatalogAsync Task<LogsFilterCatalog> / LogsFilterCatalogParser.Parse
GetCurrentAccountAsync Task<LogsAccount?> / LogsAccountParser (internal)
GetAdminActivityAsync Task<AdminActivityReport> /admins?… — из LogsRequestUriBuilder.BuildAdminActivityUri(query) LogsHtmlParser.ParseAdminActivity
GetTopOperationsAsync Task<TopOperationsReport> top?… — из LogsRequestUriBuilder.BuildTopOperationsUri(query) LogsHtmlParser.ParseTopOperations

Все относительные URI транспорт разрешает относительно своего базового адреса — по умолчанию https://arizonarp.logsparser.info/. LogsAccountParser — внутренняя (internal) деталь реализации и частью публичного API не является: полученный им аккаунт доступен через GetCurrentAccountAsync, LogsPage.Account и LogsFilterCatalog.Account.

Параметры, общие для всех методов

Каждый метод завершается одной и той же парой необязательных параметров.

Параметр Тип По умолчанию Описание
cookieStorage ICookieStorage? null Переопределяет хранилище cookie источника данных для одного этого запроса. При null используется хранилище, с которым был создан источник. Передаётся как new ParserRequest(uri, cookieStorage).
cancellationToken CancellationToken default Передаётся в ILogsParserDataSource.GetContentAsync.

Исключения, общие для всех методов

Клиент не перехватывает ничего: всё, что бросают транспорт и парсер, доходит до вызывающего кода без изменений. Перечисленные ниже исключения бросает поставляемый LogsParserHttpDataSource; собственная реализация ILogsParserDataSource может бросать свои.

Исключение Когда
AuthenticationRequiredException сервис запросил вход или двухфакторное подтверждение, а LogsParserCredentials не заданы
AuthenticationFailedException вход не стабилизировался за три внутренние попытки аутентификации
TwoFactorAuthenticationException двухфакторное подтверждение не стабилизировалось либо не удалось разрешить часовой пояс для TOTP
CsrfTokenNotFoundException на странице входа или подтверждения не оказалось <meta name="csrf-token">
AccountConfigurationException сервис перенаправил на /profile — аккаунт не настроен для доступа к логам
ReactShieldBypassException не удалось решить задачу React-защиты от DDoS
RateLimitExceededException HTTP 429 при WaitForRateLimitReset = false; содержит RetryAfterSeconds и ResetAt
LogsParserHttpException неуспешный статус или сбой транспорта, переживший все попытки повтора
OperationCanceledException cancellationToken отменён
ArgumentNullException / ArgumentException источник данных вернул null, пустой HTML или HTML из одних пробелов — парсеры проверяют вход (paramName равен html)

Все они, кроме семейства Argument*, наследуются от LogsParserException; см. Исключения.

GetLogsAsync

public async Task<LogsPage> GetLogsAsync(
    LogsQuery query,
    ICookieStorage? cookieStorage = null,
    CancellationToken cancellationToken = default)
Параметр Тип По умолчанию Описание
query LogsParser.Models.LogsQuery — Сервер, фильтры, период, игрок/цель/IP, постраничность, сортировка и параметры dynamic[n]. См. Запросы и построитель URI.
cookieStorage ICookieStorage? null Переопределение хранилища для одного запроса.
cancellationToken CancellationToken default Отмена.

Возвращаемое значение

LogsPage — Entries (IReadOnlyList<LogEntry>), MetaInfo (LogPageMetaInfo? с полями Start, End, Total) и Account (LogsAccount?, прочитанный из разметки страницы). Полное описание полей — в Моделях.

Запрашиваемый URI

LogsRequestUriBuilder.BuildLogsUri(query) — относительный URI, состоящий из одной строки запроса и всегда начинающийся с ?, например:

?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

Отсутствующая разметка

Если на странице нет <tbody>, парсер возвращает LogsPage с пустым списком Entries — вместе с той метаинформацией и разметкой аккаунта, которые всё же нашлись. Исключение не бросается.

Исключения

Исключение Когда
ArgumentNullException query равен null
ArgumentOutOfRangeException query.ServerId ≤ 0, query.Page ≤ 0 или query.Limit ≤ 0
ArgumentException query.Sort не равен "desc" или "asc"; ключ в AdditionalParameters зарезервирован или не соответствует ^dynamic\[\d+\]$
— а также все исключения из раздела Исключения, общие для всех методов

Пример

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)
Параметр Тип По умолчанию Описание
cookieStorage ICookieStorage? null Переопределение хранилища для одного запроса.
cancellationToken CancellationToken default Отмена.

Возвращаемое значение

LogsFilterCatalog — Filters (IReadOnlyList<LogsFilterDefinition>, каждый с Code, Name и своими AdditionalParameters из пар QueryKey / Label) и Account (LogsAccount?). См. Модели.

Значения Code — это именно то, что ожидает LogsQuery.Filters; значения QueryKey — те самые ключи dynamic[n], которые принимает LogsQuery.AdditionalParameters.

Запрашиваемый URI

/ — каталог читается из разметки страницы логов, а не из отдельной конечной точки.

Отсутствующая разметка

Если элемент <select> с фильтрами отсутствует, парсер возвращает каталог с пустым списком Filters, а не бросает исключение.

Исключения

Исключение Когда
— только исключения из раздела Исключения, общие для всех методов; собственных проверок у метода нет

Пример

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)
Параметр Тип По умолчанию Описание
cookieStorage ICookieStorage? null Переопределение хранилища для одного запроса.
cancellationToken CancellationToken default Отмена.

Возвращаемое значение

LogsAccount? — Nickname, Badges (IReadOnlyList<LogsAccountBadge>) и AvailableServers (IReadOnlyList<LogsAccountServer> с полями Id, Name, DisplayName, IsSelected). См. Модели.

Результат равен null, когда в разметке нет блока аккаунта. Это нормальное возвращаемое значение, а не ошибка: метод пишет предупреждение в лог и возвращает null. Поскольку запрос всё равно проходит через аутентифицирующий транспорт, null означает «на странице не было блока аккаунта», а не «не выполнен вход» — настоящая проблема аутентификации проявляется исключением.

Запрашиваемый URI

/ — та же страница, из которой читается каталог фильтров.

Исключения

Исключение Когда
— только исключения из раздела Исключения, общие для всех методов

Пример

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)" : "")}");
    }
}

LogsPage.Account и LogsFilterCatalog.Account заполняются из той же разметки, поэтому вызову, который уже получил логи или каталог, отдельное обращение к GetCurrentAccountAsync не нужно.

GetAdminActivityAsync

public async Task<AdminActivityReport> GetAdminActivityAsync(
    AdminActivityQuery query,
    ICookieStorage? cookieStorage = null,
    CancellationToken cancellationToken = default)
Параметр Тип По умолчанию Описание
query LogsParser.Models.AdminActivityQuery — PeriodFrom и PeriodTo — оба обязательные значения DateTime.
cookieStorage ICookieStorage? null Переопределение хранилища для одного запроса.
cancellationToken CancellationToken default Отмена.

Возвращаемое значение

AdminActivityReport — Period ((DateTime From, DateTime To), считанный обратно со страницы), Entries (IReadOnlyList<AdminActivityEntry>, каждая запись содержит подневные Details) и MetaInfo (AdminActivityMetaInfo с полями AdminCount, PeriodDays, TotalReports, TotalBans). См. Модели.

Запрашиваемый URI

/admins?min_period=…&max_period=…, где обе границы форматируются как yyyy-MM-dd HH:mm:ss в инвариантной культуре и кодируются для URL, например:

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

Исключения

Исключение Когда
NullReferenceException query равен null — метод читает query.PeriodFrom и query.PeriodTo в отладочном журнале до того, как построитель URI успеет проверить аргумент на null
HtmlParsingException отсутствуют поля min_period / max_period ("Admin activity period was not found.") либо таблица ("Admin activity table was not found.")
— а также все исключения из раздела Исключения, общие для всех методов

Пример

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)
Параметр Тип По умолчанию Описание
query LogsParser.Models.TopOperationsQuery — Filter (string?, отправляется как type) и Date (DateTime?), оба необязательные.
cookieStorage ICookieStorage? null Переопределение хранилища для одного запроса.
cancellationToken CancellationToken default Отмена.

Возвращаемое значение

TopOperationsReport — Date (DateOnly, считанный обратно со страницы), Entries (IReadOnlyList<TopOperationsEntry> с полями Nickname, Id, Ip, RegistrationIp, TotalTransactions, Sum) и MetaInfo (TopOperationsMetaInfo с полями PlayerCount, TotalTransactions, TotalSum). См. Модели.

Запрашиваемый URI

top?type=…&date=… — это единственный относительный URI клиента, который начинается с сегмента пути, а не с / или ?. Пустые части и части из одних пробелов опускаются, поэтому new TopOperationsQuery() даёт голое top?. Date форматируется как yyyy-MM-dd HH:mm:ss в инвариантной культуре и кодируется для URL.

Исключения

Исключение Когда
NullReferenceException query равен null — метод читает query.Filter и query.Date в отладочном журнале до того, как построитель URI успеет проверить аргумент на null
HtmlParsingException отсутствует маркер даты ("Top operations date was not found.") либо таблица ("Top operations table was not found.")
— а также все исключения из раздела Исключения, общие для всех методов

Пример

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}");
}

Отсутствующая разметка: намеренная асимметрия

Вызов Разметка отсутствует Результат
GetLogsAsync нет <tbody> LogsPage с пустым списком Entries
GetLogsFilterCatalogAsync нет <select> с фильтрами LogsFilterCatalog с пустым списком Filters
GetCurrentAccountAsync нет блока аккаунта null
GetAdminActivityAsync нет полей периода или таблицы HtmlParsingException
GetTopOperationsAsync нет маркера даты или таблицы HtmlParsingException

Различие сделано намеренно. Страница логов без строк — законный ответ: узкий фильтр или пустой период дают ровно такую разметку, и вызывающему коду не следует ловить исключение только ради опроса новых записей. Отчёты же по активности администраторов и топу операций на исправной странице всегда содержат и таблицу, и период, поэтому их отсутствие означает, что разметка изменилась или пришла не та страница, которую запрашивали, — на этом лучше упасть, чем сообщить «ноль администраторов».

Хранилище cookie для одного запроса

Аргумент cookieStorage заменяет собственное хранилище источника данных на время одного вызова, что позволяет одному клиенту обслуживать несколько сессий:

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);

Cookie сессии и токен анти-DDoS-защиты лежат в одном плоском хранилище, поэтому сохранение хранилища сохраняет и то и другое. См. Хранилище cookie.

Клиент — намеренно тонкий фасад

LogsParserClient не добавляет никакой логики сверх логирования: он строит URI, вызывает источник данных и вызывает парсер. Каждая из этих трёх частей публична и применима сама по себе — постройте URI через построитель URI, получите HTML любым ILogsParserDataSource, разберите уже имеющуюся строку статическими парсерами:

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);

Парсеры — чистые статические функции над строками без зависимости от HTTP, поэтому закэшированный или заранее сохранённый HTML можно разбирать, вообще не обращаясь к сети.

См. также

Clone this wiki locally