using System; using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Threading; using System.Threading.Tasks; using FinlyticCore.Dtos.News; using FinlyticCore.Services; using FinlyticNews.Adapters.Discovery; using FinlyticNews.Util; namespace FinlyticNews.Services; public interface IArticleDiscoveryService { Task?> DiscoverLinksAsync(string url, string adapterType, CancellationToken ct = default); } /// public class ArticleDiscoveryService : IArticleDiscoveryService { private readonly IEnumerable _adapters; private readonly IFinlyticLogger _finlyticLogger; private readonly HttpClient _httpClient; public ArticleDiscoveryService( IEnumerable adapters, IFinlyticLogger finlyticLogger, HttpClient httpClient) { _adapters = adapters; _finlyticLogger = finlyticLogger; _httpClient = httpClient; } /// public async Task?> DiscoverLinksAsync(string url, string adapterType, CancellationToken ct = default) { if (!Uri.TryCreate(url, UriKind.Absolute, out var uri)) { await _finlyticLogger.LogWarningAsync(SettingKeys.NewsChannel, "[ArticleDiscoveryService] Invalid source URL passed for discovery: {Url}", url); return null; } try { await _finlyticLogger.LogDebugAsync(SettingKeys.NewsChannel, "[ArticleDiscoveryService] Fetching content from discovery source: {Url}", uri); var content = await _httpClient.GetStringAsync(uri, ct); if (string.IsNullOrWhiteSpace(content)) return []; var trimmedContent = content.TrimStart(); var adapter = _adapters.FirstOrDefault(a => a.Name.Equals(adapterType, StringComparison.OrdinalIgnoreCase) || uri.Host.Contains(a.Name, StringComparison.OrdinalIgnoreCase)); if (adapter != null) { await _finlyticLogger.LogInfoAsync(SettingKeys.NewsChannel, "[ArticleDiscoveryService] Using specialized adapter {AdapterName} for source: {Url}", adapter.Name, url); return adapter.ExtractUrls(content, url); } if (adapterType.Equals("rss", StringComparison.OrdinalIgnoreCase) || trimmedContent.StartsWith(" a.Name.Equals("rss", StringComparison.OrdinalIgnoreCase)) ?? new RssDiscoveryAdapter(); return rssAdapter.ExtractUrls(content, url); } await _finlyticLogger.LogWarningAsync(SettingKeys.NewsChannel, "[ArticleDiscoveryService] No suitable discovery adapter found for type '{Type}' and URL: {Url}", adapterType, url); return []; } catch (Exception ex) { await _finlyticLogger.LogErrorAsync(SettingKeys.NewsChannel, ex, "[ArticleDiscoveryService] Failed to run article discovery on URL: {Url}", url); return []; } } }