-
Notifications
You must be signed in to change notification settings - Fork 0
Models RU
Справочник по всем публичным моделям библиотеки: страницы логов, данные аккаунта, каталог фильтров, отчёты по активности администраторов и топу операций, а также транспортные и конфигурационные типы.
Эти правила действуют для всех моделей на этой странице.
-
Все публичные модели — запечатанные неизменяемые записи (
sealed record). Единственное исключение —LogsParserRegistrationOptions: этоsealed classс изменяемыми свойствами, потому что он заполняется конфигурационным делегатом. -
Коллекции никогда не бывают
null. Если модель объявляетIReadOnlyList<T>, он всегда есть; когда на исходной странице ничего нет, парсер записываетArray.Empty<T>(). ПроверяйтеCount == 0, а неnull. -
Необязательные скалярные значения объявлены как nullable.
long?,string?и nullable-ссылки на модели означают «страница этого не показала», а не «ноль» или «пусто». -
Записи дают равенство по значению,
ToString(),Deconstructиwith. Позиционные параметры становятся публичными init-свойствами с теми же именами. - Модели ничего не валидируют. Их конструирование не выбрасывает исключений; перечисленные ниже исключения приходят от парсеров и транспорта, которые эти модели создают.
| Типы | Пространство имён |
|---|---|
ParserRequest, ParserCookie
|
LogsParser |
| Все остальные модели этой страницы | LogsParser.Models |
Имена папок в репозитории не совпадают с пространствами имён: в Models/Shared/CommonModels.cs объявлено
namespace LogsParser;, во всех остальных файлах под Models/ — namespace LogsParser.Models;.
| Модель | Кем создаётся | Исключения |
|---|---|---|
LogsPage |
LogsParserClient.GetLogsAsync, LogsHtmlParser.ParseLogs
|
ArgumentException на пустом HTML; при отсутствии разметки таблицы деградирует до пустого списка Entries
|
LogsAccount |
LogsParserClient.GetCurrentAccountAsync; также вложена в LogsPage.Account и LogsFilterCatalog.Account
|
ArgumentException на пустом HTML; null, если в разметке нет данных аккаунта |
LogsFilterCatalog |
LogsParserClient.GetLogsFilterCatalogAsync, LogsFilterCatalogParser.Parse
|
ArgumentException на пустом HTML |
AdminActivityReport |
LogsParserClient.GetAdminActivityAsync, LogsHtmlParser.ParseAdminActivity
|
ArgumentException на пустом HTML; HtmlParsingException, если не найдены поля периода или таблица |
TopOperationsReport |
LogsParserClient.GetTopOperationsAsync, LogsHtmlParser.ParseTopOperations
|
ArgumentException на пустом HTML; HtmlParsingException, если не найдены дата или таблица |
Модели запросов LogsQuery, AdminActivityQuery и TopOperationsQuery описаны на странице
Запросы и построитель URI.
public sealed record LogsPage(
IReadOnlyList<LogEntry> Entries,
LogPageMetaInfo? MetaInfo = null,
LogsAccount? Account = null);| Член | Тип | Описание |
|---|---|---|
Entries |
IReadOnlyList<LogEntry> |
Строки таблицы логов в порядке страницы. Пустой список, если на странице нет <tbody>. |
MetaInfo |
LogPageMetaInfo? |
Счётчики пагинации; null, если на странице нет строки пагинации. |
Account |
LogsAccount? |
Аккаунт, прочитанный из разметки страницы; null, если данных аккаунта в ней нет. |
var page = await client.GetLogsAsync(new LogsQuery(ServerId: 18, Limit: 100));
Console.WriteLine($"{page.Entries.Count} entries of {page.MetaInfo?.Total} total");
Console.WriteLine($"signed in as {page.Account?.Nickname}");public sealed record LogEntry(
DateTime Timestamp,
string Text,
string Html,
LogParticipant? Sender = null,
LogParticipant? Target = null)
{
public IReadOnlyList<LogRevealedValue> RevealedValues { get; init; } = Array.Empty<LogRevealedValue>();
}| Член | Тип | Описание |
|---|---|---|
Timestamp |
DateTime |
Дата и время строки, разобранные с инвариантной культурой. |
Text |
string |
Текст действия в том виде, в котором страница его показывает. |
Html |
string |
Полная внутренняя разметка ячейки действия, включая скрытые блоки. |
Sender |
LogParticipant? |
Участник «I»; null, если у строки нет данных по нему. |
Target |
LogParticipant? |
Участник «II»; null, если у строки нет данных по нему. |
RevealedValues |
IReadOnlyList<LogRevealedValue> |
Блоки, скрытые сайтом за кнопкой-глазом. Пусто, если таких блоков нет. |
RevealedValues — это init-свойство, а не позиционный параметр записи. Так сделано намеренно: добавление
позиционного параметра изменило бы арность первичного конструктора и сгенерированный Deconstruct уже
опубликованной записи, поэтому свойство добавлено в тело записи. Первичный конструктор по-прежнему принимает
пять аргументов, а Deconstruct по-прежнему возвращает (Timestamp, Text, Html, Sender, Target). Коллекция
задаётся через инициализатор объекта:
var entry = new LogEntry(DateTime.UtcNow, "Готовый сет:", "<td>…</td>")
{
RevealedValues = [new LogRevealedValue("Значение строки 1", "1.БоДжек Редкость 6(9553)", "…")]
};
var (timestamp, text, html, sender, target) = entry;Text и Html — не два представления одного и того же. Text содержит только то, что страница показывает
видимо: содержимое, скрытое за кнопкой-глазом, из него удалено, — тогда как Html сохраняет полную разметку
ячейки вместе со скрытыми блоками. Для строки со скрытым сетом:
// Text ограничен видимой частью…
Console.WriteLine(entry.Text); // Игрок Ivan_Kuznetsov арендует объявление №4851. Готовый сет:
// …а Html по-прежнему содержит блок app__hidden с самим сетом.
Console.WriteLine(entry.Html.Contains("app__hidden")); // TrueВсё, что было вырезано из Text, доступно в структурированном виде через RevealedValues.
public sealed record LogRevealedValue(
string Label,
string Text,
string Html);| Член | Тип | Описание |
|---|---|---|
Label |
string |
Атрибут data-title блока, например Значение строки 1. Пустая строка, если атрибута нет. |
Text |
string |
Раскрытое значение в виде текста с сохранением построчной структуры исходного блока. |
Html |
string |
Исходная внутренняя разметка блока. |
Раскрываемые значения чаще всего встречаются под фильтром item_market, где строка прячет за кнопкой-глазом
целый набор предметов. Поскольку Text сохраняет переводы строк, набор можно разложить обратно на строки:
foreach (var value in entry.RevealedValues)
{
Console.WriteLine(value.Label); // Значение строки 1
foreach (var line in value.Text.Split('\n', StringSplitOptions.RemoveEmptyEntries))
{
Console.WriteLine($" {line.Trim()}"); // 1.БоДжек Редкость 6(9553), Описание: …, 2.ТВ Робот(8583), …
}
}public sealed record LogParticipant(
long? Money = null,
long? Bank = null,
long? Donate = null,
LogAdditionalInfo? AdditionalInfo = null,
string? LastIp = null,
string? RegistrationIp = null);| Член | Тип | Описание |
|---|---|---|
Money |
long? |
Наличные на момент записи. |
Bank |
long? |
Баланс банковского счёта. |
Donate |
long? |
Баланс донат-валюты. |
AdditionalInfo |
LogAdditionalInfo? |
Блок сведений об аккаунте, скрытый за кнопкой-глазом в ячейке данных. |
LastIp |
string? |
Последний IP-адрес из ячейки IP. |
RegistrationIp |
string? |
Регистрационный IP-адрес из ячейки IP. |
LogEntry.Sender — это участник «I», а LogEntry.Target — участник «II», ровно так, как сайт помечает их
в ячейке данных. Каждый участник привязан к блоку дополнительной информации, следующему за его собственным
маркером, а не сопоставляется с блоками по индексу, поэтому строка, где есть только получатель, никогда не
припишет его данные отправителю:
// Строка, в ячейке данных которой есть только маркер «II:».
Console.WriteLine(entry.Sender is null); // True
Console.WriteLine(entry.Target?.AdditionalInfo?.AccountId); // 999111Участник создаётся только тогда, когда строка содержит для его маркера суммы, блок дополнительной информации
или IP-адрес; иначе свойство остаётся null.
public sealed record LogAdditionalInfo(
long AccountId,
long VC,
long SubAccount1,
long SubAccount2,
long SubAccount3,
long SubAccount4,
long SubAccount5,
long SubAccount6,
long Deposit,
int AdminLevel);| Член | Тип | Описание |
|---|---|---|
AccountId |
long |
Идентификатор аккаунта (ID аккаунта). |
VC |
long |
Баланс VC$. |
SubAccount1 … SubAccount6
|
long |
Шесть дополнительных счетов (Доп. счёт №1 … №6), берутся со страницы как есть. Сайт часто выводит здесь -1; библиотека не приписывает этому значению никакого смысла. |
Deposit |
long |
Баланс депозита. |
AdminLevel |
int |
Уровень администрирования. |
Блок разбирается только при наличии всех десяти значений; неполный блок даёт null в
LogParticipant.AdditionalInfo.
public sealed record LogPageMetaInfo(int Start, int End, int Total);| Член | Тип | Описание |
|---|---|---|
Start |
int |
Номер первой строки на странице (нумерация с единицы). |
End |
int |
Номер последней строки на странице. |
Total |
int |
Общее число строк, подходящих под запрос, по всем страницам. |
Читается из строки Показано с … по … из …, которая присутствует даже тогда, когда самой таблицы нет: страница
без <tbody> всё равно возвращает мета-информацию.
if (page.MetaInfo is { } meta)
{
var pages = (int)Math.Ceiling(meta.Total / (double)(meta.End - meta.Start + 1));
Console.WriteLine($"{meta.Start}-{meta.End} of {meta.Total} ({pages} pages)");
}public sealed record LogsAccount(
string Nickname,
IReadOnlyList<LogsAccountBadge> Badges,
IReadOnlyList<LogsAccountServer> AvailableServers);| Член | Тип | Описание |
|---|---|---|
Nickname |
string |
Никнейм из панели навигации. Пустая строка, если разметки нет. |
Badges |
IReadOnlyList<LogsAccountBadge> |
Значки рядом с никнеймом, без повторов по имени. |
AvailableServers |
IReadOnlyList<LogsAccountServer> |
Серверы, предлагаемые селектором сервера. |
Вся модель равна null (а не пуста), если в разметке нет ни никнейма, ни значков, ни селектора серверов, — это
признак того, что HTML не является страницей авторизованного пользователя.
public sealed record LogsAccountBadge(string Name);| Член | Тип | Описание |
|---|---|---|
Name |
string |
Текст значка, например Admin или VIP. |
public sealed record LogsAccountServer(
int Id,
string Name,
string DisplayName,
bool IsSelected);| Член | Тип | Описание |
|---|---|---|
Id |
int |
Номер сервера, используемый как LogsQuery.ServerId. |
Name |
string |
Только имя сервера, без числового префикса (Phoenix). |
DisplayName |
string |
Подпись в том виде, в котором она отрисована, включая числовой префикс ([18] Phoenix). |
IsSelected |
bool |
true для сервера, выбранного в текущей сессии. |
Если подпись не соответствует форме [n] Имя, Name и DisplayName совпадают.
var account = await client.GetCurrentAccountAsync();
var current = account?.AvailableServers.FirstOrDefault(server => server.IsSelected);
Console.WriteLine($"{current?.DisplayName} → ServerId {current?.Id}"); // [18] Phoenix → ServerId 18
var page = await client.GetLogsAsync(new LogsQuery(ServerId: current!.Id));public sealed record LogsFilterCatalog(
IReadOnlyList<LogsFilterDefinition> Filters,
LogsAccount? Account = null);| Член | Тип | Описание |
|---|---|---|
Filters |
IReadOnlyList<LogsFilterDefinition> |
Все фильтры, предлагаемые селектором типа логов. |
Account |
LogsAccount? |
Аккаунт, прочитанный из разметки той же страницы. |
public sealed record LogsFilterDefinition(
string Code,
string Name,
IReadOnlyList<LogsFilterAdditionalParameter> AdditionalParameters);| Член | Тип | Описание |
|---|---|---|
Code |
string |
Код фильтра, отправляемый сервису, например kill или item_market. Именно он попадает в LogsQuery.Filters. |
Name |
string |
Человекочитаемая русская подпись, например Убийство. |
AdditionalParameters |
IReadOnlyList<LogsFilterAdditionalParameter> |
Дополнительные поля, которые сайт показывает для этого фильтра. Пусто, если их нет. |
public sealed record LogsFilterAdditionalParameter(
string QueryKey,
string Label);| Член | Тип | Описание |
|---|---|---|
QueryKey |
string |
Ключ поля в строке запроса, всегда вида dynamic[n], например dynamic[0]. |
Label |
string |
Подпись поля, например Оружие. |
QueryKey — это ровно тот ключ, который принимает LogsQuery.AdditionalParameters: построитель URI отвергает
ключи, не соответствующие ^dynamic\[\d+\]$:
var catalog = await client.GetLogsFilterCatalogAsync();
var kill = catalog.Filters.Single(filter => filter.Code == "kill");
var weapon = kill.AdditionalParameters.First(); // QueryKey "dynamic[0]", Label "Оружие"
var page = await client.GetLogsAsync(new LogsQuery(
ServerId: 18,
Filters: [kill.Code],
AdditionalParameters: new Dictionary<string, string> { [weapon.QueryKey] = "Deagle" }));public sealed record AdminActivityReport(
(DateTime From, DateTime To) Period,
IReadOnlyList<AdminActivityEntry> Entries,
AdminActivityMetaInfo MetaInfo);| Член | Тип | Описание |
|---|---|---|
Period |
(DateTime From, DateTime To) |
Период, который сервис фактически отдал; читается обратно из полей формы. |
Entries |
IReadOnlyList<AdminActivityEntry> |
По одной записи на администратора. |
MetaInfo |
AdminActivityMetaInfo |
Сводные счётчики, никогда не null. |
Period — это именованный кортеж, а не запись: обращайтесь к нему как report.Period.From /
report.Period.To либо деконструируйте:
var report = await client.GetAdminActivityAsync(
new AdminActivityQuery(DateTime.Today.AddDays(-7), DateTime.Today));
var (from, to) = report.Period;
Console.WriteLine($"{from:yyyy-MM-dd} — {to:yyyy-MM-dd}");public sealed record AdminActivityEntry(
string Nickname,
int Id,
int AdminLevel,
int TotalReports,
int TotalMutes,
int TotalJails,
int TotalWarns,
int TotalBans,
int TotalThanks,
TimeSpan TotalOnline,
IReadOnlyList<AdminActivityDay> Details);| Член | Тип | Описание |
|---|---|---|
Nickname |
string |
Никнейм администратора. |
Id |
int |
Идентификатор администратора. |
AdminLevel |
int |
Уровень администрирования. |
TotalReports |
int |
Обработано репортов за период. |
TotalMutes |
int |
Выдано мутов за период. |
TotalJails |
int |
Выдано джейлов за период. |
TotalWarns |
int |
Выдано варнов за период. |
TotalBans |
int |
Выдано банов за период. |
TotalThanks |
int |
Получено благодарностей за период. |
TotalOnline |
TimeSpan |
Суммарное время онлайн. |
Details |
IReadOnlyList<AdminActivityDay> |
Разбивка по дням. Пусто, если у строки нет раскрываемого блока. |
TotalOnline не читается из отдельной ячейки: это сумма значений Online по дням из Details. Когда Details
пуст, TotalOnline равен TimeSpan.Zero, даже если администратор был онлайн.
foreach (var admin in report.Entries.OrderByDescending(entry => entry.TotalOnline))
{
Console.WriteLine($"{admin.Nickname} [{admin.AdminLevel}] {admin.TotalOnline:hh\\:mm\\:ss} " +
$"over {admin.Details.Count} days, {admin.TotalBans} bans");
}public sealed record AdminActivityDay(
DateTime Date,
TimeSpan Online,
int Reports,
int Mutes,
int Jails,
int Warns,
int Bans);| Член | Тип | Описание |
|---|---|---|
Date |
DateTime |
День, разобранный из формата yyyy-MM-dd. |
Online |
TimeSpan |
Время онлайн за этот день (hh:mm:ss). |
Reports |
int |
Обработано репортов за этот день. |
Mutes |
int |
Выдано мутов за этот день. |
Jails |
int |
Выдано джейлов за этот день. |
Warns |
int |
Выдано варнов за этот день. |
Bans |
int |
Выдано банов за этот день. |
public sealed record AdminActivityMetaInfo(int AdminCount, int PeriodDays, int TotalReports, int TotalBans);| Член | Тип | Описание |
|---|---|---|
AdminCount |
int |
Количество разобранных записей. |
PeriodDays |
int |
Полных дней между Period.From и Period.To, никогда не отрицательное. |
TotalReports |
int |
Сумма TotalReports по всем записям. |
TotalBans |
int |
Сумма TotalBans по всем записям. |
Все счётчики вычисляются по разобранным записям, а не читаются со страницы.
public sealed record TopOperationsReport(
DateOnly Date,
IReadOnlyList<TopOperationsEntry> Entries,
TopOperationsMetaInfo MetaInfo);| Член | Тип | Описание |
|---|---|---|
Date |
DateOnly |
День, за который построен отчёт; читается из строки Данные за:. |
Entries |
IReadOnlyList<TopOperationsEntry> |
По одной записи на игрока. |
MetaInfo |
TopOperationsMetaInfo |
Сводные счётчики, никогда не null. |
Date имеет тип DateOnly: отчёт строится по дням и не содержит времени — в отличие от
TopOperationsQuery.Date, который объявлен как DateTime?.
public sealed record TopOperationsEntry(
string Nickname,
int Id,
IPAddress Ip,
IPAddress RegistrationIp,
int TotalTransactions,
ulong Sum);| Член | Тип | Описание |
|---|---|---|
Nickname |
string |
Никнейм игрока. |
Id |
int |
Идентификатор игрока. |
Ip |
IPAddress |
Последний IP-адрес. |
RegistrationIp |
IPAddress |
Регистрационный IP-адрес. |
TotalTransactions |
int |
Количество транзакций за день. |
Sum |
ulong |
Суммарный оборот игрока. |
Оба IP-члена имеют тип System.Net.IPAddress (пространство имён System.Net), а не string, — обратите
внимание на асимметрию с LogParticipant.LastIp / RegistrationIp, которые объявлены как string?.
Неразбираемый адрес заменяется на IPAddress.Loopback с записью предупреждения в лог.
var top = await client.GetTopOperationsAsync(new TopOperationsQuery(Date: DateTime.Today));
foreach (var entry in top.Entries.Where(e => e.Ip.Equals(e.RegistrationIp)))
{
Console.WriteLine($"{entry.Nickname} ({entry.Id}): {entry.Sum:N0} in {entry.TotalTransactions} transactions");
}public sealed record TopOperationsMetaInfo(int PlayerCount, int TotalTransactions, ulong TotalSum);| Член | Тип | Описание |
|---|---|---|
PlayerCount |
int |
Количество разобранных записей. |
TotalTransactions |
int |
Сумма TotalTransactions по всем записям. |
TotalSum |
ulong |
Сумма Sum по всем записям, тоже ulong. |
public sealed record ParserRequest(string RelativeUri, ICookieStorage? CookieStorage = null);Пространство имён LogsParser.
| Член | Тип | Описание |
|---|---|---|
RelativeUri |
string |
Путь и строка запроса относительно LogsParserHttpOptions.BaseUri. |
CookieStorage |
ICookieStorage? |
Хранилище, переопределяющее хранилище источника данных для этого одного запроса. При null используется хранилище по умолчанию. |
using LogsParser;
using LogsParser.Abstractions;
ILogsParserDataSource dataSource = /* … */;
var html = await dataSource.GetContentAsync(new ParserRequest("/?server_number=18&limit=100"));public sealed record ParserCookie(string Name, string Value);Пространство имён LogsParser.
| Член | Тип | Описание |
|---|---|---|
Name |
string |
Имя cookie; сравнивается через StringComparer.Ordinal. |
Value |
string |
Значение cookie. |
Cookie — это пара «имя/значение» и ничего больше: все атрибуты Set-Cookie (путь, срок жизни, флаги)
намеренно отбрасываются. Сессионные cookie (arizonarp_session, XSRF-TOKEN) и токен анти-DDoS R3ACTLB
хранятся в одном плоском хранилище.
using LogsParser;
using LogsParser.Abstractions;
var storage = new MemoryCookieStorage();
storage.SetCookies([new ParserCookie("arizonarp_session", "restored-value")]);public sealed record LogsParserCredentials(string Login, string Password, string TotpSecret);| Член | Тип | Описание |
|---|---|---|
Login |
string |
Логин на сайте. |
Password |
string |
Пароль от сайта. |
TotpSecret |
string |
Base32-секрет TOTP, по которому генерируются коды RFC 6238 для подтверждения двухфакторной аутентификации. |
Для источника данных учётные данные необязательны: без них он запрашивает страницы с теми cookie, что у него есть, и завершается исключением аутентификации, когда сервис требует вход. Пароли и секреты никогда не попадают в логи.
public sealed record LogsParserHttpOptions| Свойство | Тип | По умолчанию | Описание |
|---|---|---|---|
BaseUri |
Uri |
new("https://arizonarp.logsparser.info/") |
Базовый адрес всех запросов. |
UserAgent |
string |
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36" |
Заголовок User-Agent. |
Accept |
string |
"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7" |
Заголовок Accept. |
MaxRetryAttempts |
int |
5 |
Верхняя граница числа повторов при временных сбоях. Решение анти-DDoS-задачи и ожидание известного момента сброса лимита попытку не расходуют. |
WaitForRateLimitReset |
bool |
true |
При 429 дождаться момента сброса и повторить запрос; при false вместо этого выбрасывается RateLimitExceededException. |
Все свойства объявлены как init-свойства, поэтому изменённая копия создаётся через with:
using LogsParser.Models;
using LogsParser.Net;
var options = new LogsParserHttpOptions() with
{
MaxRetryAttempts = 2,
WaitForRateLimitReset = false
};
using var dataSource = new LogsParserHttpDataSource(options: options);BaseUri, UserAgent и Accept применяются только к тому HttpClient, который их ещё не задал: внедрённый
клиент сохраняет собственные BaseAddress и заголовки.
public sealed class LogsParserRegistrationOptionsЭтот тип — запечатанный класс с изменяемыми свойствами, а не запись: он передаётся в делегат
Action<LogsParserRegistrationOptions> метода AddLogsParser и заполняется в нём.
| Свойство | Тип | По умолчанию | Описание |
|---|---|---|---|
Credentials |
LogsParserCredentials? |
null |
Учётные данные, регистрируемые как singleton, если заданы. |
HttpOptions |
LogsParserHttpOptions |
new() |
Настройки транспорта, регистрируемые как singleton. |
CookieStorageFactory |
Func<IServiceProvider, ICookieStorage>? |
null |
Заменяет регистрацию MemoryCookieStorage как singleton по умолчанию. |
DataSourceFactory |
Func<IServiceProvider, ILogsParserDataSource>? |
null |
Заменяет регистрацию LogsParserHttpDataSource как transient по умолчанию. |
using LogsParser.DependencyInjection;
using LogsParser.Models;
services.AddLogsParser(options =>
{
options.Credentials = new LogsParserCredentials("my_login", "my_password", "BASE32SECRET");
options.HttpOptions = new LogsParserHttpOptions { MaxRetryAttempts = 3 };
});using LogsParser;
using LogsParser.Models;
using LogsParser.Net;
using var dataSource = new LogsParserHttpDataSource(
credentials: new LogsParserCredentials("my_login", "my_password", "BASE32SECRET"));
var client = new LogsParserClient(dataSource);
var page = await client.GetLogsAsync(new LogsQuery(
ServerId: 18,
Filters: ["item_market"],
PeriodFrom: DateTime.Today.AddDays(-1),
PeriodTo: DateTime.Today,
Limit: 100));
if (page.MetaInfo is { } meta)
{
Console.WriteLine($"rows {meta.Start}–{meta.End} of {meta.Total}");
}
foreach (var entry in page.Entries)
{
// Text — это то, что показывает страница; скрытых блоков в нём нет.
Console.WriteLine($"{entry.Timestamp:yyyy-MM-dd HH:mm:ss} {entry.Text}");
// «I» — отправитель.
if (entry.Sender is { } sender)
{
Console.WriteLine($" I : money={sender.Money} bank={sender.Bank} donate={sender.Donate}");
Console.WriteLine($" ip={sender.LastIp} reg={sender.RegistrationIp}");
if (sender.AdditionalInfo is { } info)
{
Console.WriteLine($" account={info.AccountId} deposit={info.Deposit} adminLevel={info.AdminLevel}");
}
}
// «II» — получатель. Присутствует сам по себе, даже если отправителя нет.
if (entry.Target is { } target)
{
Console.WriteLine($" II: money={target.Money} account={target.AdditionalInfo?.AccountId}");
}
// Всё, что прячет кнопка-глаз, — с подписью и построчной структурой.
foreach (var revealed in entry.RevealedValues)
{
Console.WriteLine($" [{revealed.Label}]");
foreach (var line in revealed.Text.Split('\n'))
{
if (!string.IsNullOrWhiteSpace(line))
{
Console.WriteLine($" {line.Trim()}");
}
}
}
}- LogsParserClient — методы, возвращающие эти модели
-
Запросы и построитель URI —
LogsQuery,AdminActivityQuery,TopOperationsQuery - Парсеры — построение тех же моделей из HTML без HTTP
-
Хранилище cookie —
ParserCookieиICookieStorageна практике -
Транспорт и аутентификация —
ParserRequestиLogsParserHttpOptionsв работе -
Внедрение зависимостей —
LogsParserRegistrationOptionsв контейнере -
Исключения —
HtmlParsingExceptionи остальная иерархия
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
- Внедрение зависимостей
- Логирование
- Исключения