Skip to content

Commit 9e7047e

Browse files
skoshelevRomanovM
authored andcommitted
nopSolutions#7964 Automated SEO optimization with AI tools for any entity
1 parent b815a4d commit 9e7047e

4 files changed

Lines changed: 241 additions & 60 deletions

File tree

src/Libraries/Nop.Services/ArtificialIntelligence/ArtificialIntelligenceService.cs

Lines changed: 110 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
77
using Nop.Core.Domain.Catalog;
88
using Nop.Core.Domain.Common;
99
using Nop.Core.Domain.Localization;
10-
using Nop.Core.Domain.News;
1110
using Nop.Core.Domain.Topics;
1211
using Nop.Core.Domain.Vendors;
12+
using Nop.Core.Events;
1313
using Nop.Services.Localization;
1414
using Nop.Services.Logging;
1515

@@ -24,6 +24,7 @@ public partial class ArtificialIntelligenceService : IArtificialIntelligenceServ
2424

2525
protected readonly ArtificialIntelligenceHttpClient _httpClient;
2626
protected readonly ArtificialIntelligenceSettings _artificialIntelligenceSettings;
27+
protected readonly IEventPublisher _eventPublisher;
2728
protected readonly ILanguageService _languageService;
2829
protected readonly ILocalizationService _localizationService;
2930
protected readonly ILogger _logger;
@@ -36,6 +37,7 @@ public partial class ArtificialIntelligenceService : IArtificialIntelligenceServ
3637

