using DailyDigestWorker.Configuration; // Не используется напрямую, но нужно для DI WTelegramClient using Microsoft.Extensions.Options; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using TL; // Основное пространство имен WTelegramClient using WTelegram; // Для Client namespace DailyDigestWorker.Services { public class TelegramChannelReader : ITelegramChannelReader // Реализуем обновленный интерфейс { private readonly ILogger _logger; private readonly Client _client; // Наш Singleton WTelegramClient // Зависимость от IOptions убрана // Кэшированное поле _targetPeer убрано // Конструктор теперь принимает только логгер и WTelegramClient public TelegramChannelReader( ILogger logger, Client client) { _logger = logger; _client = client; } // Метод теперь принимает channelUsername как параметр public async Task?> GetRecentNewsAsync( string channelUsername, // Имя канала для этого конкретного вызова int maxAgeHours, int limit, CancellationToken cancellationToken) { // Внешний try-catch для диагностики любых ошибок try { _logger.LogInformation("[GetRecentNewsAsync] Попытка чтения новостей из канала {ChannelUsername}...", channelUsername); if (cancellationToken.IsCancellationRequested) { _logger.LogWarning("[GetRecentNewsAsync] Операция отменена ПЕРЕД началом работы для канала {ChannelUsername}.", channelUsername); return null; } _logger.LogDebug("[GetRecentNewsAsync] Шаг 1: Проверка/выполнение авторизации пользователя..."); User? currentUser = null; try { // Выполняем логин, если необходимо (может запросить ввод в консоль) currentUser = await _client.LoginUserIfNeeded().ConfigureAwait(false); } catch (Exception loginEx) { _logger.LogError(loginEx, "[GetRecentNewsAsync] Ошибка ВО ВРЕМЯ выполнения LoginUserIfNeeded."); return null; } if (currentUser == null) { _logger.LogError("[GetRecentNewsAsync] Авторизация не удалась (LoginUserIfNeeded вернул null или произошла ошибка выше)."); return null; } _logger.LogInformation("[GetRecentNewsAsync] Шаг 1 Успех: Авторизация как {UserFirstName} (ID: {UserId})", currentUser.first_name, currentUser.id); // 2. Найти канал (получить InputPeer) - делаем это каждый раз _logger.LogDebug("[GetRecentNewsAsync] Шаг 2: Поиск InputPeer для канала {ChannelUsername}...", channelUsername); InputPeer? targetPeer = null; // Локальная переменная try { var resolved = await _client.Contacts_ResolveUsername(channelUsername).ConfigureAwait(false); if (resolved?.UserOrChat is Channel channel) { targetPeer = channel.ToInputPeer(); _logger.LogInformation("[GetRecentNewsAsync] Шаг 2 Успех: Найден InputPeer для канала '{ChannelTitle}' (ID: {ChannelId})", channel.title, channel.id); } else { _logger.LogError("[GetRecentNewsAsync] Шаг 2 Ошибка: Не удалось найти канал по юзернейму {ChannelUsername} или это не канал. Resolved: {@Resolved}", channelUsername, resolved); return null; } } catch (RpcException e) { _logger.LogError(e, "[GetRecentNewsAsync] Ошибка Telegram API ({ErrorCode}) при поиске канала {ChannelUsername}: {ErrorMessage}", e.Code, channelUsername, e.Message); return null; } catch (Exception ex) { _logger.LogError(ex, "[GetRecentNewsAsync] Неожиданная ошибка при поиске канала {ChannelUsername}.", channelUsername); return null; } // Если targetPeer все еще null (не должно произойти при успешном resolve) if (targetPeer == null) { _logger.LogError("[GetRecentNewsAsync] Критическая ошибка: InputPeer остался null после ResolveUsername для {ChannelUsername}", channelUsername); return null; } // 3. Получить историю сообщений _logger.LogDebug("[GetRecentNewsAsync] Шаг 3: Запрос истории сообщений (limit={Limit})...", limit); Messages_MessagesBase? history = null; // Используем nullable тип try { history = await _client.Messages_GetHistory(targetPeer, limit: limit).ConfigureAwait(false); } catch (RpcException e) { _logger.LogError(e, "[GetRecentNewsAsync] Ошибка Telegram API ({ErrorCode}) при получении истории для {ChannelUsername}: {ErrorMessage}", e.Code, channelUsername, e.Message); return null; } catch (Exception ex) { _logger.LogError(ex, "[GetRecentNewsAsync] Неожиданная ошибка при получении истории для {ChannelUsername}.", channelUsername); return null; } if (history == null) { _logger.LogWarning("[GetRecentNewsAsync] Шаг 3 Ошибка: Не удалось получить историю сообщений (результат null) для канала {ChannelUsername}.", channelUsername); return null; } _logger.LogInformation("[GetRecentNewsAsync] Шаг 3 Успех: Получено {Count} сообщений/элементов в истории для канала {ChannelUsername}.", history.Messages.Length, channelUsername); // 4. Отфильтровать сообщения по дате и извлечь текст _logger.LogDebug("[GetRecentNewsAsync] Шаг 4: Фильтрация сообщений новее {CutoffDateUtc} UTC...", DateTime.UtcNow.AddHours(-maxAgeHours)); var newsTexts = new List(); var cutoffDate = DateTime.UtcNow.AddHours(-maxAgeHours); foreach (var msgBase in history.Messages) { if (cancellationToken.IsCancellationRequested) { _logger.LogWarning("[GetRecentNewsAsync] Операция отменена во время фильтрации сообщений."); return null; } if (msgBase is Message message) { if (message.Date >= cutoffDate) { if (!string.IsNullOrWhiteSpace(message.message)) { newsTexts.Add(message.message); } } else { _logger.LogDebug("[GetRecentNewsAsync] Достигнуто сообщение старше {CutoffDateUtc} UTC, остановка фильтрации.", cutoffDate); break; // Оптимизация: сообщения идут от новых к старым } } } _logger.LogInformation("[GetRecentNewsAsync] Шаг 4 Успех: Найдено {Count} новостных сообщений за последние {Hours} часов из канала {ChannelUsername}.", newsTexts.Count, maxAgeHours, channelUsername); // Сообщения были от новых к старым, переворачиваем для хронологии newsTexts.Reverse(); return newsTexts; } catch (RpcException e) // Ловим ошибки Telegram API на уровне всего метода { _logger.LogError(e, "[GetRecentNewsAsync] Ошибка Telegram API ({ErrorCode}) во время работы с каналом {ChannelUsername}: {ErrorMessage}", e.Code, channelUsername, e.Message); if (e.Code == 420) // FLOOD_WAIT_X { _logger.LogWarning("[GetRecentNewsAsync] Получен FloodWait на {Seconds} секунд. Пропускаем чтение.", e.X); } return null; } catch (Exception ex) // Ловим ЛЮБЫЕ другие ошибки внутри метода { _logger.LogError(ex, "[GetRecentNewsAsync] Неожиданная ошибка ВНУТРИ метода для канала {ChannelUsername}.", channelUsername); return null; } } } }