daily_digest/Services/TelegramChannelReader.cs
2025-04-12 10:20:46 +07:00

181 lines
10 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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<TelegramChannelReader> _logger;
private readonly Client _client; // Наш Singleton WTelegramClient
// Зависимость от IOptions<TelegramClientSettings> убрана
// Кэшированное поле _targetPeer убрано
// Конструктор теперь принимает только логгер и WTelegramClient
public TelegramChannelReader(
ILogger<TelegramChannelReader> logger,
Client client)
{
_logger = logger;
_client = client;
}
// Метод теперь принимает channelUsername как параметр
public async Task<List<string>?> 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<string>();
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;
}
}
}
}