Почистить комментарии от упоминаний процесса
Удалены <remarks>, <summary> сжаты до короткой фразы, вырезаны ссылки на Task/Ruling/этап/python/прототип; //-комментарии со ссылками на процесс удалены; то же в .proto. Правила обновлены в docs/spec/Код-стайл-Дейл.md. Строк комментариев 27210 -> ~19100.
This commit is contained in:
@@ -9,25 +9,10 @@ using RpcException = TL.RpcException;
|
||||
namespace Deal.Telegram.Telegram;
|
||||
|
||||
#pragma warning disable CS0618 // Auth_SendCode/Auth_SignIn используются осознанно: ручной веб-вход 1:1 с прототипом
|
||||
// (start_phone/submit_code/submit_password L134–176); Obsolete-метки библиотеки ведут на
|
||||
// LoginUserIfNeeded, который умеет только интерактивный конфиг-ввод, а не наш пошаговый API.
|
||||
|
||||
/// <summary>
|
||||
/// Реальная реализация <see cref="ISessionClient"/> поверх WTelegramClient (план Task 9; Ruling 3).
|
||||
///
|
||||
/// Сессия библиотеки живёт в памяти процесса: байты сессии (внутренне зашифрованы WTelegramClient
|
||||
/// ключом api_hash) подаются в конструктор и обновляются колбэком при каждом сохранении библиотекой;
|
||||
/// at-rest файл data/sessions/<tenant>.session (AES-GCM-обёртка, Ruling 3) пишет SessionStore —
|
||||
/// расшифрованного файла на диске нет ни в какой момент (Ruling 3: только в памяти процесса).
|
||||
/// Шаги входа повторяют python-прототип на уровне TL-методов:
|
||||
/// Auth_SendCode → (код) Auth_SignIn → 2FA: Account_GetPassword + Auth_CheckPassword; QR —
|
||||
/// LoginWithQRCode с колбэком новых URL. Ошибки переводятся в <see cref="Sessions.SessionException"/>.
|
||||
/// Операции каталога (план Task 10) ходят TL-методами messages.getDialogs/getHistory и readHistory
|
||||
/// (ReadHistory клиента — generic-хелпер channels/messages); discovery (план Task 11) — contacts.search,
|
||||
/// getFullChannel/getFullChat (участники/forum), getForumTopics+getReplies (чтение форумов по темам) и
|
||||
/// channels.joinChannel/leaveChannel; realtime-сообщения нормализует штатный
|
||||
/// <see cref="UpdateManager"/> библиотеки (UpdateNewMessage для всех типов, включая каналы и короткие
|
||||
/// UpdateShort*) и поднимаются событием <see cref="MessageReceived"/>.
|
||||
/// Реальная реализация <see cref="ISessionClient"/> поверх WTelegramClient.
|
||||
/// </summary>
|
||||
public sealed class WTelegramSessionClient : ISessionClient
|
||||
{
|
||||
@@ -52,10 +37,8 @@ public sealed class WTelegramSessionClient : ISessionClient
|
||||
// LRU-кэш access_hash сущностей (ключ — подписанный id диалога; заполняется из ответов).
|
||||
private readonly LruCache<string, long> _entityAccessHashes = new(AccessHashCacheCapacity);
|
||||
|
||||
// LRU-кэш сущностей чатов/каналов (ключ — raw id; для имён и forum-флага discovery, Task 11).
|
||||
private readonly LruCache<long, ChatBase> _chatsById = new(ChatEntityCacheCapacity);
|
||||
|
||||
// LRU-кэш сущностей пользователей (ключ — raw id; для имён discovery, Task 11).
|
||||
private readonly LruCache<long, User> _usersById = new(UserEntityCacheCapacity);
|
||||
|
||||
// Защита кэшей сущностей (обновляются из потоков reactor/вызовов).
|
||||
@@ -234,7 +217,6 @@ public sealed class WTelegramSessionClient : ISessionClient
|
||||
return _client.DisposeAsync();
|
||||
}
|
||||
|
||||
// --- Каталог и сообщения (план Task 10; Ruling 3/7; TL-методы getDialogs/getHistory/readHistory) ---
|
||||
|
||||
/// <inheritdoc />
|
||||
public event Func<TelegramMessage, Task>? MessageReceived;
|
||||
@@ -288,11 +270,9 @@ public sealed class WTelegramSessionClient : ISessionClient
|
||||
{
|
||||
InputPeer peer = await ResolvePeerAsync(dialogId, cancellationToken).ConfigureAwait(false);
|
||||
// Generic-хелпер библиотеки: для канала — channels.readHistory, иначе — messages.readHistory;
|
||||
// max_id=0 (default) — «снять новое» по всему диалогу (1:1 send_read_acknowledge прототипа).
|
||||
await RunTlCallAsync(() => _client.ReadHistory(peer), cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
// --- Discovery (план Task 11; discovery_search/info/read/join/leave L622–873; Ruling 3/7) ---
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<TelegramDialog>> SearchAsync(
|
||||
@@ -321,9 +301,7 @@ public sealed class WTelegramSessionClient : ISessionClient
|
||||
/// <inheritdoc />
|
||||
public async Task<TelegramSourceInfo> GetInfoAsync(string dialogId, CancellationToken cancellationToken)
|
||||
{
|
||||
// discovery_info L666–716: определение никогда не бросает наружу — недоступная сущность/полный
|
||||
// чат дают инфо по умолчанию (name=id, kind пуст, participants пуст), сбой участников не роняет
|
||||
// остальные поля (прототип: исключение только логируется).
|
||||
TelegramSourceInfo unknown = DefaultSourceInfo(dialogId);
|
||||
if (!TryParseSignedId(dialogId, out bool isChannel, out bool isChat, out bool isUser, out long rawId))
|
||||
{
|
||||
@@ -361,7 +339,6 @@ public sealed class WTelegramSessionClient : ISessionClient
|
||||
int limit,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
// discovery_read L718–760: limit ≤ 0 — пустой ok без сетевых вызовов (L730–732).
|
||||
if (limit <= 0)
|
||||
{
|
||||
return DiscoveryReadResult.Empty;
|
||||
@@ -379,7 +356,6 @@ public sealed class WTelegramSessionClient : ISessionClient
|
||||
}
|
||||
catch (SessionException)
|
||||
{
|
||||
// Сущность не разрешилась (приватный/закрытый источник без членства) → no_history (L736–739).
|
||||
return DiscoveryReadResult.NoHistory();
|
||||
}
|
||||
|
||||
@@ -388,7 +364,6 @@ public sealed class WTelegramSessionClient : ISessionClient
|
||||
IReadOnlyList<DiscoveryMessage> forumMessages = await ReadForumTopicsAsync(peer, limit, cancellationToken).ConfigureAwait(false);
|
||||
if (forumMessages.Count > 0)
|
||||
{
|
||||
// Форум прочитан по темам (L746–747: непустой результат тем — ответ, без ленты).
|
||||
return new DiscoveryReadResult(true, null, forumMessages);
|
||||
}
|
||||
}
|
||||
@@ -403,7 +378,6 @@ public sealed class WTelegramSessionClient : ISessionClient
|
||||
}
|
||||
catch (SessionException)
|
||||
{
|
||||
// История недоступна (приватный/закрытый) → ok=false no_history (L751–753), не ошибка RPC.
|
||||
return DiscoveryReadResult.NoHistory();
|
||||
}
|
||||
}
|
||||
@@ -411,7 +385,6 @@ public sealed class WTelegramSessionClient : ISessionClient
|
||||
/// <inheritdoc />
|
||||
public async Task JoinAsync(string username, CancellationToken cancellationToken)
|
||||
{
|
||||
// discovery_join L818–839: username → сущность → channels.JoinChannel (для мегагрупп/каналов).
|
||||
Contacts_ResolvedPeer resolved = await RunTlCallAsync(() => _client.Contacts_ResolveUsername(username), cancellationToken).ConfigureAwait(false);
|
||||
CacheEntities(resolved.chats.Values, resolved.users.Values);
|
||||
|
||||
@@ -427,7 +400,6 @@ public sealed class WTelegramSessionClient : ISessionClient
|
||||
/// <inheritdoc />
|
||||
public async Task LeaveAsync(string dialogId, CancellationToken cancellationToken)
|
||||
{
|
||||
// discovery_leave L841–848: channels.LeaveChannel по подписанному id (каналы/супергруппы).
|
||||
if (!TryParseSignedId(dialogId, out bool isChannel, out _, out _, out long rawId) || !isChannel)
|
||||
{
|
||||
throw new SessionException(StatusCode.InvalidArgument, SessionErrorMessages.InvalidDialogId);
|
||||
@@ -438,7 +410,6 @@ public sealed class WTelegramSessionClient : ISessionClient
|
||||
}
|
||||
|
||||
// Инфо о канале/супергруппе: entity из кэша/полного чата, участники — GetFullChannel best-effort
|
||||
// (1:1 L686–715: entity недоступен → default; участники недоступны → остальные поля остаются).
|
||||
// dialogId: Подписанный id (для имени по умолчанию).
|
||||
// rawId: Raw id канала.
|
||||
// unknown: Инфо по умолчанию (сущность недоступна).
|
||||
@@ -457,12 +428,10 @@ public sealed class WTelegramSessionClient : ISessionClient
|
||||
|
||||
if (cached is not null)
|
||||
{
|
||||
// Сущность известна (поиск/каталог): имя/kind/forum — сразу, участники — best-effort (L700–715).
|
||||
int? participants = await TryFetchChannelParticipantsAsync(dialogId, rawId, cancellationToken).ConfigureAwait(false);
|
||||
return DescribeChannel(dialogId, cached, participants);
|
||||
}
|
||||
|
||||
// Неизвестная сущность: полный чат принесёт её; недоступен → default (как entity-not-found L686–690).
|
||||
InputChannel input = await ResolveInputChannelAsync(dialogId, rawId, cancellationToken).ConfigureAwait(false);
|
||||
Messages_ChatFull full = await RunTlCallAsync(() => _client.Channels_GetFullChannel(input), cancellationToken).ConfigureAwait(false);
|
||||
CacheEntities(full.chats.Values, full.users.Values);
|
||||
@@ -477,7 +446,6 @@ public sealed class WTelegramSessionClient : ISessionClient
|
||||
return DescribeChannel(dialogId, channel, fullParticipants);
|
||||
}
|
||||
|
||||
// Участники канала best-effort: сбой полного чата → null, имя/kind не роняются (L713–715).
|
||||
// dialogId: Подписанный id.
|
||||
// rawId: Raw id канала.
|
||||
// cancellationToken: Отмена операции.
|
||||
@@ -537,7 +505,6 @@ public sealed class WTelegramSessionClient : ISessionClient
|
||||
return DescribeGroup(dialogId, group, fullParticipants);
|
||||
}
|
||||
|
||||
// Список участников базовой группы best-effort: сбой → null (нет членства/приватная, L713–715).
|
||||
// rawId: Raw id группы.
|
||||
// cancellationToken: Отмена операции.
|
||||
private async Task<int?> TryFetchBasicParticipantsAsync(long rawId, CancellationToken cancellationToken)
|
||||
@@ -575,7 +542,6 @@ public sealed class WTelegramSessionClient : ISessionClient
|
||||
// Инфо о личном чате/боте: имя из кэша сущности (полного чата у людей нет).
|
||||
// dialogId: Подписанный id.
|
||||
// rawId: Raw id пользователя.
|
||||
// unknown: Инфо по умолчанию (сущность неизвестна — как entity-not-found прототипа).
|
||||
private TelegramSourceInfo GetUserInfo(
|
||||
string dialogId,
|
||||
long rawId,
|
||||
@@ -602,7 +568,6 @@ public sealed class WTelegramSessionClient : ISessionClient
|
||||
return new TelegramSourceInfo(dialogId, name, username, DialogKinds.Chat, participants: null, isForum: false);
|
||||
}
|
||||
|
||||
// Выборка по активным темам форума: GetForumTopics + по каждой теме getReplies (L762–800).
|
||||
// peer: Peer форума.
|
||||
// limit: Размер выборки (раскладывается по темам).
|
||||
// cancellationToken: Отмена операции.
|
||||
@@ -615,7 +580,6 @@ public sealed class WTelegramSessionClient : ISessionClient
|
||||
var outMessages = new List<DiscoveryMessage>();
|
||||
try
|
||||
{
|
||||
// channels/messages.getForumTopics (L771–775): до 5 активных тем, без смещения.
|
||||
Messages_ForumTopics forum = await RunTlCallAsync(
|
||||
() => _client.Messages_GetForumTopics(peer, offset_date: default, offset_id: 0, offset_topic: 0, limit: ForumTopicsLimit),
|
||||
cancellationToken).ConfigureAwait(false);
|
||||
@@ -627,14 +591,12 @@ public sealed class WTelegramSessionClient : ISessionClient
|
||||
return outMessages;
|
||||
}
|
||||
|
||||
// На тему минимум 3 сообщения, cap 10 (прототип L784); суммарно выборка может слегка превысить limit.
|
||||
int perTopic = Math.Min(Math.Max(3, (int)Math.Ceiling(limit / (double)topics.Length)), ForumMessagesPerTopicCap);
|
||||
DateTimeOffset now = DateTimeOffset.UtcNow;
|
||||
foreach (ForumTopic topic in topics)
|
||||
{
|
||||
try
|
||||
{
|
||||
// get_messages(reply_to=topic.id) эквивалент: messages.getReplies (L792, Fix round 1).
|
||||
Messages_MessagesBase result = await RunTlCallAsync(
|
||||
() => _client.Messages_GetReplies(peer, topic.id, limit: perTopic),
|
||||
cancellationToken).ConfigureAwait(false);
|
||||
@@ -651,13 +613,11 @@ public sealed class WTelegramSessionClient : ISessionClient
|
||||
}
|
||||
catch (SessionException)
|
||||
{
|
||||
// Тема не прочиталась — пропуск (прототип L793–795: continue).
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (SessionException)
|
||||
{
|
||||
// getForumTopics недоступен — безопасный фолбэк на обычную ленту (L777–779).
|
||||
outMessages.Clear();
|
||||
}
|
||||
|
||||
@@ -703,7 +663,6 @@ public sealed class WTelegramSessionClient : ISessionClient
|
||||
participants,
|
||||
isForum: (channel.flags & Channel.Flags.forum) != 0);
|
||||
|
||||
// Отображаемое имя сущности (title/first_name) или id (как get_display_name прототипа).
|
||||
// dialogId: Подписанный id (фолбэк имени).
|
||||
// chat: Сущность чата/канала.
|
||||
private static string DisplayName(string dialogId, ChatBase chat)
|
||||
@@ -712,7 +671,6 @@ public sealed class WTelegramSessionClient : ISessionClient
|
||||
return title.Length > 0 ? title : dialogId;
|
||||
}
|
||||
|
||||
// True — канал из кэша сущностей является форумом (темы; entity.forum прототипа L698).
|
||||
// rawId: Raw id канала.
|
||||
private bool IsForumChannel(long rawId)
|
||||
{
|
||||
@@ -724,15 +682,11 @@ public sealed class WTelegramSessionClient : ISessionClient
|
||||
}
|
||||
}
|
||||
|
||||
// Сколько активных тем форума запрашивает чтение выборки (getForumTopics limit=5, L773).
|
||||
private const int ForumTopicsLimit = 5;
|
||||
|
||||
// Потолок сообщений на тему форума (cap 10, прототип L784).
|
||||
private const int ForumMessagesPerTopicCap = 10;
|
||||
|
||||
// --- Realtime-события (план Task 10; прототип _on_message L255–283) ---
|
||||
|
||||
// Единый колбэк штатного UpdateManager (см. _updateManager): вызывается
|
||||
// последовательно на каждое обновление в правильном порядке (без пропусков/дублей по pts).
|
||||
// Все типы новых сообщений библиотека нормализует в TL.UpdateNewMessage:
|
||||
// * UpdateNewChannelMessage (каналы/супергруппы) — подкласс UpdateNewMessage;
|
||||
@@ -810,7 +764,6 @@ public sealed class WTelegramSessionClient : ISessionClient
|
||||
private void OnSessionSaved(byte[] sessionBytes)
|
||||
=> Volatile.Write(ref _latestSessionBytes, sessionBytes);
|
||||
|
||||
// Запрашивает код с одним повтором при AUTH_RESTART (как LoginUserIfNeeded L1202–1205).
|
||||
private async Task<Auth_SentCodeBase> SendCodeOnceAsync(string phone, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
@@ -849,7 +802,6 @@ public sealed class WTelegramSessionClient : ISessionClient
|
||||
|
||||
// Переводит RpcException Telegram в SessionException: FloodWait — RESOURCE_EXHAUSTED
|
||||
// (detail с префиксом "flood", контракт telegram.proto); 400-ошибки входных данных —
|
||||
// INVALID_ARGUMENT (текст RPC как detail, как у прототипа: ошибка показывается как есть);
|
||||
// остальные серверные/сетевые сбои — UNAVAILABLE «Telegram недоступен…» (безопасный повтор).
|
||||
// exception: Исключение RPC Telegram.
|
||||
private static SessionException MapRpcException(RpcException exception)
|
||||
@@ -867,7 +819,6 @@ public sealed class WTelegramSessionClient : ISessionClient
|
||||
return new SessionException(StatusCode.Unavailable, SessionErrorMessages.TelegramUnavailable, exception);
|
||||
}
|
||||
|
||||
// --- Приватные помощники каталога/сообщений (план Task 10) ---
|
||||
|
||||
// Исполняет TL-вызов с единым переводом ошибок (RpcException Telegram → SessionException;
|
||||
// прочие сбои — UNAVAILABLE «Telegram недоступен…»).
|
||||
|
||||
Reference in New Issue
Block a user