93 lines
3.6 KiB
C#
93 lines
3.6 KiB
C#
using System.Text.RegularExpressions;
|
|
using System.Xml.Linq;
|
|
using FinlyticCore.Dtos.News;
|
|
|
|
namespace FinlyticNews.Adapters.Discovery;
|
|
|
|
/// <summary>
|
|
/// Specialized RSS discovery adapter for Finanznachrichten.de, capable of parsing custom fn:isin elements and standard RSS metadata.
|
|
/// </summary>
|
|
public partial class FinanznachrichtenRssDiscoveryAdapter : ArticleDiscoveryAdapter
|
|
{
|
|
private static readonly XNamespace FnNamespace = "http://www.finanznachrichten.de/service/rss";
|
|
|
|
/// <inheritdoc />
|
|
public override string Name => "finanznachrichten_rss";
|
|
|
|
/// <inheritdoc />
|
|
public override List<DiscoveredArticle> ExtractUrls(string content, string sourceUrl)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(content)) return [];
|
|
|
|
var articles = new List<DiscoveredArticle>();
|
|
if (!Uri.TryCreate(sourceUrl, UriKind.Absolute, out var baseUri)) return articles;
|
|
|
|
try
|
|
{
|
|
var doc = XDocument.Parse(content);
|
|
var channel = doc.Root?.Element("channel");
|
|
var channelTitle = channel?.Element("title")?.Value?.Trim();
|
|
var channelLanguage = channel?.Element("language")?.Value?.Trim();
|
|
|
|
var items = doc.Descendants("item");
|
|
|
|
foreach (var item in items)
|
|
{
|
|
var link = item.Element("link")?.Value?.Trim() ?? item.Element("guid")?.Value?.Trim();
|
|
if (string.IsNullOrWhiteSpace(link)) continue;
|
|
|
|
// ISIN Extraktion
|
|
var isins = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
|
foreach (var isinEl in item.Elements(FnNamespace + "isin"))
|
|
{
|
|
if (string.IsNullOrWhiteSpace(isinEl.Value)) continue;
|
|
|
|
var parts = isinEl.Value.Split(new[] { ',', ';', ' ' }, StringSplitOptions.RemoveEmptyEntries);
|
|
foreach (var part in parts)
|
|
{
|
|
var trimmed = part.Trim().ToUpperInvariant();
|
|
if (IsValidIsin(trimmed))
|
|
{
|
|
isins.Add(trimmed);
|
|
}
|
|
}
|
|
}
|
|
|
|
var title = item.Element("title")?.Value?.Trim();
|
|
var description = item.Element("description")?.Value?.Trim();
|
|
|
|
DateTime? pubDate = null;
|
|
var pubDateValue = item.Element("pubDate")?.Value;
|
|
if (!string.IsNullOrWhiteSpace(pubDateValue) && DateTime.TryParse(pubDateValue, out var parsedDate))
|
|
{
|
|
pubDate = parsedDate.ToUniversalTime();
|
|
}
|
|
|
|
if (Uri.TryCreate(baseUri, link, out var absoluteUri))
|
|
{
|
|
articles.Add(new DiscoveredArticle(
|
|
Url: absoluteUri.ToString(),
|
|
Isins: isins.Count > 0 ? isins.ToList() : null,
|
|
Title: title,
|
|
Summary: description,
|
|
PublishedAt: pubDate,
|
|
Language: channelLanguage,
|
|
SourceName: channelTitle
|
|
));
|
|
}
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
// XML Malformed / Parsing Errors ignorieren
|
|
}
|
|
|
|
// Deduplizierung nach URL
|
|
return articles.GroupBy(a => a.Url, StringComparer.OrdinalIgnoreCase)
|
|
.Select(g => g.First())
|
|
.ToList();
|
|
}
|
|
|
|
private static bool IsValidIsin(string input)
|
|
=> input.Length == 12 && char.IsLetter(input[0]) && char.IsLetter(input[1]);
|
|
} |