3738
public ArtificialIntelligenceService(ArtificialIntelligenceHttpClient httpClient,
3839
ArtificialIntelligenceSettings artificialIntelligenceSettings,
40+
IEventPublisher eventPublisher,
3941
ILanguageService languageService,
4042
ILocalizationService localizationService,
4143
ILogger logger,
@@ -44,6 +46,7 @@ public ArtificialIntelligenceService(ArtificialIntelligenceHttpClient httpClient
4446
{
4547
_httpClient = httpClient;
4648
_artificialIntelligenceSettings = artificialIntelligenceSettings;
49+
_eventPublisher = eventPublisher;
4750
_languageService = languageService;
4851
_localizationService = localizationService;
4952
_logger = logger;
@@ -135,76 +138,27 @@ protected virtual async Task<string> GetTonOfVoiceInstructionAsync(ToneOfVoiceTy
135138
return await ValidateTitleAndTextAsync(languageName, textRequiredLocale, titleRequiredLocale, title, text);
136139
}
137140

138-
/// <summary>
139-
/// Validates title and text
140-
/// </summary>
141-
/// <param name="languageName">Target language name</param>
142-
/// <param name="textRequiredLocale">Locale for raising an exception about the title field required</param>
143-
/// <param name="titleRequiredLocale">Locale for raising an exception about the text field required</param>
144-
/// <param name="title">Title for validate</param>
145-
/// <param name="text">Text for validate</param>
146-
/// <returns>
147-
/// A task that represents the asynchronous operation
148-
/// The task result contains the validated title and text
149-
/// </returns>
150-
protected virtual async Task<(string title, string text)> ValidateTitleAndTextAsync(string languageName,
151-
string textRequiredLocale, string titleRequiredLocale, string title, string text)
152-
{
153-
if (string.IsNullOrEmpty(title))
154-
throw new NopException(string.Format(await _localizationService.GetResourceAsync(titleRequiredLocale), languageName));
155-
156-
if (!string.IsNullOrEmpty(text))
157-
text = Regex.Replace(text, "<.*?>", string.Empty);
158-
159-
if (string.IsNullOrEmpty(text))
160-
throw new NopException(string.Format(await _localizationService.GetResourceAsync(textRequiredLocale), languageName));
161-
162-
return (title, text);
163-
}
164-
165141
/// <summary>
166142
/// Create meta tags by artificial intelligence
167143
/// </summary>
168-
/// <param name="entity">The entity to which need to generate meta tags</param>
169144
/// <param name="currentMetaTitle">Current entity meta title</param>
170145
/// <param name="currentMetaKeywords">Current entity meta keywords</param>
171146
/// <param name="currentMetaDescription">Current entity meta description</param>
147+
/// <param name="title">The main title for entity</param>
148+
/// <param name="text">The main text for entity</param>
172149
/// <param name="languageId">Target language identifier</param>
173150
/// <returns>
174151
/// A task that represents the asynchronous operation
175152
/// The task result contains the generated meta tags
176153
/// </returns>
177-
protected virtual async Task<(string metaTitle, string metaKeywords, string metaDescription)> CreateMetaTagsAsync<TEntity>(TEntity entity,
178-
string currentMetaTitle, string currentMetaKeywords, string currentMetaDescription, int languageId)
179-
where TEntity : BaseEntity, IMetaTagsSupported
154+
protected virtual async Task<(string metaTitle, string metaKeywords, string metaDescription)> CreateMetaTagsAsync(string currentMetaTitle, string currentMetaKeywords, string currentMetaDescription, string title, string text, int languageId)
180155
{
181-
var title = string.Empty;
182-
var text = string.Empty;
183-
var metaTitle = string.Empty;
184-
var metaKeywords = string.Empty;
185-
var metaDescription = string.Empty;
156+
string metaTitle;
157+
string metaKeywords;
158+
string metaDescription;
186159

187160
var currentLanguage = await _languageService.GetLanguageByIdAsync(languageId != 0 ? languageId : _localizationSettings.DefaultAdminLanguageId);
188161

189-
(title, text) = entity switch
190-
{
191-
Product product => await GetTitleAndTextAsync(product, languageId, currentLanguage.Name, p => p.Name, p => p.FullDescription,
192-
"Admin.ArtificialIntelligence.ProductDescriptionRequired", "Admin.ArtificialIntelligence.ProductNameRequired"),
193-
Category category => await GetTitleAndTextAsync(category, languageId, currentLanguage.Name, c => c.Name, c => c.Description,
194-
"Admin.ArtificialIntelligence.CategoryDescriptionRequired", "Admin.ArtificialIntelligence.CategoryNameRequired"),
195-
BlogPost blogPost => await GetTitleAndTextAsync(blogPost, currentLanguage.Name, bp => bp.Title, bp => bp.Body,
196-
"Admin.ArtificialIntelligence.BlogPostBodyRequired", "Admin.ArtificialIntelligence.BlogPostTitleRequired"),
197-
Manufacturer manufacturer => await GetTitleAndTextAsync(manufacturer, languageId, currentLanguage.Name, m => m.Name, m => m.Description,
198-
"Admin.ArtificialIntelligence.ManufacturerDescriptionRequired", "Admin.ArtificialIntelligence.ManufacturerNameRequired"),
199-
NewsItem newsItem => await GetTitleAndTextAsync(newsItem, currentLanguage.Name, n => n.Title, n => n.Full,
200-
"Admin.ArtificialIntelligence.NewsItemFullRequired", "Admin.ArtificialIntelligence.NewsItemTitleRequired"),
201-
Topic topic => await GetTitleAndTextAsync(topic, languageId, currentLanguage.Name, t => t.Title, t => t.Body,
202-
"Admin.ArtificialIntelligence.TopicBodyRequired", "Admin.ArtificialIntelligence.TopicTitleRequired"),
203-
Vendor vendor => await GetTitleAndTextAsync(vendor, languageId, currentLanguage.Name, v => v.Name, v => v.Description,
204-
"Admin.ArtificialIntelligence.VendorDescriptionRequired", "Admin.ArtificialIntelligence.VendorNameRequired"),
205-
_ => (title, text)
206-
};
207-
208162
try
209163
{
210164
if (_artificialIntelligenceSettings.AllowMetaTitleGeneration && string.IsNullOrEmpty(currentMetaTitle))
@@ -260,10 +214,75 @@ protected virtual async Task<string> GetTonOfVoiceInstructionAsync(ToneOfVoiceTy
260214
return (metaTitle, metaKeywords, metaDescription);
261215
}
262216

217+
/// <summary>
218+
/// Create meta tags by artificial intelligence
219+
/// </summary>
220+
/// <param name="entity">The entity to which need to generate meta tags</param>
221+
/// <param name="currentMetaTitle">Current entity meta title</param>
222+
/// <param name="currentMetaKeywords">Current entity meta keywords</param>
223+
/// <param name="currentMetaDescription">Current entity meta description</param>
224+
/// <param name="languageId">Target language identifier</param>
225+
/// <returns>
226+
/// A task that represents the asynchronous operation
227+
/// The task result contains the generated meta tags
228+
/// </returns>
229+
protected virtual async Task<(string metaTitle, string metaKeywords, string metaDescription)> CreateMetaTagsAsync<TEntity>(TEntity entity,
230+
string currentMetaTitle, string currentMetaKeywords, string currentMetaDescription, int languageId)
231+
where TEntity : BaseEntity, IMetaTagsSupported
232+
{
233+
var currentLanguage = await _languageService.GetLanguageByIdAsync(languageId != 0 ? languageId : _localizationSettings.DefaultAdminLanguageId);
234+
235+
var (title, text) = entity switch
236+
{
237+
Product product => await GetTitleAndTextAsync(product, languageId, currentLanguage.Name, p => p.Name, p => p.FullDescription,
238+
"Admin.ArtificialIntelligence.ProductDescriptionRequired", "Admin.ArtificialIntelligence.ProductNameRequired"),
239+
Category category => await GetTitleAndTextAsync(category, languageId, currentLanguage.Name, c => c.Name, c => c.Description,
240+
"Admin.ArtificialIntelligence.CategoryDescriptionRequired", "Admin.ArtificialIntelligence.CategoryNameRequired"),
241+
BlogPost blogPost => await GetTitleAndTextAsync(blogPost, currentLanguage.Name, bp => bp.Title, bp => bp.Body,
242+
"Admin.ArtificialIntelligence.BlogPostBodyRequired", "Admin.ArtificialIntelligence.BlogPostTitleRequired"),
243+
Manufacturer manufacturer => await GetTitleAndTextAsync(manufacturer, languageId, currentLanguage.Name, m => m.Name, m => m.Description,
244+
"Admin.ArtificialIntelligence.ManufacturerDescriptionRequired", "Admin.ArtificialIntelligence.ManufacturerNameRequired"),
245+
Topic topic => await GetTitleAndTextAsync(topic, languageId, currentLanguage.Name, t => t.Title, t => t.Body,
246+
"Admin.ArtificialIntelligence.TopicBodyRequired", "Admin.ArtificialIntelligence.TopicTitleRequired"),
247+
Vendor vendor => await GetTitleAndTextAsync(vendor, languageId, currentLanguage.Name, v => v.Name, v => v.Description,
248+
"Admin.ArtificialIntelligence.VendorDescriptionRequired", "Admin.ArtificialIntelligence.VendorNameRequired"),
249+
_ => (string.Empty, string.Empty)
250+
};
251+
252+
return await CreateMetaTagsAsync(currentMetaTitle, currentMetaKeywords, currentMetaDescription, title, text, languageId);
253+
}
254+
263255
#endregion
264256

265257
#region Methods
266258

259+
/// <summary>
260+
/// Validates title and text
261+
/// </summary>
262+
/// <param name="languageName">Target language name</param>
263+
/// <param name="textRequiredLocale">Locale for raising an exception about the title field required</param>
264+
/// <param name="titleRequiredLocale">Locale for raising an exception about the text field required</param>
265+
/// <param name="title">Title for validate</param>
266+
/// <param name="text">Text for validate</param>
267+
/// <returns>
268+
/// A task that represents the asynchronous operation
269+
/// The task result contains the validated title and text
270+
/// </returns>
271+
public virtual async Task<(string title, string text)> ValidateTitleAndTextAsync(string languageName,
272+
string textRequiredLocale, string titleRequiredLocale, string title, string text)
273+
{
274+
if (string.IsNullOrEmpty(title))
275+
throw new NopException(string.Format(await _localizationService.GetResourceAsync(titleRequiredLocale), languageName));
276+
277+
if (!string.IsNullOrEmpty(text))
278+
text = Regex.Replace(text, "<.*?>", string.Empty);
279+
280+
if (string.IsNullOrEmpty(text))
281+
throw new NopException(string.Format(await _localizationService.GetResourceAsync(textRequiredLocale), languageName));
282+
283+
return (title, text);
284+
}
285+
267286
/// <summary>
268287
/// Create product description by artificial intelligence
269288
/// </summary>
@@ -346,5 +365,40 @@ public virtual async Task<string> CreateProductDescriptionAsync(string productNa
346365
return await CreateMetaTagsAsync(entity, metaTitle, metaKeywords, metaDescription, languageId);
347366
}
348367

368+
/// <summary>
369+
/// Create meta tags by artificial intelligence
370+
/// </summary>
371+
/// <param name="entityTypeName">The name of entity type to which need to generate meta tags</param>
372+
/// <param name="entityId">The entity identifier to which need to generate meta tags</param>
373+
/// <param name="languageId">The language identifier; leave 0 to use <see cref="LocalizationSettings.DefaultAdminLanguageId"/></param>
374+
/// <returns>
375+
/// A task that represents the asynchronous operation
376+
/// The task result contains the generated meta tags
377+
/// </returns>
378+
public virtual async Task<(string metaTitle, string metaKeywords, string metaDescription)> CreateMetaTagsAsync(string entityTypeName, int entityId, int languageId)
379+
{
380+
var currentLanguage = await _languageService.GetLanguageByIdAsync(languageId != 0 ? languageId : _localizationSettings.DefaultAdminLanguageId);
381+
382+
var metaTagsGeneratingEvent = new MetaTagsGeneratingEvent(this)
383+
{
384+
EntityTypeName = entityTypeName,
385+
EntityId = entityId,
386+
LanguageId = languageId,
387+
LanguageName = currentLanguage.Name
388+
};
389+
390+
await _eventPublisher.PublishAsync(metaTagsGeneratingEvent);
391+
392+
if (!metaTagsGeneratingEvent.StopProcessing)
393+
return (string.Empty, string.Empty, string.Empty);
394+
395+
return await CreateMetaTagsAsync(metaTagsGeneratingEvent.CurrentMetaTitle,
396+
metaTagsGeneratingEvent.CurrentMetaKeywords,
397+
metaTagsGeneratingEvent.CurrentMetaDescription,
398+
metaTagsGeneratingEvent.Title,
399+
metaTagsGeneratingEvent.Text,
400+
languageId);
401+
}
402+
349403
#endregion
350404
}

src/Libraries/Nop.Services/ArtificialIntelligence/IArtificialIntelligenceService.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,20 @@ namespace Nop.Services.ArtificialIntelligence;
1010
/// </summary>
1111
public partial interface IArtificialIntelligenceService
1212
{
13+
/// <summary>
14+
/// Validates title and text
15+
/// </summary>
16+
/// <param name="languageName">Target language name</param>
17+
/// <param name="textRequiredLocale">Locale for raising an exception about the title field required</param>
18+
/// <param name="titleRequiredLocale">Locale for raising an exception about the text field required</param>
19+
/// <param name="title">Title for validate</param>
20+
/// <param name="text">Text for validate</param>
21+
/// <returns>
22+
/// A task that represents the asynchronous operation
23+
/// The task result contains the validated title and text
24+
/// </returns>
25+
Task<(string title, string text)> ValidateTitleAndTextAsync(string languageName, string textRequiredLocale, string titleRequiredLocale, string title, string text);
26+
1327
/// <summary>
1428
/// Create product description by artificial intelligence
1529
/// </summary>
@@ -48,4 +62,16 @@ public partial interface IArtificialIntelligenceService
4862
/// </returns>
4963
Task<(string metaTitle, string metaKeywords, string metaDescription)> CreateMetaTagsAsync<TEntity>(TEntity entity, int languageId = 0)
5064
where TEntity : BaseEntity, IMetaTagsSupported;
65+
66+
/// <summary>
67+
/// Create meta tags by artificial intelligence
68+
/// </summary>
69+
/// <param name="entityTypeName">The name of entity type to which need to generate meta tags</param>
70+
/// <param name="entityId">The entity identifier to which need to generate meta tags</param>
71+
/// <param name="languageId">The language identifier; leave 0 to use <see cref="LocalizationSettings.DefaultAdminLanguageId"/></param>
72+
/// <returns>
73+
/// A task that represents the asynchronous operation
74+
/// The task result contains the generated meta tags
75+
/// </returns>
76+
Task<(string metaTitle, string metaKeywords, string metaDescription)> CreateMetaTagsAsync(string entityTypeName, int entityId, int languageId);
5177
}
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
using Nop.Core.Events;
2+
3+
namespace Nop.Services.ArtificialIntelligence;
4+
5+
/// <summary>
6+
/// Represents a meta tags generating event
7+
/// </summary>
8+
public partial class MetaTagsGeneratingEvent : IStopProcessingEvent
9+
{
10+
#region Fields
11+
12+
private readonly IArtificialIntelligenceService _artificialIntelligenceService;
13+
14+
#endregion
15+
16+
#region Ctor
17+
18+
public MetaTagsGeneratingEvent(IArtificialIntelligenceService artificialIntelligenceService)
19+
{
20+
_artificialIntelligenceService = artificialIntelligenceService;
21+
}
22+
23+
#endregion
24+
25+
#region Methods
26+
27+
/// <summary>
28+
/// Validates title and text
29+
/// </summary>
30+
/// <param name="textRequiredLocale">Locale for raising an exception about the title field required</param>
31+
/// <param name="titleRequiredLocale">Locale for raising an exception about the text field required</param>
32+
/// <param name="title">Title for validate</param>
33+
/// <param name="text">Text for validate</param>
34+
/// <returns>
35+
/// A task that represents the asynchronous operation
36+
/// The task result contains the validated title and text
37+
/// </returns>
38+
public virtual async Task<(string title, string text)> ValidateTitleAndTextAsync(string textRequiredLocale, string titleRequiredLocale, string title, string text)
39+
{
40+
return await _artificialIntelligenceService.ValidateTitleAndTextAsync(LanguageName, textRequiredLocale, titleRequiredLocale, title, text);
41+
}
42+
43+
#endregion
44+
45+
#region Properties
46+
47+
/// <summary>
48+
/// Gets or sets a value whether processing of event publishing should be stopped
49+
/// </summary>
50+
public bool StopProcessing { get; set; }
51+
52+
/// <summary>
53+
/// Gets or sets the name of entity type to which generating meta tags
54+
/// </summary>
55+
public string EntityTypeName { get; set; }
56+
57+
/// <summary>
58+
/// Gets or sets the entity identifier to which generating meta tags
59+
/// </summary>
60+
public int EntityId { get; set; }
61+
62+
/// <summary>
63+
/// Gets or sets the language identifier to which generating meta tags
64+
/// </summary>
65+
public int LanguageId { get; set; }
66+
67+
/// <summary>
68+
/// Gets or sets the language name to which generating meta tags
69+
/// </summary>
70+
public string LanguageName { get; set; }
71+
72+
/// <summary>
73+
/// Gets or sets the current meta title of entity
74+
/// </summary>
75+
public string CurrentMetaTitle { get; set; }
76+
77+
/// <summary>
78+
/// Gets or sets the current meta keywords of entity
79+
/// </summary>
80+
public string CurrentMetaKeywords { get; set; }
81+
82+
/// <summary>
83+
/// Gets or sets the Current meta description of entity
84+
/// </summary>
85+
public string CurrentMetaDescription { get; set; }
86+
87+
/// <summary>
88+
/// Gets or sets the title of entity
89+
/// </summary>
90+
public string Title { get; set; }
91+
92+
/// <summary>
93+
/// Gets or sets the main text of entity
94+
/// </summary>
95+
public string Text { get; set; }
96+
97+
#endregion
98+
}

0 commit comments

Comments
 (0)