812 lines
33 KiB
C#
812 lines
33 KiB
C#
using System.Net.Http.Headers;
|
||
using System.Text;
|
||
using System.Text.Json;
|
||
using Microsoft.Extensions.Logging;
|
||
using ListmonkIntegration.Models;
|
||
using TemplateInfo = ListmonkIntegration.Models.TemplateInfo;
|
||
|
||
namespace ListmonkIntegration.Services
|
||
{
|
||
public class ListmonkService : IListmonkService
|
||
{
|
||
|
||
private readonly HttpClient _httpClient;
|
||
private readonly ListmonkSettings _settings;
|
||
private readonly ILogger<ListmonkService> _logger;
|
||
|
||
public ListmonkService(
|
||
HttpClient httpClient,
|
||
ListmonkSettings settings,
|
||
ILogger<ListmonkService> logger)
|
||
{
|
||
_httpClient = httpClient;
|
||
_settings = settings;
|
||
_logger = logger;
|
||
|
||
// Настройка базового адреса
|
||
_httpClient.BaseAddress = new Uri(_settings.BaseUrl);
|
||
|
||
// Listmonk использует Basic Auth с API credentials
|
||
// Username: логин пользователя (например, "admin")
|
||
// Password: API токен (d1qPRY5jl1LsD1LskI4zWu4ukYWTyNLxjot05CjgRin4nQy3)
|
||
var username = _settings.Username;
|
||
var password = !string.IsNullOrEmpty(_settings.ApiToken)
|
||
? _settings.ApiToken
|
||
: _settings.Password;
|
||
|
||
var authToken = Convert.ToBase64String(
|
||
Encoding.ASCII.GetBytes($"{username}:{password}"));
|
||
|
||
_httpClient.DefaultRequestHeaders.Authorization =
|
||
new AuthenticationHeaderValue("Basic", authToken);
|
||
|
||
_logger.LogInformation($"Using Basic Auth with username: {username}");
|
||
}
|
||
|
||
public async Task<bool> TestConnectionAsync()
|
||
{
|
||
try
|
||
{
|
||
_logger.LogInformation($"Testing connection to Listmonk...");
|
||
_logger.LogInformation($"Base URL: {_settings.BaseUrl}");
|
||
_logger.LogInformation($"Username: {_settings.Username}");
|
||
|
||
var response = await _httpClient.GetAsync("/api/config");
|
||
|
||
_logger.LogInformation($"Response status: {response.StatusCode}");
|
||
|
||
if (response.IsSuccessStatusCode)
|
||
{
|
||
var content = await response.Content.ReadAsStringAsync();
|
||
_logger.LogInformation($"Success! Connected to Listmonk");
|
||
return true;
|
||
}
|
||
else
|
||
{
|
||
var errorContent = await response.Content.ReadAsStringAsync();
|
||
_logger.LogWarning($"Failed! Status: {response.StatusCode}, Content: {errorContent}");
|
||
return false;
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogError(ex, $"Exception during connection test: {ex.Message}");
|
||
return false;
|
||
}
|
||
}
|
||
|
||
public async Task<int> CreateListAsync(string listName)
|
||
{
|
||
_logger.LogInformation($"Creating list: {listName}");
|
||
|
||
// Сначала проверим, нет ли уже списка с таким именем
|
||
var existingListId = await FindListIdByNameAsync(listName);
|
||
if (existingListId.HasValue)
|
||
{
|
||
_logger.LogInformation($"List '{listName}' already exists with ID {existingListId.Value}");
|
||
return existingListId.Value;
|
||
}
|
||
|
||
var content = new StringContent(
|
||
JsonSerializer.Serialize(new
|
||
{
|
||
name = listName,
|
||
type = "private",
|
||
optin = "single"
|
||
}),
|
||
Encoding.UTF8,
|
||
"application/json");
|
||
|
||
var response = await _httpClient.PostAsync("/api/lists", content);
|
||
var responseBody = await response.Content.ReadAsStringAsync();
|
||
|
||
_logger.LogInformation($"Create list response: {responseBody}");
|
||
|
||
response.EnsureSuccessStatusCode();
|
||
|
||
var json = JsonDocument.Parse(responseBody);
|
||
|
||
// Listmonk возвращает {"data": {"id": 1, ...}}
|
||
var listId = json.RootElement
|
||
.GetProperty("data")
|
||
.GetProperty("id")
|
||
.GetInt32();
|
||
|
||
_logger.LogInformation($"List created with ID: {listId}");
|
||
return listId;
|
||
}
|
||
|
||
private async Task<int?> FindListIdByNameAsync(string listName)
|
||
{
|
||
try
|
||
{
|
||
var response = await _httpClient.GetAsync("/api/lists");
|
||
if (response.IsSuccessStatusCode)
|
||
{
|
||
var responseBody = await response.Content.ReadAsStringAsync();
|
||
_logger.LogInformation($"Lists response: {responseBody.Substring(0, Math.Min(200, responseBody.Length))}...");
|
||
|
||
var json = JsonDocument.Parse(responseBody);
|
||
|
||
// Структура: {"data": {"results": [...]}}
|
||
if (json.RootElement.TryGetProperty("data", out var data))
|
||
{
|
||
JsonElement results;
|
||
if (data.ValueKind == JsonValueKind.Object &&
|
||
data.TryGetProperty("results", out results))
|
||
{
|
||
if (results.ValueKind == JsonValueKind.Array)
|
||
{
|
||
foreach (var list in results.EnumerateArray())
|
||
{
|
||
var name = list.GetProperty("name").GetString();
|
||
if (name == listName)
|
||
{
|
||
var id = list.GetProperty("id").GetInt32();
|
||
_logger.LogInformation($"Found existing list '{listName}' with ID {id}");
|
||
return id;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
else if (data.ValueKind == JsonValueKind.Array)
|
||
{
|
||
foreach (var list in data.EnumerateArray())
|
||
{
|
||
var name = list.GetProperty("name").GetString();
|
||
if (name == listName)
|
||
{
|
||
var id = list.GetProperty("id").GetInt32();
|
||
return id;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogWarning(ex, $"Error searching for list '{listName}'");
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
public async Task<bool> ImportSubscribersAsync(List<int> listIds, List<Subscriber> subscribers)
|
||
{
|
||
_logger.LogInformation($"Importing {subscribers.Count} subscribers to lists: {string.Join(", ", listIds)}");
|
||
|
||
int successCount = 0;
|
||
int failCount = 0;
|
||
int updatedCount = 0;
|
||
int createdCount = 0;
|
||
|
||
foreach (var sub in subscribers)
|
||
{
|
||
try
|
||
{
|
||
// Проверяем, существует ли подписчик
|
||
var existingSubscriber = await GetSubscriberByEmailAsync(sub.Email);
|
||
|
||
if (existingSubscriber != null)
|
||
{
|
||
// Подписчик существует - обновляем его
|
||
_logger.LogInformation($"Subscriber {sub.Email} exists, updating...");
|
||
|
||
// Получаем ID существующего подписчика
|
||
var subscriberId = await GetSubscriberIdByEmailAsync(sub.Email);
|
||
if (subscriberId.HasValue)
|
||
{
|
||
// Получаем текущие списки подписчика
|
||
var currentListIds = await GetSubscriberListIdsAsync(subscriberId.Value);
|
||
|
||
// Добавляем новые списки к существующим
|
||
var allListIds = new List<int>(currentListIds);
|
||
foreach (var listId in listIds)
|
||
{
|
||
if (!allListIds.Contains(listId))
|
||
{
|
||
allListIds.Add(listId);
|
||
}
|
||
}
|
||
|
||
// Объединяем атрибуты
|
||
var mergedAttributes = new Dictionary<string, string>(existingSubscriber.Attributes);
|
||
foreach (var attr in sub.Attributes)
|
||
{
|
||
mergedAttributes[attr.Key] = attr.Value;
|
||
}
|
||
|
||
// Обновляем подписчика
|
||
var updated = await UpdateSubscriberAsync(
|
||
subscriberId.Value,
|
||
sub.Email,
|
||
sub.FullName,
|
||
mergedAttributes,
|
||
allListIds
|
||
);
|
||
|
||
if (updated)
|
||
{
|
||
updatedCount++;
|
||
successCount++;
|
||
}
|
||
else
|
||
{
|
||
failCount++;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
failCount++;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// Создаём нового подписчика
|
||
await AddSubscriberToList(listIds, sub);
|
||
createdCount++;
|
||
successCount++;
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
failCount++;
|
||
_logger.LogWarning(ex, $"Failed to process subscriber {sub.Email}");
|
||
}
|
||
}
|
||
|
||
_logger.LogInformation($"Import completed: {successCount} success ({createdCount} created, {updatedCount} updated), {failCount} failed");
|
||
return true;
|
||
}
|
||
|
||
private async Task<List<int>> GetSubscriberListIdsAsync(int subscriberId)
|
||
{
|
||
try
|
||
{
|
||
var response = await _httpClient.GetAsync($"/api/subscribers/{subscriberId}");
|
||
|
||
if (!response.IsSuccessStatusCode)
|
||
return new List<int>();
|
||
|
||
var responseBody = await response.Content.ReadAsStringAsync();
|
||
var json = JsonDocument.Parse(responseBody);
|
||
|
||
var listIds = new List<int>();
|
||
|
||
if (json.RootElement.TryGetProperty("data", out var data))
|
||
{
|
||
if (data.TryGetProperty("lists", out var lists))
|
||
{
|
||
foreach (var list in lists.EnumerateArray())
|
||
{
|
||
if (list.TryGetProperty("id", out var id))
|
||
{
|
||
listIds.Add(id.GetInt32());
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
return listIds;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogError(ex, $"Error getting list IDs for subscriber {subscriberId}");
|
||
return new List<int>();
|
||
}
|
||
}
|
||
|
||
private async Task AddSubscriberToList(List<int> listIds, Subscriber subscriber)
|
||
{
|
||
var subscriberData = new
|
||
{
|
||
email = subscriber.Email,
|
||
name = subscriber.FullName,
|
||
lists = listIds, // Массив ID списков
|
||
attribs = subscriber.Attributes,
|
||
status = "enabled",
|
||
mode = "subscribe"
|
||
};
|
||
|
||
var jsonContent = JsonSerializer.Serialize(subscriberData);
|
||
var content = new StringContent(jsonContent, Encoding.UTF8, "application/json");
|
||
|
||
var response = await _httpClient.PostAsync("/api/subscribers", content);
|
||
|
||
if (!response.IsSuccessStatusCode)
|
||
{
|
||
var errorContent = await response.Content.ReadAsStringAsync();
|
||
_logger.LogWarning($"Failed to add subscriber {subscriber.Email}: {response.StatusCode} - {errorContent}");
|
||
throw new Exception($"Failed to add subscriber: {errorContent}");
|
||
}
|
||
}
|
||
|
||
public async Task<bool> CreateCampaignAsync(string name, int listId, int templateId)
|
||
{
|
||
var content = new StringContent(
|
||
JsonSerializer.Serialize(new
|
||
{
|
||
name = name,
|
||
subject = "Тема письма",
|
||
lists = new[] { listId },
|
||
template_id = templateId,
|
||
type = "regular",
|
||
content_type = "html"
|
||
}),
|
||
Encoding.UTF8,
|
||
"application/json");
|
||
|
||
var response = await _httpClient.PostAsync("/api/campaigns", content);
|
||
return response.IsSuccessStatusCode;
|
||
}
|
||
|
||
public async Task<List<ListInfo>> GetListsAsync()
|
||
{
|
||
_logger.LogInformation("Fetching lists from Listmonk");
|
||
|
||
var response = await _httpClient.GetAsync("/api/lists");
|
||
|
||
if (!response.IsSuccessStatusCode)
|
||
{
|
||
var errorContent = await response.Content.ReadAsStringAsync();
|
||
_logger.LogError($"Failed to fetch lists: {response.StatusCode} - {errorContent}");
|
||
throw new Exception($"Failed to fetch lists: {response.StatusCode}");
|
||
}
|
||
|
||
var responseBody = await response.Content.ReadAsStringAsync();
|
||
_logger.LogInformation($"Raw response: {responseBody.Substring(0, Math.Min(500, responseBody.Length))}");
|
||
|
||
var json = JsonDocument.Parse(responseBody);
|
||
var lists = new List<ListInfo>();
|
||
|
||
// Структура: {"data": {"results": [...]}}
|
||
if (json.RootElement.TryGetProperty("data", out var data))
|
||
{
|
||
if (data.ValueKind == JsonValueKind.Object &&
|
||
data.TryGetProperty("results", out var results))
|
||
{
|
||
if (results.ValueKind == JsonValueKind.Array)
|
||
{
|
||
foreach (var list in results.EnumerateArray())
|
||
{
|
||
var listInfo = new ListInfo
|
||
{
|
||
Id = list.GetProperty("id").GetInt32(),
|
||
Name = list.GetProperty("name").GetString() ?? string.Empty,
|
||
SubscriberCount = list.TryGetProperty("subscriber_count", out var countProp)
|
||
? countProp.GetInt32()
|
||
: 0
|
||
};
|
||
lists.Add(listInfo);
|
||
_logger.LogInformation($"Parsed list: ID={listInfo.Id}, Name={listInfo.Name}, Subscribers={listInfo.SubscriberCount}");
|
||
}
|
||
}
|
||
}
|
||
else if (data.ValueKind == JsonValueKind.Array)
|
||
{
|
||
// На случай, если Listmonk вернёт массив напрямую
|
||
foreach (var list in data.EnumerateArray())
|
||
{
|
||
var listInfo = new ListInfo
|
||
{
|
||
Id = list.GetProperty("id").GetInt32(),
|
||
Name = list.GetProperty("name").GetString() ?? string.Empty,
|
||
SubscriberCount = list.TryGetProperty("subscriber_count", out var countProp)
|
||
? countProp.GetInt32()
|
||
: 0
|
||
};
|
||
lists.Add(listInfo);
|
||
}
|
||
}
|
||
}
|
||
|
||
_logger.LogInformation($"Total lists found: {lists.Count}");
|
||
return lists;
|
||
}
|
||
|
||
public async Task<List<string>> GetListAttributesAsync(int listId)
|
||
{
|
||
_logger.LogInformation($"Fetching attributes for list {listId}");
|
||
|
||
// Получаем ТОЛЬКО подписчиков из конкретного списка
|
||
var response = await _httpClient.GetAsync($"/api/subscribers?list_id={listId}&per_page=1000");
|
||
|
||
if (!response.IsSuccessStatusCode)
|
||
{
|
||
_logger.LogWarning($"Failed to fetch subscribers for list {listId}: {response.StatusCode}");
|
||
return new List<string>();
|
||
}
|
||
|
||
var responseBody = await response.Content.ReadAsStringAsync();
|
||
_logger.LogInformation($"Subscribers response for list {listId}: {responseBody.Substring(0, Math.Min(500, responseBody.Length))}");
|
||
|
||
var json = JsonDocument.Parse(responseBody);
|
||
var attributes = new HashSet<string>();
|
||
|
||
// Структура: {"data": {"results": [...]}}
|
||
if (json.RootElement.TryGetProperty("data", out var data))
|
||
{
|
||
JsonElement results;
|
||
if (data.ValueKind == JsonValueKind.Object && data.TryGetProperty("results", out results))
|
||
{
|
||
if (results.ValueKind == JsonValueKind.Array)
|
||
{
|
||
foreach (var subscriber in results.EnumerateArray())
|
||
{
|
||
if (subscriber.TryGetProperty("attribs", out var attribs))
|
||
{
|
||
foreach (var prop in attribs.EnumerateObject())
|
||
{
|
||
attributes.Add(prop.Name);
|
||
_logger.LogDebug($"Found attribute '{prop.Name}' for list {listId}");
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
else if (data.ValueKind == JsonValueKind.Array)
|
||
{
|
||
foreach (var subscriber in data.EnumerateArray())
|
||
{
|
||
if (subscriber.TryGetProperty("attribs", out var attribs))
|
||
{
|
||
foreach (var prop in attribs.EnumerateObject())
|
||
{
|
||
attributes.Add(prop.Name);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
var result = attributes.ToList();
|
||
_logger.LogInformation($"Found {result.Count} unique attributes for list {listId}: {string.Join(", ", result)}");
|
||
return result;
|
||
}
|
||
|
||
public async Task<int> CreateTemplateAsync(string name, string subject, string body)
|
||
{
|
||
_logger.LogInformation($"Creating template: {name}");
|
||
|
||
// Listmonk требует плейсхолдер {{ template "content" . }} в теле шаблона
|
||
// Если его нет — добавляем автоматически
|
||
if (!body.Contains("{{ template \"content\" . }}"))
|
||
{
|
||
body = body.TrimEnd() + "\n\n{{ template \"content\" . }}";
|
||
_logger.LogInformation("Added required placeholder to template body");
|
||
}
|
||
|
||
var templateData = new
|
||
{
|
||
name = name,
|
||
subject = subject,
|
||
body = body,
|
||
type = "campaign"
|
||
};
|
||
|
||
var jsonContent = JsonSerializer.Serialize(templateData);
|
||
_logger.LogInformation($"Request body: {jsonContent}");
|
||
|
||
var content = new StringContent(jsonContent, Encoding.UTF8, "application/json");
|
||
|
||
var response = await _httpClient.PostAsync("/api/templates", content);
|
||
var responseBody = await response.Content.ReadAsStringAsync();
|
||
|
||
_logger.LogInformation($"Response status: {response.StatusCode}");
|
||
_logger.LogInformation($"Response body: {responseBody}");
|
||
|
||
if (!response.IsSuccessStatusCode)
|
||
{
|
||
_logger.LogError($"Failed to create template: {response.StatusCode} - {responseBody}");
|
||
throw new Exception($"Failed to create template: {responseBody}");
|
||
}
|
||
|
||
var json = JsonDocument.Parse(responseBody);
|
||
|
||
int templateId;
|
||
if (json.RootElement.TryGetProperty("data", out var data))
|
||
{
|
||
templateId = data.GetProperty("id").GetInt32();
|
||
}
|
||
else
|
||
{
|
||
templateId = json.RootElement.GetProperty("id").GetInt32();
|
||
}
|
||
|
||
_logger.LogInformation($"Template created with ID: {templateId}");
|
||
return templateId;
|
||
}
|
||
public async Task<List<TemplateInfo>> GetTemplatesAsync()
|
||
{
|
||
_logger.LogInformation("Fetching templates from Listmonk");
|
||
|
||
var response = await _httpClient.GetAsync("/api/templates");
|
||
|
||
if (!response.IsSuccessStatusCode)
|
||
{
|
||
var error = await response.Content.ReadAsStringAsync();
|
||
_logger.LogError($"Failed to fetch templates: {response.StatusCode} - {error}");
|
||
return new List<TemplateInfo>();
|
||
}
|
||
|
||
var responseBody = await response.Content.ReadAsStringAsync();
|
||
_logger.LogInformation($"Templates response: {responseBody.Substring(0, Math.Min(500, responseBody.Length))}");
|
||
|
||
var json = JsonDocument.Parse(responseBody);
|
||
var templates = new List<TemplateInfo>();
|
||
|
||
if (json.RootElement.TryGetProperty("data", out var data))
|
||
{
|
||
JsonElement results;
|
||
if (data.ValueKind == JsonValueKind.Object && data.TryGetProperty("results", out results))
|
||
{
|
||
if (results.ValueKind == JsonValueKind.Array)
|
||
{
|
||
foreach (var template in results.EnumerateArray())
|
||
{
|
||
templates.Add(new TemplateInfo
|
||
{
|
||
Id = template.GetProperty("id").GetInt32(),
|
||
Name = template.GetProperty("name").GetString() ?? string.Empty,
|
||
Subject = template.TryGetProperty("subject", out var subj)
|
||
? subj.GetString() ?? string.Empty
|
||
: string.Empty
|
||
});
|
||
}
|
||
}
|
||
}
|
||
else if (data.ValueKind == JsonValueKind.Array)
|
||
{
|
||
foreach (var template in data.EnumerateArray())
|
||
{
|
||
templates.Add(new TemplateInfo
|
||
{
|
||
Id = template.GetProperty("id").GetInt32(),
|
||
Name = template.GetProperty("name").GetString() ?? string.Empty,
|
||
Subject = template.TryGetProperty("subject", out var subj)
|
||
? subj.GetString() ?? string.Empty
|
||
: string.Empty
|
||
});
|
||
}
|
||
}
|
||
}
|
||
|
||
_logger.LogInformation($"Found {templates.Count} templates");
|
||
return templates;
|
||
}
|
||
|
||
public async Task<string> GetTemplateBodyAsync(int templateId)
|
||
{
|
||
_logger.LogInformation($"Fetching template {templateId}");
|
||
|
||
var response = await _httpClient.GetAsync($"/api/templates/{templateId}");
|
||
response.EnsureSuccessStatusCode();
|
||
|
||
var responseBody = await response.Content.ReadAsStringAsync();
|
||
var json = JsonDocument.Parse(responseBody);
|
||
|
||
if (json.RootElement.TryGetProperty("data", out var data))
|
||
{
|
||
if (data.TryGetProperty("body", out var body))
|
||
{
|
||
return body.GetString() ?? string.Empty;
|
||
}
|
||
}
|
||
|
||
return string.Empty;
|
||
}
|
||
|
||
public async Task<int> CreateCampaignAsync(string name, int listId, int templateId, string subject)
|
||
{
|
||
_logger.LogInformation($"Creating campaign: {name}");
|
||
_logger.LogInformation($"List ID: {listId}, Template ID: {templateId}, Subject: {subject}");
|
||
|
||
// Убираем send_at - кампания создастся как черновик, потом запустим отдельно
|
||
var campaignData = new
|
||
{
|
||
name = name,
|
||
subject = subject,
|
||
lists = new[] { listId },
|
||
template_id = templateId,
|
||
type = "regular",
|
||
content_type = "html",
|
||
messenger = "email"
|
||
};
|
||
|
||
var jsonContent = JsonSerializer.Serialize(campaignData);
|
||
_logger.LogInformation($"Request body: {jsonContent}");
|
||
|
||
var content = new StringContent(jsonContent, Encoding.UTF8, "application/json");
|
||
|
||
var response = await _httpClient.PostAsync("/api/campaigns", content);
|
||
var responseBody = await response.Content.ReadAsStringAsync();
|
||
|
||
_logger.LogInformation($"Response status: {response.StatusCode}");
|
||
_logger.LogInformation($"Response body: {responseBody}");
|
||
|
||
if (!response.IsSuccessStatusCode)
|
||
{
|
||
_logger.LogError($"Failed to create campaign: {response.StatusCode} - {responseBody}");
|
||
throw new Exception($"Failed to create campaign: {responseBody}");
|
||
}
|
||
|
||
var json = JsonDocument.Parse(responseBody);
|
||
|
||
int campaignId;
|
||
if (json.RootElement.TryGetProperty("data", out var data))
|
||
{
|
||
campaignId = data.GetProperty("id").GetInt32();
|
||
}
|
||
else
|
||
{
|
||
campaignId = json.RootElement.GetProperty("id").GetInt32();
|
||
}
|
||
|
||
_logger.LogInformation($"Campaign created with ID: {campaignId}");
|
||
return campaignId;
|
||
}
|
||
|
||
public async Task<bool> StartCampaignAsync(int campaignId)
|
||
{
|
||
_logger.LogInformation($"Starting campaign {campaignId}");
|
||
|
||
var statusData = new
|
||
{
|
||
status = "running"
|
||
};
|
||
|
||
var jsonContent = JsonSerializer.Serialize(statusData);
|
||
_logger.LogInformation($"Request body: {jsonContent}");
|
||
|
||
var content = new StringContent(jsonContent, Encoding.UTF8, "application/json");
|
||
|
||
// ВАЖНО: PUT, не POST!
|
||
var request = new HttpRequestMessage(HttpMethod.Put, $"/api/campaigns/{campaignId}/status")
|
||
{
|
||
Content = content
|
||
};
|
||
|
||
var response = await _httpClient.SendAsync(request);
|
||
var responseBody = await response.Content.ReadAsStringAsync();
|
||
|
||
_logger.LogInformation($"Response status: {response.StatusCode}");
|
||
_logger.LogInformation($"Response body: {responseBody}");
|
||
|
||
if (!response.IsSuccessStatusCode)
|
||
{
|
||
_logger.LogError($"Failed to start campaign: {response.StatusCode} - {responseBody}");
|
||
return false;
|
||
}
|
||
|
||
_logger.LogInformation($"Campaign {campaignId} started successfully");
|
||
return true;
|
||
}
|
||
public async Task<Subscriber?> GetSubscriberByEmailAsync(string email)
|
||
{
|
||
_logger.LogInformation($"Searching for subscriber by email: {email}");
|
||
|
||
try
|
||
{
|
||
// Ищем подписчика по email
|
||
var response = await _httpClient.GetAsync($"/api/subscribers?query=LOWER(email)=LOWER('{email}')&per_page=1");
|
||
|
||
if (!response.IsSuccessStatusCode)
|
||
{
|
||
_logger.LogWarning($"Failed to search subscriber: {response.StatusCode}");
|
||
return null;
|
||
}
|
||
|
||
var responseBody = await response.Content.ReadAsStringAsync();
|
||
var json = JsonDocument.Parse(responseBody);
|
||
|
||
if (json.RootElement.TryGetProperty("data", out var data))
|
||
{
|
||
JsonElement results;
|
||
if (data.ValueKind == JsonValueKind.Object && data.TryGetProperty("results", out results))
|
||
{
|
||
if (results.ValueKind == JsonValueKind.Array && results.GetArrayLength() > 0)
|
||
{
|
||
var subscriberData = results[0];
|
||
|
||
// Парсим атрибуты
|
||
var attributes = new Dictionary<string, string>();
|
||
if (subscriberData.TryGetProperty("attribs", out var attribs))
|
||
{
|
||
foreach (var prop in attribs.EnumerateObject())
|
||
{
|
||
attributes[prop.Name] = prop.Value.GetString() ?? string.Empty;
|
||
}
|
||
}
|
||
|
||
// Получаем списки
|
||
var listIds = new List<int>();
|
||
if (subscriberData.TryGetProperty("lists", out var lists))
|
||
{
|
||
foreach (var list in lists.EnumerateArray())
|
||
{
|
||
if (list.TryGetProperty("id", out var id))
|
||
{
|
||
listIds.Add(id.GetInt32());
|
||
}
|
||
}
|
||
}
|
||
|
||
return new Subscriber
|
||
{
|
||
Email = subscriberData.GetProperty("email").GetString() ?? string.Empty,
|
||
FullName = subscriberData.GetProperty("name").GetString() ?? string.Empty,
|
||
Attributes = attributes
|
||
};
|
||
}
|
||
}
|
||
}
|
||
|
||
return null;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogError(ex, $"Error searching subscriber {email}");
|
||
return null;
|
||
}
|
||
}
|
||
|
||
public async Task<bool> UpdateSubscriberAsync(int subscriberId, string email, string name, Dictionary<string, string> attributes, List<int> listIds)
|
||
{
|
||
_logger.LogInformation($"Updating subscriber {subscriberId} ({email})");
|
||
|
||
var updateData = new
|
||
{
|
||
email = email,
|
||
name = name,
|
||
attribs = attributes,
|
||
lists = listIds,
|
||
status = "enabled"
|
||
};
|
||
|
||
var jsonContent = JsonSerializer.Serialize(updateData);
|
||
_logger.LogInformation($"Update data: {jsonContent}");
|
||
|
||
var content = new StringContent(jsonContent, Encoding.UTF8, "application/json");
|
||
var response = await _httpClient.PutAsync($"/api/subscribers/{subscriberId}", content);
|
||
|
||
var responseBody = await response.Content.ReadAsStringAsync();
|
||
_logger.LogInformation($"Update response: {response.StatusCode} - {responseBody}");
|
||
|
||
return response.IsSuccessStatusCode;
|
||
}
|
||
private async Task<int?> GetSubscriberIdByEmailAsync(string email)
|
||
{
|
||
try
|
||
{
|
||
var response = await _httpClient.GetAsync($"/api/subscribers?query=LOWER(email)=LOWER('{email}')&per_page=1");
|
||
|
||
if (!response.IsSuccessStatusCode)
|
||
return null;
|
||
|
||
var responseBody = await response.Content.ReadAsStringAsync();
|
||
var json = JsonDocument.Parse(responseBody);
|
||
|
||
if (json.RootElement.TryGetProperty("data", out var data))
|
||
{
|
||
JsonElement results;
|
||
if (data.ValueKind == JsonValueKind.Object && data.TryGetProperty("results", out results))
|
||
{
|
||
if (results.ValueKind == JsonValueKind.Array && results.GetArrayLength() > 0)
|
||
{
|
||
return results[0].GetProperty("id").GetInt32();
|
||
}
|
||
}
|
||
}
|
||
|
||
return null;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogError(ex, $"Error getting subscriber ID for {email}");
|
||
return null;
|
||
}
|
||
}
|
||
}
|
||
}
|