141 lines
5.5 KiB
C#
141 lines
5.5 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Net.Http;
|
|
using System.Text.Json;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using FinlyticCore.Dtos.News;
|
|
using FinlyticCore.Services;
|
|
using FinlyticCore.Util;
|
|
using FinlyticNews.Util;
|
|
using Microsoft.Extensions.Configuration;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
|
|
namespace FinlyticNews.Services;
|
|
|
|
/// <summary>
|
|
/// Defines integration operations with the external n8n AI workflow webhook.
|
|
/// </summary>
|
|
public interface IN8nService
|
|
{
|
|
/// <summary>
|
|
/// Submits raw article content and pre-filtered assets to n8n, returning the parsed response metadata.
|
|
/// </summary>
|
|
Task<N8nResponsePayload?> AnalyzeArticleAsync(string content, List<FilteredAssetPayload> filteredAssets, CancellationToken ct = default);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public class N8nService : IN8nService
|
|
{
|
|
private readonly HttpClient _httpClient;
|
|
private readonly IServiceScopeFactory _scopeFactory;
|
|
private readonly IConfiguration _configuration;
|
|
private readonly IFinlyticLogger<N8nService> _finlyticLogger;
|
|
|
|
public N8nService(
|
|
HttpClient httpClient,
|
|
IServiceScopeFactory scopeFactory,
|
|
IConfiguration configuration,
|
|
IFinlyticLogger<N8nService> finlyticLogger)
|
|
{
|
|
_httpClient = httpClient;
|
|
_scopeFactory = scopeFactory;
|
|
_configuration = configuration;
|
|
_finlyticLogger = finlyticLogger;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public async Task<N8nResponsePayload?> AnalyzeArticleAsync(string content, List<FilteredAssetPayload> filteredAssets, CancellationToken ct = default)
|
|
{
|
|
string? targetUrl = null;
|
|
|
|
using (var scope = _scopeFactory.CreateScope())
|
|
{
|
|
var settingsService = scope.ServiceProvider.GetService<ISettingsService>();
|
|
if (settingsService != null)
|
|
{
|
|
var dynamicUrl = await settingsService.GetSettingAsync(SettingKeys.N8nArticleExtractionUrl, ct);
|
|
if (!string.IsNullOrWhiteSpace(dynamicUrl))
|
|
{
|
|
targetUrl = dynamicUrl.Trim();
|
|
}
|
|
}
|
|
|
|
if (string.IsNullOrWhiteSpace(targetUrl))
|
|
{
|
|
var settingsDb = scope.ServiceProvider.GetService<ISettingsDbService>();
|
|
if (settingsDb != null)
|
|
{
|
|
var settings = await settingsDb.GetSettingsAsync();
|
|
targetUrl = settings?.N8nWebhookUrl;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (string.IsNullOrWhiteSpace(targetUrl))
|
|
{
|
|
targetUrl = _configuration["N8N:ArticleExtractionUrl"]
|
|
?? _configuration["N8N__ArticleExtractionUrl"];
|
|
}
|
|
|
|
if (string.IsNullOrWhiteSpace(targetUrl))
|
|
{
|
|
await _finlyticLogger.LogErrorAsync(SettingKeys.NewsChannel, "[N8nService] N8nWebhookUrl is not configured in DB or application settings.");
|
|
return null;
|
|
}
|
|
|
|
await _finlyticLogger.LogInfoAsync(SettingKeys.NewsChannel, "[N8nService] Posting article to n8n webhook pipeline at: {Url}", targetUrl);
|
|
|
|
var payload = new N8nRequestPayload(content, filteredAssets ?? []);
|
|
|
|
try
|
|
{
|
|
var jsonContent = JsonSerializer.Serialize(payload, FinlyticJsonSerializerContext.Default.N8nRequestPayload);
|
|
using var requestContent = new StringContent(jsonContent, System.Text.Encoding.UTF8, "application/json");
|
|
|
|
using var response = await _httpClient.PostAsync(targetUrl, requestContent, ct);
|
|
|
|
if (!response.IsSuccessStatusCode)
|
|
{
|
|
var errorMsg = await response.Content.ReadAsStringAsync(ct);
|
|
await _finlyticLogger.LogErrorAsync(SettingKeys.NewsChannel, "[N8nService] n8n webhook returned status code {StatusCode}. Error payload: {Error}", response.StatusCode, errorMsg);
|
|
return null;
|
|
}
|
|
|
|
using var responseStream = await response.Content.ReadAsStreamAsync(ct);
|
|
using var doc = await JsonDocument.ParseAsync(responseStream, cancellationToken: ct);
|
|
|
|
var root = doc.RootElement;
|
|
|
|
if (root.ValueKind == JsonValueKind.Array)
|
|
{
|
|
if (root.GetArrayLength() == 0)
|
|
{
|
|
await _finlyticLogger.LogWarningAsync(SettingKeys.NewsChannel, "[N8nService] n8n webhook returned an empty array.");
|
|
return null;
|
|
}
|
|
root = root[0];
|
|
}
|
|
|
|
if (root.ValueKind == JsonValueKind.Object)
|
|
{
|
|
if (root.TryGetProperty("json", out var jsonChild) && jsonChild.ValueKind == JsonValueKind.Object)
|
|
root = jsonChild;
|
|
else if (root.TryGetProperty("output", out var outChild) && outChild.ValueKind == JsonValueKind.Object)
|
|
root = outChild;
|
|
else if (root.TryGetProperty("data", out var dataChild) && dataChild.ValueKind == JsonValueKind.Object)
|
|
root = dataChild;
|
|
else if (root.TryGetProperty("body", out var bodyChild) && bodyChild.ValueKind == JsonValueKind.Object)
|
|
root = bodyChild;
|
|
}
|
|
|
|
var result = root.Deserialize(FinlyticJsonSerializerContext.Default.N8nResponsePayload);
|
|
return result;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
await _finlyticLogger.LogErrorAsync(SettingKeys.NewsChannel, ex, "[N8nService] Failed to communicate with or parse response from n8n webhook workflow.");
|
|
return null;
|
|
}
|
|
}
|
|
} |