-
Notifications
You must be signed in to change notification settings - Fork 0
LogsParserClient RU
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; см. Исключения.
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?, прочитанный из разметки страницы). Полное описание полей — в
Моделях.
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}");
}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.
/ — каталог читается из разметки страницы логов, а не из отдельной конечной точки.
Если элемент <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}");
}
}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 означает «на странице не было блока аккаунта», а не «не выполнен вход» —
настоящая проблема аутентификации проявляется исключением.
/ — та же страница, из которой читается каталог фильтров.
| Исключение | Когда |
|---|---|
| — | только исключения из раздела Исключения, общие для всех методов |
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 не нужно.
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). См.
Модели.
/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");
}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). См.
Модели.
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 |
Различие сделано намеренно. Страница логов без строк — законный ответ: узкий фильтр или пустой период дают ровно такую разметку, и вызывающему коду не следует ловить исключение только ради опроса новых записей. Отчёты же по активности администраторов и топу операций на исправной странице всегда содержат и таблицу, и период, поэтому их отсутствие означает, что разметка изменилась или пришла не та страница, которую запрашивали, — на этом лучше упасть, чем сообщить «ноль администраторов».
Аргумент 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 можно разбирать, вообще не обращаясь к сети.
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
- Внедрение зависимостей
- Логирование
- Исключения