Try your search with a different keyword or use * as a wildcard.
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using brevo_csharp.Api;
using brevo_csharp.Client;
using brevo_csharp.Model;
using Microsoft.Net.Http.Headers;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Nop.Core;
using Nop.Core.Domain.Customers;
using Nop.Core.Domain.Localization;
using Nop.Core.Domain.Messages;
using Nop.Plugin.Misc.Brevo.Domain;
using Nop.Services.Configuration;
using Nop.Services.Customers;
using Nop.Services.Directory;
using Nop.Services.Installation;
using Nop.Services.Localization;
using Nop.Services.Logging;
using Nop.Services.Messages;
using Nop.Services.Stores;
using Nop.Web.Framework.Mvc.Routing;
using static brevo_csharp.Model.GetAttributesAttributes;
namespace Nop.Plugin.Misc.Brevo.Services;
/// <summary>
/// Represents Brevo manager
/// </summary>
public partial class BrevoManager
{
#region Fields
protected readonly ICountryService _countryService;
protected readonly ICustomerService _customerService;
protected readonly IEmailAccountService _emailAccountService;
protected readonly ILanguageService _languageService;
protected readonly ILogger _logger;
protected readonly INewsLetterSubscriptionService _newsLetterSubscriptionService;
protected readonly INopUrlHelper _nopUrlHelper;
protected readonly ISettingService _settingService;
protected readonly IStateProvinceService _stateProvinceService;
protected readonly IStoreService _storeService;
protected readonly IWebHelper _webHelper;
protected readonly IWorkContext _workContext;
#endregion
#region Ctor
public BrevoManager(ICountryService countryService,
ICustomerService customerService,
IEmailAccountService emailAccountService,
ILanguageService languageService,
ILogger logger,
INewsLetterSubscriptionService newsLetterSubscriptionService,
INopUrlHelper nopUrlHelper,
ISettingService settingService,
IStateProvinceService stateProvinceService,
IStoreService storeService,
IWebHelper webHelper,
IWorkContext workContext)
{
_countryService = countryService;
_customerService = customerService;
_emailAccountService = emailAccountService;
_languageService = languageService;
_logger = logger;
_newsLetterSubscriptionService = newsLetterSubscriptionService;
_nopUrlHelper = nopUrlHelper;
_settingService = settingService;
_stateProvinceService = stateProvinceService;
_storeService = storeService;
_webHelper = webHelper;
_workContext = workContext;
}
#endregion
#region Utilities
/// <summary>
/// Handle function and get result
/// </summary>
/// <typeparam name="TResult">Result type</typeparam>
/// <param name="function">Function</param>
/// <param name="logErrors">Whether to log errors</param>
/// <returns>
/// A task that represents the asynchronous operation
/// The task result contains the result; error if exists
/// </returns>
private async Task<(TResult Result, string Error)> HandleFunctionAsync<TResult>(Func<Task<TResult>> function, bool logErrors = true)
{
try
{
//whether plugin is configured
var brevoSettings = await _settingService.LoadSettingAsync<BrevoSettings>();
if (!IsConfigured(brevoSettings))
throw new NopException("Plugin not configured");
return (await function(), default);
}
catch (Exception exception)
{
var errorMessage = exception.Message;
if (logErrors)
{
var logMessage = $"{BrevoDefaults.SystemName} error: {Environment.NewLine}{errorMessage}";
await _logger.ErrorAsync(logMessage, exception, await _workContext.GetCurrentCustomerAsync());
}
return (default, errorMessage);
}
}
/// <summary>
/// Prepare API client
/// </summary>
/// <returns>
/// A task that represents the asynchronous operation
/// The task result contains the aPI client
/// </returns>
protected async Task<TClient> CreateApiClientAsync<TClient>(Func<Configuration, TClient> clientCtor) where TClient : IApiAccessor
{
//check whether plugin is configured to request services (validate API key)
var brevoSettings = await _settingService.LoadSettingAsync<BrevoSettings>();
if (!IsConfigured(brevoSettings))
throw new NopException("Plugin not configured");
var apiConfiguration = new Configuration()
{
ApiKey = new Dictionary<string, string>
{
[BrevoDefaults.ApiKeyHeader] = brevoSettings.ApiKey,
[BrevoDefaults.PartnerKeyHeader] = brevoSettings.ApiKey
},
ApiKeyPrefix = new Dictionary<string, string> { [BrevoDefaults.PartnerKeyHeader] = BrevoDefaults.PartnerName },
UserAgent = BrevoDefaults.UserAgent
};
return clientCtor(apiConfiguration);
}
/// <summary>
/// Import contacts from the store to account
/// </summary>
/// <param name="brevoSettings">Plugin settings</param>
/// <returns>
/// A task that represents the asynchronous operation
/// The task result contains the list of messages
/// </returns>
protected async Task<IList<(NotifyType Type, string Message)>> ImportContactsAsync(BrevoSettings brevoSettings)
{
var messages = new List<(NotifyType, string)>();
//import contacts to account
try
{
//create API client
var client = await CreateApiClientAsync(config => new ContactsApi(config));
var languages = await _languageService.GetAllLanguagesAsync();
List<Customer> customers = null;
async Task<List<Customer>> getCustomersAsync()
{
customers ??= (await _customerService.GetAllCustomersAsync()).ToList();
return customers;
}
async Task<string> getNamesAsync()
{
switch (await GetAccountLanguageAsync())
{
case BrevoAccountLanguage.French:
return
$"{BrevoDefaults.FirstNameFrenchServiceAttribute};" +
$"{BrevoDefaults.LastNameFrenchServiceAttribute};";
case BrevoAccountLanguage.German:
return
$"{BrevoDefaults.FirstNameGermanServiceAttribute};" +
$"{BrevoDefaults.LastNameGermanServiceAttribute};";
case BrevoAccountLanguage.Italian:
return
$"{BrevoDefaults.FirstNameItalianServiceAttribute};" +
$"{BrevoDefaults.LastNameItalianServiceAttribute};";
case BrevoAccountLanguage.Portuguese:
return
$"{BrevoDefaults.FirstNamePortugueseServiceAttribute};" +
$"{BrevoDefaults.LastNamePortugueseServiceAttribute};";
case BrevoAccountLanguage.Spanish:
return
$"{BrevoDefaults.FirstNameSpanishServiceAttribute};" +
$"{BrevoDefaults.LastNameSpanishServiceAttribute};";
default:
return
$"{BrevoDefaults.FirstNameServiceAttribute};" +
$"{BrevoDefaults.LastNameServiceAttribute};";
}
}
//get mappings from the settings
foreach (var mapping in brevoSettings.SubscriptionTypeMappings)
{
var typeId = mapping.Key;
var listId = mapping.Value;
if (typeId == 0 || listId == 0)
continue;
//try to get subscriptions by the type
var subscriptions = (await _newsLetterSubscriptionService
.GetAllNewsLetterSubscriptionsAsync(subscriptionTypeId: typeId, isActive: true))
.ToList();
if (!subscriptions.Any())
{
await _logger.WarningAsync($"Brevo synchronization warning: There are no subscriptions of type #{typeId}");
messages.Add((NotifyType.Warning, $"There are no subscriptions of type #{typeId}"));
continue;
}
//get notification URL
var notificationUrl = _nopUrlHelper.RouteUrl(BrevoDefaults.ImportContactsRoute, null, _webHelper.GetCurrentRequestProtocol());
//prepare CSV
var title =
$"{BrevoDefaults.EmailServiceAttribute};" +
await getNamesAsync() +
$"{BrevoDefaults.UsernameServiceAttribute};" +
$"{BrevoDefaults.SMSServiceAttribute};" +
$"{BrevoDefaults.PhoneServiceAttribute};" +
$"{BrevoDefaults.CountryServiceAttribute};" +
$"{BrevoDefaults.StoreIdServiceAttribute};" +
$"{BrevoDefaults.GenderServiceAttribute};" +
$"{BrevoDefaults.DateOfBirthServiceAttribute};" +
$"{BrevoDefaults.CompanyServiceAttribute};" +
$"{BrevoDefaults.Address1ServiceAttribute};" +
$"{BrevoDefaults.Address2ServiceAttribute};" +
$"{BrevoDefaults.ZipCodeServiceAttribute};" +
$"{BrevoDefaults.CityServiceAttribute};" +
$"{BrevoDefaults.CountyServiceAttribute};" +
$"{BrevoDefaults.StateServiceAttribute};" +
$"{BrevoDefaults.FaxServiceAttribute};" +
$"{BrevoDefaults.LanguageAttribute};";
var csv = await subscriptions.AggregateAwaitAsync(title, async (all, subscription) =>
{
var firstName = string.Empty;
var lastName = string.Empty;
var phone = string.Empty;
var countryName = string.Empty;
var sms = string.Empty;
var gender = string.Empty;
var dateOfBirth = string.Empty;
var company = string.Empty;
var address1 = string.Empty;
var address2 = string.Empty;
var zipCode = string.Empty;
var city = string.Empty;
var county = string.Empty;
var state = string.Empty;
var fax = string.Empty;
Language language = null;
var customer = (await getCustomersAsync()).FirstOrDefault(customer => customer.Email == subscription.Email);
if (customer != null)
{
firstName = customer.FirstName;
lastName = customer.LastName;
phone = customer.Phone;
var countryId = customer.CountryId;
var country = await _countryService.GetCountryByIdAsync(countryId);
countryName = country?.Name;
var countryIsoCode = country?.NumericIsoCode ?? 0;
if (countryIsoCode > 0 && !string.IsNullOrEmpty(phone))
{
//use the first phone code only
var phoneCode = ISO3166.FromISOCode(countryIsoCode)
?.DialCodes?.FirstOrDefault()?.Replace(" ", string.Empty) ?? string.Empty;
sms = phone.Replace($"+{phoneCode}", string.Empty);
}
gender = customer.Gender;
dateOfBirth = customer.DateOfBirth?.ToString("yyyy-MM-dd");
company = customer.Company;
address1 = customer.StreetAddress;
address2 = customer.StreetAddress2;
zipCode = customer.ZipPostalCode;
city = customer.City;
county = customer.County;
state = (await _stateProvinceService.GetStateProvinceByIdAsync(customer.StateProvinceId))?.Name;
fax = customer.Fax;
}
language = languages
.FirstOrDefault(lang => lang.Id == (customer?.LanguageId ?? subscription.LanguageId))
?? languages.FirstOrDefault();
return
$"{all}\n" +
$"{subscription.Email};" +
$"{firstName};" +
$"{lastName};" +
$"{customer?.Username};" +
$"{sms};" +
$"{phone};" +
$"{countryName};" +
$"{subscription.StoreId};" +
$"{gender};" +
$"{dateOfBirth};" +
$"{company};" +
$"{address1};" +
$"{address2};" +
$"{zipCode};" +
$"{city};" +
$"{county};" +
$"{state};" +
$"{fax};" +
$"{language?.LanguageCulture};";
});
//prepare data to import
var requestContactImport = new RequestContactImport
{
NotifyUrl = notificationUrl,
FileBody = csv,
ListIds = [listId]
};
//start import
await client.ImportContactsAsync(requestContactImport);
}
}
catch (Exception exception)
{
//log full error
await _logger.ErrorAsync($"Brevo synchronization error: {exception.Message}", exception, await _workContext.GetCurrentCustomerAsync());
messages.Add((NotifyType.Error, $"Brevo synchronization error: {exception.Message}"));
}
return messages;
}
/// <summary>
/// Export contacts from account to the store
/// </summary>
/// <param name="brevoSettings">Plugin settings</param>
/// <returns>
/// A task that represents the asynchronous operation
/// The task result contains the list of messages
/// </returns>
protected async Task<IList<(NotifyType Type, string Message)>> ExportContactsAsync(BrevoSettings brevoSettings)
{
var messages = new List<(NotifyType, string)>();
try
{
//create API client
var client = await CreateApiClientAsync(config => new ContactsApi(config));
//get mappings from the settings
foreach (var mapping in brevoSettings.SubscriptionTypeMappings)
{
var typeId = mapping.Key;
var listId = mapping.Value;
if (typeId == 0 || listId == 0)
continue;
//check whether there are contacts in the list
var contacts = await client.GetContactsFromListAsync(listId);
var contactObjects = JsonConvert
.DeserializeAnonymousType(contacts.ToJson(), new { contacts = new[] { new { email = string.Empty, emailBlacklisted = false } } });
var blackListedEmails = contactObjects?.contacts
?.Where(contact => contact.emailBlacklisted && !string.IsNullOrEmpty(contact.email))
.Select(contact => contact.email)
.Distinct()
.ToList()
?? new List<string>();
foreach (var email in blackListedEmails)
{
//email in black list, so unsubscribe contact
var subscriptions = await _newsLetterSubscriptionService
.GetNewsLetterSubscriptionsByEmailAsync(email, subscriptionTypeId: typeId);
foreach (var subscription in subscriptions)
{
subscription.Active = false;
await _newsLetterSubscriptionService.UpdateNewsLetterSubscriptionAsync(subscription, false);
}
}
}
}
catch (Exception exception)
{
//log full error
await _logger.ErrorAsync($"Brevo synchronization error: {exception.Message}", exception, await _workContext.GetCurrentCustomerAsync());
messages.Add((NotifyType.Error, $"Brevo synchronization error: {exception.Message}"));
}
return messages;
}
/// <summary>
/// Add new service attribute in account
/// </summary>
/// <param name="attributes">Collection of attributes</param>
/// <returns>
/// A task that represents the asynchronous operation
/// The task result contains the errors if exist
/// </returns>
protected async Task<string> CreateAttributesAsync(IList<(CategoryEnum Category, string Name, string Value, CreateAttribute.TypeEnum? Type)> attributes)
{
if (!attributes.Any())
return string.Empty;
try
{
//create API client
var client = await CreateApiClientAsync(config => new ContactsApi(config));
foreach (var attribute in attributes)
{
//prepare data
var createAttribute = new CreateAttribute(attribute.Value, type: attribute.Type);
//create attribute
await client.CreateAttributeAsync(attribute.Category.ToString().ToLowerInvariant(), attribute.Name, createAttribute);
}
}
catch (Exception exception)
{
//log full error
await _logger.ErrorAsync($"Brevo error: {exception.Message}.", exception, await _workContext.GetCurrentCustomerAsync());
return exception.Message;
}
return string.Empty;
}
[GeneratedRegex("(%[^\\%]*.%)")]
private static partial Regex SpecialCharRegex();
[GeneratedRegex("({{\\s*params\\..*?\\s*}})")]
private static partial Regex ParamsRegex();
#endregion
#region Methods
#region Synchronization
/// <summary>
/// Synchronize contacts
/// </summary>
/// <returns>
/// A task that represents the asynchronous operation
/// The task result contains the list of messages
/// </returns>
public async Task<IList<(NotifyType Type, string Message)>> SynchronizeAsync()
{
var messages = new List<(NotifyType, string)>();
try
{
//whether plugin is configured
var brevoSettings = await _settingService.LoadSettingAsync<BrevoSettings>();
if (!string.IsNullOrEmpty(brevoSettings.ApiKey))
{
var importMessages = await ImportContactsAsync(brevoSettings);
messages.AddRange(importMessages);
var exportMessages = await ExportContactsAsync(brevoSettings);
messages.AddRange(exportMessages);
}
}
catch (Exception exception)
{
//log full error
await _logger.ErrorAsync($"Brevo synchronization error: {exception.Message}.", exception, await _workContext.GetCurrentCustomerAsync());
messages.Add((NotifyType.Error, $"Brevo synchronization error: {exception.Message}"));
}
return messages;
}
/// <summary>
/// Subscribe new contact
/// </summary>
/// <param name="subscription">Subscription</param>
/// <returns>A task that represents the asynchronous operation</returns>
public async System.Threading.Tasks.Task SubscribeAsync(NewsLetterSubscription subscription)
{
try
{
//try to get list id
var brevoSettings = await _settingService.LoadSettingAsync<BrevoSettings>();
if (!brevoSettings.SubscriptionTypeMappings.TryGetValue(subscription.TypeId, out var listId) || listId == 0)
return;
//create API client
var client = await CreateApiClientAsync(config => new ContactsApi(config));
GetExtendedContactDetails contactObject = null;
try
{
contactObject = await client.GetContactInfoAsync(subscription.Email);
}
catch (ApiException apiException)
{
if (apiException.ErrorCode != 404)
{
await _logger.ErrorAsync($"Brevo error: {apiException.Message}.", apiException, await _workContext.GetCurrentCustomerAsync());
return;
}
}
//prepare attributes
var firstName = string.Empty;
var lastName = string.Empty;
var phone = string.Empty;
var sms = string.Empty;
var countryName = string.Empty;
var gender = string.Empty;
var dateOfBirth = string.Empty;
var company = string.Empty;
var address1 = string.Empty;
var address2 = string.Empty;
var zipCode = string.Empty;
var city = string.Empty;
var county = string.Empty;
var state = string.Empty;
var fax = string.Empty;
Language language = null;
var customer = await _customerService.GetCustomerByEmailAsync(subscription.Email);
if (customer != null)
{
firstName = customer.FirstName;
lastName = customer.LastName;
phone = customer.Phone;
var countryId = customer.CountryId;
var country = await _countryService.GetCountryByIdAsync(countryId);
countryName = country?.Name;
var countryIsoCode = country?.NumericIsoCode ?? 0;
if (countryIsoCode > 0 && !string.IsNullOrEmpty(phone))
{
//use the first phone code only
var phoneCode = ISO3166.FromISOCode(countryIsoCode)
?.DialCodes?.FirstOrDefault()?.Replace(" ", string.Empty) ?? string.Empty;
sms = phone.Replace($"+{phoneCode}", string.Empty);
}
gender = customer.Gender;
dateOfBirth = customer.DateOfBirth?.ToString("yyyy-MM-dd");
company = customer.Company;
address1 = customer.StreetAddress;
address2 = customer.StreetAddress2;
zipCode = customer.ZipPostalCode;
city = customer.City;
county = customer.County;
state = (await _stateProvinceService.GetStateProvinceByIdAsync(customer.StateProvinceId))?.Name;
fax = customer.Fax;
}
language = await _languageService.GetLanguageByIdAsync(customer?.LanguageId ?? subscription.LanguageId)
?? (await _languageService.GetAllLanguagesAsync(storeId: subscription.StoreId)).FirstOrDefault();
var attributes = new Dictionary<string, string>
{
[BrevoDefaults.UsernameServiceAttribute] = customer?.Username,
[BrevoDefaults.SMSServiceAttribute] = sms,
[BrevoDefaults.PhoneServiceAttribute] = phone,
[BrevoDefaults.CountryServiceAttribute] = countryName,
[BrevoDefaults.StoreIdServiceAttribute] = subscription.StoreId.ToString(),
[BrevoDefaults.GenderServiceAttribute] = gender,
[BrevoDefaults.DateOfBirthServiceAttribute] = dateOfBirth,
[BrevoDefaults.CompanyServiceAttribute] = company,
[BrevoDefaults.Address1ServiceAttribute] = address1,
[BrevoDefaults.Address2ServiceAttribute] = address2,
[BrevoDefaults.ZipCodeServiceAttribute] = zipCode,
[BrevoDefaults.CityServiceAttribute] = city,
[BrevoDefaults.CountyServiceAttribute] = county,
[BrevoDefaults.StateServiceAttribute] = state,
[BrevoDefaults.FaxServiceAttribute] = fax,
[BrevoDefaults.LanguageAttribute] = language?.LanguageCulture
};
switch (await GetAccountLanguageAsync())
{
case BrevoAccountLanguage.French:
attributes.Add(BrevoDefaults.FirstNameFrenchServiceAttribute, firstName);
attributes.Add(BrevoDefaults.LastNameFrenchServiceAttribute, lastName);
break;
case BrevoAccountLanguage.German:
attributes.Add(BrevoDefaults.FirstNameGermanServiceAttribute, firstName);
attributes.Add(BrevoDefaults.LastNameGermanServiceAttribute, lastName);
break;
case BrevoAccountLanguage.Italian:
attributes.Add(BrevoDefaults.FirstNameItalianServiceAttribute, firstName);
attributes.Add(BrevoDefaults.LastNameItalianServiceAttribute, lastName);
break;
case BrevoAccountLanguage.Portuguese:
attributes.Add(BrevoDefaults.FirstNamePortugueseServiceAttribute, firstName);
attributes.Add(BrevoDefaults.LastNamePortugueseServiceAttribute, lastName);
break;
case BrevoAccountLanguage.Spanish:
attributes.Add(BrevoDefaults.FirstNameSpanishServiceAttribute, firstName);
attributes.Add(BrevoDefaults.LastNameSpanishServiceAttribute, lastName);
break;
case BrevoAccountLanguage.English:
attributes.Add(BrevoDefaults.FirstNameServiceAttribute, firstName);
attributes.Add(BrevoDefaults.LastNameServiceAttribute, lastName);
break;
}
//Add new contact
if (contactObject == null)
{
var createContact = new CreateContact
{
Email = subscription.Email,
Attributes = attributes,
ListIds = [listId],
UpdateEnabled = true
};
await client.CreateContactAsync(createContact);
}
else
{
//update contact
var updateContact = new UpdateContact
{
Attributes = attributes,
ListIds = [listId],
EmailBlacklisted = false
};
await client.UpdateContactAsync(subscription.Email, updateContact);
}
}
catch (Exception exception)
{
//log full error
await _logger.ErrorAsync($"Brevo error: {exception.Message}.", exception, await _workContext.GetCurrentCustomerAsync());
}
}
/// <summary>
/// Unsubscribe contact
/// </summary>
/// <param name="subscription">Subscription</param>
/// <returns>A task that represents the asynchronous operation</returns>
public async System.Threading.Tasks.Task UnsubscribeAsync(NewsLetterSubscription subscription)
{
try
{
//try to get list id
var brevoSettings = await _settingService.LoadSettingAsync<BrevoSettings>();
if (!brevoSettings.SubscriptionTypeMappings.TryGetValue(subscription.TypeId, out var listId) || listId == 0)
return;
//create API client
var client = await CreateApiClientAsync(config => new ContactsApi(config));
//update contact
var updateContact = new UpdateContact
{
UnlinkListIds = [listId]
};
await client.UpdateContactAsync(subscription.Email, updateContact);
}
catch (Exception exception)
{
//log full error
await _logger.ErrorAsync($"Brevo error: {exception.Message}.", exception, await _workContext.GetCurrentCustomerAsync());
}
}
/// <summary>
/// Unsubscribe contact
/// </summary>
/// <param name="request">HTTP request</param>
/// <returns>A task that represents the asynchronous operation</returns>
public async System.Threading.Tasks.Task HandleWebhookAsync(Microsoft.AspNetCore.Http.HttpRequest request)
{
await HandleFunctionAsync(async () =>
{
using var streamReader = new StreamReader(request.Body);
var requestContent = await streamReader.ReadToEndAsync();
//parse string to JSON object
var unsubscriber = JsonConvert
.DeserializeAnonymousType(requestContent, new { list_id = new List<int>(), email = string.Empty });
if (unsubscriber.list_id?.Any() != true || string.IsNullOrEmpty(unsubscriber.email))
return true;
//get subscriptions by email
var subscriptions = await _newsLetterSubscriptionService.GetNewsLetterSubscriptionsByEmailAsync(unsubscriber.email, isActive: true);
if (!subscriptions.Any())
return true;
var brevoSettings = await _settingService.LoadSettingAsync<BrevoSettings>();
foreach (var mapping in brevoSettings.SubscriptionTypeMappings)
{
var typeId = mapping.Key;
var listId = mapping.Value;
if (typeId == 0 || listId == 0 || !unsubscriber.list_id.Contains(listId))
continue;
if (subscriptions.FirstOrDefault(subscription => subscription.TypeId == typeId) is not NewsLetterSubscription subscription)
continue;
//update subscription
subscription.Active = false;
await _newsLetterSubscriptionService.UpdateNewsLetterSubscriptionAsync(subscription);
await _logger.InformationAsync($"{BrevoDefaults.SystemName} unsubscription: email '{subscription.Email}', subscription type #{subscription.TypeId}");
}
return true;
});
}
/// <summary>
/// Create webhook to get notification about unsubscribed contacts
/// </summary>
/// <returns>
/// A task that represents the asynchronous operation
/// The task result contains the webhook id
/// </returns>
public async Task<int> GetUnsubscribeWebHookIdAsync()
{
try
{
//create API client
var client = await CreateApiClientAsync(config => new WebhooksApi(config));
//check whether webhook already exist
var brevoSettings = await _settingService.LoadSettingAsync<BrevoSettings>();
if (brevoSettings.UnsubscribeWebhookId != 0)
{
await client.GetWebhookAsync(brevoSettings.UnsubscribeWebhookId);
return brevoSettings.UnsubscribeWebhookId;
}
//or create new one
var notificationUrl = _nopUrlHelper.RouteUrl(BrevoDefaults.UnsubscribeContactRoute, null, _webHelper.GetCurrentRequestProtocol());
var webhook = new CreateWebhook(notificationUrl, "Unsubscribe event webhook",
[CreateWebhook.EventsEnum.Unsubscribed], CreateWebhook.TypeEnum.Transactional);
var result = await client.CreateWebhookAsync(webhook);
return (int)result.Id;
}
catch (Exception exception)
{
//log full error
await _logger.ErrorAsync($"Brevo error: {exception.Message}.", exception, await _workContext.GetCurrentCustomerAsync());
return 0;
}
}
/// <summary>
/// Update contact after completing order
/// </summary>
/// <param name="order">Order</param>
/// <returns>A task that represents the asynchronous operation</returns>
public async System.Threading.Tasks.Task UpdateContactAfterCompletingOrderAsync(Core.Domain.Orders.Order order)
{
try
{
ArgumentNullException.ThrowIfNull(order);
var customer = await _customerService.GetCustomerByIdAsync(order.CustomerId);
if (customer.Email is null)
return;
//create API client
var client = await CreateApiClientAsync(config => new ContactsApi(config));
try
{
var contactInfo = await client.GetContactInfoAsync(customer.Email);
}
catch (ApiException apiException)
{
if (apiException.ErrorCode == 404)
{
return;
}
else
{
await _logger.ErrorAsync($"Brevo error: {apiException.Message}.", apiException, await _workContext.GetCurrentCustomerAsync());
return;
}
}
//update contact
var attributes = new Dictionary<string, string>
{
[BrevoDefaults.IdServiceAttribute] = order.Id.ToString(),
[BrevoDefaults.OrderIdServiceAttribute] = order.Id.ToString(),
[BrevoDefaults.OrderDateServiceAttribute] = order.PaidDateUtc.ToString(),
[BrevoDefaults.OrderTotalServiceAttribute] = order.OrderTotal.ToString()
};
var updateContact = new UpdateContact { Attributes = attributes };
await client.UpdateContactAsync(customer.Email, updateContact);
}
catch (Exception exception)
{
//log full error
await _logger.ErrorAsync($"Brevo error: {exception.Message}.", exception, await _workContext.GetCurrentCustomerAsync());
}
}
#endregion
#region Common
/// <summary>
/// Check whether the plugin is configured
/// </summary>
/// <param name="settings">Plugin settings</param>
/// <returns>Result</returns>
public static bool IsConfigured(BrevoSettings settings)
{
//API key is required to request remote services
return !string.IsNullOrEmpty(settings?.ApiKey);
}
/// <summary>
/// Get account information
/// </summary>
/// <returns>
/// A task that represents the asynchronous operation
/// The task result contains the account info; whether marketing automation is enabled, errors if exist
/// </returns>
public async Task<(string Info, bool MarketingAutomationEnabled, string MAkey, string Errors)> GetAccountInfoAsync()
{
try
{
//create API client
var client = await CreateApiClientAsync(config => new AccountApi(config));
//get account
var account = await client.GetAccountAsync();
//prepare info
var info = string.Format("First name: {1}{0}Last name: {2}{0}Email: {3}{0}Email credits: {4}{0}SMS credits: {5}{0}",
Environment.NewLine,
WebUtility.HtmlEncode(account.FirstName),
WebUtility.HtmlEncode(account.LastName),
WebUtility.HtmlEncode(account.Email),
account.Plan.Where(plan => plan.Type != GetAccountPlan.TypeEnum.Sms).Sum(plan => plan.Credits),
account.Plan.Where(plan => plan.Type == GetAccountPlan.TypeEnum.Sms).Sum(plan => plan.Credits));
//get marketing automation tacker ID
var key = account.MarketingAutomation?.Key ?? string.Empty;
return (info, account.MarketingAutomation?.Enabled ?? false, key, null);
}
catch (Exception exception)
{
//log full error
await _logger.ErrorAsync($"Brevo error: {exception.Message}.", exception, await _workContext.GetCurrentCustomerAsync());
return (null, false, null, exception.Message);
}
}
/// <summary>
/// Set partner value
/// </summary>
/// <returns>True if partner successfully set; otherwise false</returns>
public async Task<bool> SetPartnerAsync()
{
try
{
var stores = (await _storeService.GetAllStoresAsync()).ToList();
var storeCredentials = new Dictionary<string, string>();
foreach (var store in stores)
{
var bSettings = await _settingService.LoadSettingAsync<BrevoSettings>(store.Id);
var apiKey = bSettings.ApiKey;
if (!string.IsNullOrEmpty(apiKey) && !storeCredentials.Where(s => s.Value == apiKey).Any())
storeCredentials.Add(store.Url, apiKey);
}
//whether plugin is configured
if (!storeCredentials.Any())
return false;
foreach (var storeCredential in storeCredentials)
{
await HttpBrevoClientAsync(storeCredential.Key, storeCredential.Value);
}
}
catch (Exception exception)
{
//log full error
await _logger.ErrorAsync($"Brevo error: {exception.Message}.", exception, await _workContext.GetCurrentCustomerAsync());
return false;
}
return true;
async System.Threading.Tasks.Task HttpBrevoClientAsync(string storeUrl, string apiKey)
{
//create API client
var httpClient = new HttpClient
{
//configure client
BaseAddress = new Uri(BrevoDefaults.AccountApiUrl),
Timeout = TimeSpan.FromSeconds(10),
};
//Default Request Headers needed to be added in the HttpClient Object
httpClient.DefaultRequestHeaders.Add(BrevoDefaults.ApiKeyHeader, apiKey);
httpClient.DefaultRequestHeaders.Add(BrevoDefaults.SibPluginHeader, BrevoDefaults.PluginVersion);
httpClient.DefaultRequestHeaders.Add(HeaderNames.UserAgent, BrevoDefaults.UserAgentAccountAPI);
httpClient.DefaultRequestHeaders.Add(HeaderNames.Accept, MimeTypes.ApplicationJson);
var requestObject = new JObject
{
{ "partnerName", BrevoDefaults.PartnerName },
{ "active", true },
{ "plugin_version", "1.0.0" },
{ "shop_version", NopVersion.FULL_VERSION },
{ "shop_url", storeUrl },
{ "created_at", DateTime.UtcNow },
{ "activated_at", DateTime.UtcNow },
{ "type", "sib" }
};
var requestString = JsonConvert.SerializeObject(requestObject);
var requestContent = new StringContent(requestString, Encoding.Default, MimeTypes.ApplicationJson);
var requestMessage = new HttpRequestMessage(HttpMethod.Post, "partner/information") { Content = requestContent };
var httpResponse = await httpClient.SendAsync(requestMessage);
httpResponse.EnsureSuccessStatusCode();
}
}
/// <summary>
/// Get available lists to synchronize contacts
/// </summary>
/// <returns>
/// A task that represents the asynchronous operation
/// The task result contains the list of id-name pairs of lists; errors if exist
/// </returns>
public async Task<(IList<(string Id, string Name)> Lists, string Errors)> GetListsAsync()
{
var availableLists = new List<(string Id, string Name)>();
try
{
//create API client
var client = await CreateApiClientAsync(config => new ContactsApi(config));
//get available lists
var lists = await client.GetListsAsync(BrevoDefaults.DefaultSynchronizationListsLimit);
//prepare id-name pairs
var template = new { lists = new[] { new { id = string.Empty, name = string.Empty } } };
var listObjects = JsonConvert.DeserializeAnonymousType(lists.ToJson(), template);
if (listObjects?.lists != null)
{
foreach (var list in listObjects.lists)
{
if (list != null)
availableLists.Add((list.id, list.name));
}
}
}
catch (Exception exception)
{
//log full error
await _logger.ErrorAsync($"Brevo error: {exception.Message}.", exception, await _workContext.GetCurrentCustomerAsync());
return (availableLists, exception.Message);
}
return (availableLists, null);
}
/// <summary>
/// Get available senders of transactional emails
/// </summary>
/// <returns>
/// A task that represents the asynchronous operation
/// The task result contains the list of id-name pairs of senders; errors if exist
/// </returns>
public async Task<(IList<(string Id, string Name)> Lists, string Errors)> GetSendersAsync()
{
var availableSenders = new List<(string Id, string Name)>();
try
{
//create API client
var client = await CreateApiClientAsync(config => new SendersApi(config));
//get available senderes
var senders = await client.GetSendersAsync();
//prepare id-name pairs
foreach (var sender in senders.Senders)
{
availableSenders.Add((sender.Id.ToString(), $"{sender.Name} ({sender.Email})"));
}
}
catch (Exception exception)
{
//log full error
await _logger.ErrorAsync($"Brevo error: {exception.Message}.", exception, await _workContext.GetCurrentCustomerAsync());
return (availableSenders, exception.Message);
}
return (availableSenders, null);
}
/// <summary>
/// Get account language
/// </summary>
/// <returns>
/// A task that represents the asynchronous operation
/// The task result contains the BrevoAccountLanguage
/// </returns>
public async Task<BrevoAccountLanguage> GetAccountLanguageAsync()
{
try
{
//create API client
var client = await CreateApiClientAsync(config => new ContactsApi(config));
var attributes = await client.GetAttributesAsync();
var allAttribytes = attributes.Attributes.Select(s => s.Name).ToList();
var defaultNameAttributes = new List<string>
{
BrevoDefaults.FirstNameServiceAttribute,
BrevoDefaults.LastNameServiceAttribute
};
if (defaultNameAttributes.All(attr => allAttribytes.Contains(attr)))
return BrevoAccountLanguage.English;
var frenchNameAttributes = new List<string>
{
BrevoDefaults.FirstNameFrenchServiceAttribute,
BrevoDefaults.LastNameFrenchServiceAttribute
};
if (frenchNameAttributes.All(attr => allAttribytes.Contains(attr)))
return BrevoAccountLanguage.French;
var italianNameAttributes = new List<string>
{
BrevoDefaults.FirstNameItalianServiceAttribute,
BrevoDefaults.LastNameItalianServiceAttribute
};
if (italianNameAttributes.All(attr => allAttribytes.Contains(attr)))
return BrevoAccountLanguage.Italian;
var spanishNameAttributes = new List<string>
{
BrevoDefaults.FirstNameSpanishServiceAttribute,
BrevoDefaults.LastNameSpanishServiceAttribute
};
if (spanishNameAttributes.All(attr => allAttribytes.Contains(attr)))
return BrevoAccountLanguage.Spanish;
var germanNameAttributes = new List<string>
{
BrevoDefaults.FirstNameGermanServiceAttribute,
BrevoDefaults.LastNameGermanServiceAttribute
};
if (germanNameAttributes.All(attr => allAttribytes.Contains(attr)))
return BrevoAccountLanguage.German;
var portugueseNameAttributes = new List<string>
{
BrevoDefaults.FirstNamePortugueseServiceAttribute,
BrevoDefaults.LastNamePortugueseServiceAttribute
};
if (portugueseNameAttributes.All(attr => allAttribytes.Contains(attr)))
return BrevoAccountLanguage.Portuguese;
//Create default customer names attribytes
var initialAttributes = new List<(CategoryEnum category, string Name, string Value, CreateAttribute.TypeEnum? Type)>
{
(CategoryEnum.Normal, BrevoDefaults.FirstNameServiceAttribute, null, CreateAttribute.TypeEnum.Text),
(CategoryEnum.Normal, BrevoDefaults.LastNameServiceAttribute, null, CreateAttribute.TypeEnum.Text)
};
//create attributes that are not already on account
var newAttributes = new List<(CategoryEnum category, string Name, string Value, CreateAttribute.TypeEnum? Type)>();
foreach (var attribute in initialAttributes)
{
if (!allAttribytes.Contains(attribute.Name))
newAttributes.Add(attribute);
}
await CreateAttributesAsync(newAttributes);
return BrevoAccountLanguage.English;
}
catch (Exception exception)
{
//log full error
await _logger.ErrorAsync($"Brevo error: {exception.Message}.", exception, await _workContext.GetCurrentCustomerAsync());
return BrevoAccountLanguage.English;
}
}
/// <summary>
/// Check and create missing attributes in account
/// </summary>
/// <returns>
/// A task that represents the asynchronous operation
/// The task result contains the errors if exist
/// </returns>
public async Task<string> PrepareAttributesAsync()
{
try
{
//create API client
var client = await CreateApiClientAsync(config => new ContactsApi(config));
var attributes = await client.GetAttributesAsync();
var attributeNames = attributes.Attributes.Select(s => s.Name).ToList();
//prepare attributes to create
var initialAttributes = new List<(CategoryEnum category, string Name, string Value, CreateAttribute.TypeEnum? Type)>
{
(CategoryEnum.Normal, BrevoDefaults.UsernameServiceAttribute, null, CreateAttribute.TypeEnum.Text),
(CategoryEnum.Normal, BrevoDefaults.PhoneServiceAttribute, null, CreateAttribute.TypeEnum.Text),
(CategoryEnum.Normal, BrevoDefaults.CountryServiceAttribute, null, CreateAttribute.TypeEnum.Text),
(CategoryEnum.Normal, BrevoDefaults.StoreIdServiceAttribute, null, CreateAttribute.TypeEnum.Text),
(CategoryEnum.Normal, BrevoDefaults.GenderServiceAttribute, null, CreateAttribute.TypeEnum.Text),
(CategoryEnum.Normal, BrevoDefaults.DateOfBirthServiceAttribute, null, CreateAttribute.TypeEnum.Text),
(CategoryEnum.Normal, BrevoDefaults.CompanyServiceAttribute, null, CreateAttribute.TypeEnum.Text),
(CategoryEnum.Normal, BrevoDefaults.Address1ServiceAttribute, null, CreateAttribute.TypeEnum.Text),
(CategoryEnum.Normal, BrevoDefaults.Address2ServiceAttribute, null, CreateAttribute.TypeEnum.Text),
(CategoryEnum.Normal, BrevoDefaults.ZipCodeServiceAttribute, null, CreateAttribute.TypeEnum.Text),
(CategoryEnum.Normal, BrevoDefaults.CityServiceAttribute, null, CreateAttribute.TypeEnum.Text),
(CategoryEnum.Normal, BrevoDefaults.CountyServiceAttribute, null, CreateAttribute.TypeEnum.Text),
(CategoryEnum.Normal, BrevoDefaults.StateServiceAttribute, null, CreateAttribute.TypeEnum.Text),
(CategoryEnum.Normal, BrevoDefaults.FaxServiceAttribute, null, CreateAttribute.TypeEnum.Text),
(CategoryEnum.Normal, BrevoDefaults.LanguageAttribute, null, CreateAttribute.TypeEnum.Text),
(CategoryEnum.Transactional, BrevoDefaults.OrderIdServiceAttribute, null, CreateAttribute.TypeEnum.Id),
(CategoryEnum.Transactional, BrevoDefaults.OrderDateServiceAttribute, null, CreateAttribute.TypeEnum.Text),
(CategoryEnum.Transactional, BrevoDefaults.OrderTotalServiceAttribute, null, CreateAttribute.TypeEnum.Float),
(CategoryEnum.Calculated, BrevoDefaults.OrderTotalSumServiceAttribute, $"SUM[{BrevoDefaults.OrderTotalServiceAttribute}]", null),
(CategoryEnum.Calculated, BrevoDefaults.OrderTotalMonthSumServiceAttribute, $"SUM[{BrevoDefaults.OrderTotalServiceAttribute},{BrevoDefaults.OrderDateServiceAttribute},>,NOW(-30)]", null),
(CategoryEnum.Calculated, BrevoDefaults.OrderCountServiceAttribute, $"COUNT[{BrevoDefaults.OrderIdServiceAttribute}]", null),
(CategoryEnum.Global, BrevoDefaults.AllOrderTotalSumServiceAttribute, $"SUM[{BrevoDefaults.OrderTotalSumServiceAttribute}]", null),
(CategoryEnum.Global, BrevoDefaults.AllOrderTotalMonthSumServiceAttribute, $"SUM[{BrevoDefaults.OrderTotalMonthSumServiceAttribute}]", null),
(CategoryEnum.Global, BrevoDefaults.AllOrderCountServiceAttribute, $"SUM[{BrevoDefaults.OrderCountServiceAttribute}]", null)
};
//create attributes that are not already on account
var newAttributes = new List<(CategoryEnum category, string Name, string Value, CreateAttribute.TypeEnum? Type)>();
foreach (var attribute in initialAttributes)
{
if (!attributeNames.Contains(attribute.Name))
newAttributes.Add(attribute);
}
return await CreateAttributesAsync(newAttributes);
}
catch (Exception exception)
{
//log full error
await _logger.ErrorAsync($"Brevo error: {exception.Message}.", exception, await _workContext.GetCurrentCustomerAsync());
return exception.Message;
}
}
/// <summary>
/// Move message template tokens to transactional attributes
/// </summary>
/// <param name="tokens">List of available message templates tokens</param>
/// <returns>
/// A task that represents the asynchronous operation
/// The task result contains the errors if exist
/// </returns>
public async Task<string> PrepareTransactionalAttributesAsync(IList<string> tokens)
{
try
{
//create API client
var client = await CreateApiClientAsync(config => new ContactsApi(config));
//get already existing transactional attributes
var attributes = await client.GetAttributesAsync();
var transactionalAttributes = attributes.Attributes
.Where(attribute => attribute.Category == CategoryEnum.Transactional).ToList();
//bring tokens to attributes format
tokens = tokens.Select(token => token.Replace("%", "").Replace(".", "_").Replace("(s)", "-s-").ToUpperInvariant()).ToList();
//get attributes that are not already on account
tokens = tokens.Except(transactionalAttributes.Select(attribute => attribute.Name)).ToList();
if (!tokens.Any())
return string.Empty;
//prepare attributes to create
var newAttributes = new List<(CategoryEnum category, string Name, string Value, CreateAttribute.TypeEnum? Type)>();
foreach (var token in tokens)
{
newAttributes.Add((CategoryEnum.Transactional, token, null, CreateAttribute.TypeEnum.Text));
}
return await CreateAttributesAsync(newAttributes);
}
catch (Exception exception)
{
//log full error
await _logger.ErrorAsync($"Brevo error: {exception.Message}.", exception, await _workContext.GetCurrentCustomerAsync());
return exception.Message;
}
}
#endregion
#region SMTP
/// <summary>
/// Check whether SMTP is enabled on account
/// </summary>
/// <returns>
/// A task that represents the asynchronous operation
/// The task result contains the result of check; errors if exist
/// </returns>
public async Task<(bool Enabled, string Errors)> SmtpIsEnabledAsync()
{
try
{
//create API client
var client = await CreateApiClientAsync(config => new AccountApi(config));
//get account
var account = await client.GetAccountAsync();
return (account.Relay?.Enabled ?? false, null);
}
catch (Exception exception)
{
//log full error
await _logger.ErrorAsync($"Brevo error: {exception.Message}.", exception, await _workContext.GetCurrentCustomerAsync());
return (false, exception.Message);
}
}
/// <summary>
/// Get email account identifier
/// </summary>
/// <param name="senderId">Sender identifier</param>
/// <param name="smtpKey">SMTP key</param>
/// <returns>
/// A task that represents the asynchronous operation
/// The task result contains the email account identifier; errors if exist
/// </returns>
public async Task<(int Id, string Errors)> GetEmailAccountIdAsync(string senderId, string smtpKey)
{
try
{
//create API clients
var sendersClient = await CreateApiClientAsync(config => new SendersApi(config));
var accountClient = await CreateApiClientAsync(config => new AccountApi(config));
//get all available senders
var senders = await sendersClient.GetSendersAsync();
if (!senders.Senders.Any())
return (0, "There are no senders");
var currentSender = senders.Senders.FirstOrDefault(sender => sender.Id.ToString() == senderId);
if (currentSender != null)
{
//try to find existing email account by name and email
var emailAccount = (await _emailAccountService.GetAllEmailAccountsAsync())
.FirstOrDefault(account => account.DisplayName == currentSender.Name && account.Email == currentSender.Email);
if (emailAccount != null)
return (emailAccount.Id, null);
}
//or create new one
currentSender ??= senders.Senders.FirstOrDefault();
var relay = (await accountClient.GetAccountAsync()).Relay;
var newEmailAccount = new EmailAccount
{
Host = relay?.Data?.Relay,
Port = relay?.Data?.Port ?? 0,
Username = relay?.Data?.UserName,
Password = smtpKey,
EnableSsl = true,
Email = currentSender.Email,
DisplayName = currentSender.Name
};
await _emailAccountService.InsertEmailAccountAsync(newEmailAccount);
return (newEmailAccount.Id, null);
}
catch (Exception exception)
{
//log full error
await _logger.ErrorAsync($"Brevo error: {exception.Message}.", exception, await _workContext.GetCurrentCustomerAsync());
return (0, exception.Message);
}
}
/// <summary>
/// Get email template identifier
/// </summary>
/// <param name="templateId">Current email template id</param>
/// <param name="message">Message template</param>
/// <param name="emailAccount">Email account</param>
/// <returns>
/// A task that represents the asynchronous operation
/// The task result contains the email template identifier
/// </returns>
public async Task<int?> GetTemplateIdAsync(int? templateId, MessageTemplate message, EmailAccount emailAccount)
{
try
{
//create API client
var client = await CreateApiClientAsync(config => new TransactionalEmailsApi(config));
//check whether email template already exists
if (templateId > 0)
{
await client.GetSmtpTemplateAsync(templateId);
return templateId;
}
//or create new one
if (emailAccount == null)
throw new NopException("Email account not configured");
//the original body and subject of the email template are the same as that of the message template
var body = message.Body.Replace("%if", "\"if\"").Replace("endif%", "\"endif\"");
body = SpecialCharRegex().Replace(body, x => $"{{{{ params.{x.ToString().Replace("%", "").Replace(".", "_").ToUpperInvariant()} }}}}");
var subject = message.Subject.Replace("%if", "\"if\"").Replace("endif%", "\"endif\"");
subject = SpecialCharRegex().Replace(subject, x => $"{{{{ params.{x.ToString().Replace("%", "").Replace(".", "_").ToUpperInvariant()} }}}}");
//create email template
var createSmtpTemplate = new CreateSmtpTemplate(sender: new CreateSmtpTemplateSender(emailAccount.DisplayName, emailAccount.Email),
templateName: message.Name, htmlContent: body, subject: subject, isActive: true);
var emailTemplate = await client.CreateSmtpTemplateAsync(createSmtpTemplate);
return (int?)emailTemplate.Id;
}
catch (Exception exception)
{
//log full error
await _logger.ErrorAsync($"Brevo error: {exception.Message}.", exception, await _workContext.GetCurrentCustomerAsync());
return null;
}
}
/// <summary>
/// Convert Brevo email template to queued email
/// </summary>
/// <param name="templateId">Email template identifier</param>
/// <returns>
/// A task that represents the asynchronous operation
/// The task result contains the queued email
/// </returns>
public async Task<QueuedEmail> GetQueuedEmailFromTemplateAsync(int templateId)
{
try
{
//create API client
var client = await CreateApiClientAsync(config => new TransactionalEmailsApi(config));
if (templateId == 0)
throw new NopException("Message template is empty");
//get template
var template = await client.GetSmtpTemplateAsync(templateId);
//bring attributes to tokens format
var subject = ParamsRegex().Replace(template.Subject, x => $"%{x.ToString().Replace("{", "").Replace("}", "").Replace("params.", "").Replace("_", ".").Trim()}%");
subject = subject.Replace("\"if\"", "%if").Replace("\"endif\"", "endif%");
var body = ParamsRegex().Replace(template.HtmlContent, x => $"%{x.ToString().Replace("{", "").Replace("}", "").Replace("params.", "").Replace("_", ".").Trim()}%");
body = body.Replace("\"if\"", "%if").Replace("\"endif\"", "endif%");
//map template to queued email
return new QueuedEmail
{
Subject = subject,
Body = body,
FromName = template.Sender?.Name,
From = template.Sender?.Email
};
}
catch (Exception exception)
{
//log full error
await _logger.ErrorAsync($"Brevo email sending error: {exception.Message}.", exception, await _workContext.GetCurrentCustomerAsync());
return null;
}
}
#endregion
#region SMS
/// <summary>
/// Send SMS
/// </summary>
/// <param name="to">Phone number of the receiver</param>
/// <param name="from">Name of sender</param>
/// <param name="text">Text</param>
/// <returns>A task that represents the asynchronous operation</returns>
public async System.Threading.Tasks.Task SendSMSAsync(string to, string from, string text)
{
//whether SMS notifications enabled
var brevoSettings = await _settingService.LoadSettingAsync<BrevoSettings>();
if (!brevoSettings.UseSmsNotifications)
return;
try
{
//check number and text
if (string.IsNullOrEmpty(to) || string.IsNullOrEmpty(text))
throw new NopException("Phone number or SMS text is empty");
//create API client
var client = await CreateApiClientAsync(config => new TransactionalSMSApi(config));
//create SMS data
var transactionalSms = new SendTransacSms(sender: from, recipient: to, content: text, type: SendTransacSms.TypeEnum.Transactional);
//send SMS
var sms = await client.SendTransacSmsAsync(transactionalSms);
await _logger.InformationAsync($"Brevo SMS sent: {sms?.Reference ?? $"credits remaining {sms?.RemainingCredits?.ToString()}"}");
}
catch (Exception exception)
{
//log full error
await _logger.ErrorAsync($"Brevo SMS sending error: {exception.Message}.", exception, await _workContext.GetCurrentCustomerAsync());
}
}
/// <summary>
/// Send SMS campaign
/// </summary>
/// <param name="listId">Contact list identifier</param>
/// <param name="from">Name of sender</param>
/// <param name="text">Text</param>
/// <returns>A task that represents the asynchronous operation</returns>
public async Task<string> SendSMSCampaignAsync(int listId, string from, string text)
{
try
{
//check list and text
if (listId == 0 || string.IsNullOrEmpty(text) || string.IsNullOrEmpty(from))
throw new NopException("List or SMS text or sender name is empty");
//create API client
var client = await CreateApiClientAsync(config => new SMSCampaignsApi(config));
//create SMS campaign
var campaign = await client.CreateSmsCampaignAsync(new CreateSmsCampaign(name: CommonHelper.EnsureMaximumLength(text, 20),
sender: from, content: text, recipients: new CreateSmsCampaignRecipients([listId])));
//send campaign
await client.SendSmsCampaignNowAsync(campaign.Id);
}
catch (Exception exception)
{
//log full error
await _logger.ErrorAsync($"Brevo SMS sending error: {exception.Message}.", exception, await _workContext.GetCurrentCustomerAsync());
return exception.Message;
}
return string.Empty;
}
#endregion
#endregion
}