diff --git a/README.md b/README.md index ad5960f..4699c88 100644 --- a/README.md +++ b/README.md @@ -28,11 +28,12 @@ I wanted to see what it took to make a module with full Manager support, includi In other words: You might have use for this module even if antispam/comments are not of interest - if you just want to understand how Manager support can be added to a custom Piranha module. There's no claim that this is the right way to do it, however. # Dependencies +* `.NET 8` * `Zon3.SpamDetector.Localization` -* `Microsoft.Extensions.Http` version 6 -* `Microsoft.Extensions.Localization` version 6 -* `Piranha` version 10 -* `Piranha.Manager` version 10 +* `Microsoft.Extensions.Http` version 8 +* `Microsoft.Extensions.Localization` version 8 +* `Piranha` version 11 +* `Piranha.Manager` version 11 * Also: An Akismet API key # Demo diff --git a/Zon3.SpamDetector.Localization/Zon3.SpamDetector.Localization.csproj b/Zon3.SpamDetector.Localization/Zon3.SpamDetector.Localization.csproj index e76a03a..f36100f 100644 --- a/Zon3.SpamDetector.Localization/Zon3.SpamDetector.Localization.csproj +++ b/Zon3.SpamDetector.Localization/Zon3.SpamDetector.Localization.csproj @@ -1,16 +1,16 @@  - net6.0 + net8.0 enable enable Zon3.SpamDetector.Localization Zon3.SpamDetector.Localization - 1.4.0 + 1.5.0 Jens Bråkenhielm - Copyright 2020-2022 (c) Jens Bråkenhielm + Copyright 2020-2024 (c) Jens Bråkenhielm Piranha Module detecting comment spam using Akismet true MIT @@ -26,7 +26,7 @@ - + diff --git a/Zon3.SpamDetector/AkismetSpamDetectorService.cs b/Zon3.SpamDetector/AkismetSpamDetectorService.cs index b35070f..538a803 100644 --- a/Zon3.SpamDetector/AkismetSpamDetectorService.cs +++ b/Zon3.SpamDetector/AkismetSpamDetectorService.cs @@ -13,34 +13,45 @@ namespace Zon3.SpamDetector { public class AkismetSpamDetectorService : SpamDetectorService { + private readonly string _piranhaVersion; + private readonly string _pluginVersion; + /// /// /// The Akismet anti-spam service implementation of the core functionality. /// Composes a request to Akismet with as much relevant information as possible provided and /// translates a response from Akismet to a review result in form of a . /// - public AkismetSpamDetectorService(IApi piranha, SpamDetectorConfigService config, IHttpClientFactory httpClientFactory, ILoggerFactory loggerFactory) : base(piranha, config, httpClientFactory, loggerFactory) + public AkismetSpamDetectorService(IApi api, SpamDetectorConfigService config, IHttpClientFactory httpClientFactory, ILoggerFactory loggerFactory) : base(api, config, httpClientFactory, loggerFactory) { - // Intentionally left empty + _piranhaVersion = Utils.GetAssemblyVersion(typeof(App).Assembly); + _pluginVersion = Utils.GetAssemblyVersion(typeof(SpamDetectorModule).Assembly); } /// protected override async Task GetSpamRequestMessageAsync(Comment comment) { - CommentId = comment.Id; + var userAgent = $"Piranha CMS/{_piranhaVersion} | SpamDetector/{_pluginVersion}"; - var post = await Piranha.Posts.GetByIdAsync(comment.ContentId); - var page = await Piranha.Pages.GetByIdAsync(post?.BlogId ?? comment.ContentId); - var permalink = post != null ? post.Permalink : page.Permalink; - var permalinkFull = $"{ConfigModel.SiteUrl}{permalink}"; + string permalink; + var post = await Api.Posts.GetByIdAsync(comment.ContentId); + if (post != null) + { + permalink = $"{ConfigModel.SiteUrl}{post.Permalink}"; + } + else + { + var page = await Api.Pages.GetByIdAsync(post?.BlogId ?? comment.ContentId); + permalink = $"{ConfigModel.SiteUrl}{page.Permalink}"; + } var parameters = new Dictionary { {"blog", HttpUtility.UrlEncode(ConfigModel.SiteUrl)}, {"user_ip", HttpUtility.UrlEncode(comment.IpAddress)}, - {"user_agent", HttpUtility.UrlEncode(comment.UserAgent)}, + {"user_agent", HttpUtility.UrlEncode(userAgent)}, // Ignore comment.UserAgent and use Akismet preferred format {"referrer", HttpUtility.UrlEncode(string.Empty)}, // ??? - {"permalink", HttpUtility.UrlEncode(permalinkFull)}, + {"permalink", HttpUtility.UrlEncode(permalink)}, {"comment_type", HttpUtility.UrlEncode("comment")}, {"comment_author", HttpUtility.UrlEncode(comment.Author ?? string.Empty)}, {"comment_author_email", HttpUtility.UrlEncode(comment.Email ?? string.Empty)}, diff --git a/Zon3.SpamDetector/Services/SpamDetectorConfigService.cs b/Zon3.SpamDetector/Services/SpamDetectorConfigService.cs index 061a423..7ce5af7 100644 --- a/Zon3.SpamDetector/Services/SpamDetectorConfigService.cs +++ b/Zon3.SpamDetector/Services/SpamDetectorConfigService.cs @@ -8,15 +8,15 @@ namespace Zon3.SpamDetector.Services /// public class SpamDetectorConfigService { - private readonly IApi _piranha; + private readonly IApi _api; /// /// Default constructor. /// - /// The Piranha API - public SpamDetectorConfigService(IApi piranha) + /// The Piranha API + public SpamDetectorConfigService(IApi api) { - _piranha = piranha; + _api = api; } /// @@ -25,7 +25,7 @@ public SpamDetectorConfigService(IApi piranha) /// The SpamDetectorService model public SpamDetectorConfigModel Get() { - using var config = new SpamDetectorConfig(_piranha); + using var config = new SpamDetectorConfig(_api); return new SpamDetectorConfigModel { Enabled = config.Enabled, @@ -44,7 +44,7 @@ public SpamDetectorConfigModel Get() /// The SpamDetectorService model public void Save(SpamDetectorConfigModel configModel) { - using var config = new SpamDetectorConfig(_piranha) + using var config = new SpamDetectorConfig(_api) { Enabled = configModel.Enabled, IsTest = configModel.IsTest, diff --git a/Zon3.SpamDetector/SpamDetectorConfig.cs b/Zon3.SpamDetector/SpamDetectorConfig.cs index 76302b5..5c2c9f0 100644 --- a/Zon3.SpamDetector/SpamDetectorConfig.cs +++ b/Zon3.SpamDetector/SpamDetectorConfig.cs @@ -18,6 +18,14 @@ public class SpamDetectorConfig : IDisposable public static readonly string KeySiteEncoding = $"{KeyPrefix}SiteEncoding"; public static readonly string KeyUserRole = $"{KeyPrefix}UserRole"; + private static readonly bool DefaultValueEnabled = false; + private static readonly bool DefaultValueIsTest = true; + private static readonly string DefaultValueSpamApiUrl = ""; + private static readonly string DefaultValueSiteUrl = ""; + private static readonly string DefaultValueSiteLanguage = "en-US"; + private static readonly string DefaultValueSiteEncoding = "UTF8"; + private static readonly string DefaultValueUserRole = "guest"; + public SpamDetectorConfig(IParamService paramService) { _service = paramService; @@ -33,7 +41,7 @@ public bool Enabled get { var param = _service.GetByKeyAsync(KeyEnabled).GetAwaiter().GetResult(); - return param == null || Convert.ToBoolean(param.Value); + return param == null ? DefaultValueEnabled : Convert.ToBoolean(param.Value); } set { @@ -52,7 +60,7 @@ public bool IsTest get { var param = _service.GetByKeyAsync(KeyIsTest).GetAwaiter().GetResult(); - return param == null || Convert.ToBoolean(param.Value); + return param == null ? DefaultValueIsTest : Convert.ToBoolean(param.Value); } set { @@ -71,7 +79,7 @@ public string SpamApiUrl get { var param = _service.GetByKeyAsync(KeySpamApiUrl).GetAwaiter().GetResult(); - return param?.Value; + return param == null ? DefaultValueSpamApiUrl : param.Value; } set { @@ -90,7 +98,7 @@ public string SiteUrl get { var param = _service.GetByKeyAsync(KeySiteUrl).GetAwaiter().GetResult(); - return param?.Value; + return param == null ? DefaultValueSiteUrl : param.Value; } set { @@ -109,7 +117,7 @@ public string SiteLanguage get { var param = _service.GetByKeyAsync(KeySiteLanguage).GetAwaiter().GetResult(); - return param == null ? "en-US" : param.Value; + return param == null ? DefaultValueSiteLanguage : param.Value; } set { @@ -128,7 +136,7 @@ public string SiteEncoding get { var param = _service.GetByKeyAsync(KeySiteEncoding).GetAwaiter().GetResult(); - return param == null ? "UTF8" : param.Value; + return param == null ? DefaultValueSiteEncoding : param.Value; } set { @@ -147,7 +155,7 @@ public string UserRole get { var param = _service.GetByKeyAsync(KeyUserRole).GetAwaiter().GetResult(); - return param == null ? "guest" : param.Value; + return param == null ? DefaultValueUserRole: param.Value; } set { diff --git a/Zon3.SpamDetector/SpamDetectorService.cs b/Zon3.SpamDetector/SpamDetectorService.cs index b2671bc..3fee79a 100644 --- a/Zon3.SpamDetector/SpamDetectorService.cs +++ b/Zon3.SpamDetector/SpamDetectorService.cs @@ -20,24 +20,23 @@ namespace Zon3.SpamDetector /// public abstract class SpamDetectorService : ISpamDetectorService { - protected IApi Piranha; + protected IApi Api; protected ILogger Logger; protected IHttpClientFactory HttpClientFactory; protected SpamDetectorConfigModel ConfigModel; - protected Guid CommentId; public bool Enabled => ConfigModel.Enabled; /// /// The default constructor. /// - /// The Piranha API + /// The Piranha API /// The configuration to use /// The factory to use to get http client for requests /// The factory to use to get a logger - protected SpamDetectorService(IApi piranha, SpamDetectorConfigService config, IHttpClientFactory httpClientFactory, ILoggerFactory loggerFactory) + protected SpamDetectorService(IApi api, SpamDetectorConfigService config, IHttpClientFactory httpClientFactory, ILoggerFactory loggerFactory) { - Piranha = piranha; + Api = api; HttpClientFactory = httpClientFactory; ConfigModel = config.Get(); @@ -48,24 +47,34 @@ protected SpamDetectorService(IApi piranha, SpamDetectorConfigService config, IH if (!ConfigModel.Enabled) { - Logger.LogWarning("Module disabled by configuration: comments will not be reviewed/changed"); + Logger.LogWarning("Module disabled: spam detection will not be performed"); + } + + if (ConfigModel.IsTest) + { + Logger.LogWarning("Module in test mode: spam detection results may be affected"); } if (string.IsNullOrEmpty(ConfigModel.SpamApiUrl)) { - const string msg = "Module not configured properly, mandatory value missing: SpamApiUrl"; + const string msg = "Module configuration error, mandatory value not set: SpamApiUrl"; Logger.LogError(msg); throw new InvalidOperationException(msg); } if (string.IsNullOrEmpty(ConfigModel.SiteUrl)) { - Logger.LogWarning("Option SiteUrl is missing: results may be wrong"); - } - - if (ConfigModel.IsTest) - { - Logger.LogWarning("Option IsTest is true: no live requests will be made"); + const string msg = "Module configuration error, mandatory value not set: SiteUrl"; + if (ConfigModel.IsTest) + { + // Mandatory but in test mode so just warn + Logger.LogWarning(msg); + } + else + { + Logger.LogError(msg); + throw new InvalidOperationException(msg); + } } } @@ -76,20 +85,18 @@ protected SpamDetectorService(IApi piranha, SpamDetectorConfigService config, IH /// Interpreted and selected information about the result of the review public async Task ReviewAsync(Comment comment) { - CommentId = comment.Id; - if (!Enabled) { return new CommentReview() { Approved = comment.IsApproved }; } - Logger.LogDebug($"Composing API request for comment '{CommentId}' by '{comment.Email}'"); + Logger.LogDebug($"Composing API request for comment '{comment.Id}' by '{comment.Email}'"); var requestMessage = await GetSpamRequestMessageAsync(comment); - Logger.LogDebug("Sending API request for comment '{1}' from IP '{2}'...", CommentId, comment.IpAddress); + Logger.LogDebug("Sending API request for comment '{1}' from IP '{2}'...", comment.Id, comment.IpAddress); var response = await HttpClientFactory.CreateClient().SendAsync(requestMessage); - Logger.LogDebug("Received API response for comment '{1}' = '{2}'", CommentId, response.Content); + Logger.LogDebug("Received API response for comment '{1}' = '{2}'", comment.Id, response.Content); response.EnsureSuccessStatusCode(); - Logger.LogDebug("Interpreting API response for comment '{1}' = '{2}'", CommentId, requestMessage); + Logger.LogDebug("Interpreting API response for comment '{1}' = '{2}'", comment.Id, requestMessage); var review = await GetCommentReviewFromResponse(response); return review; diff --git a/Zon3.SpamDetector/Zon3.SpamDetector.csproj b/Zon3.SpamDetector/Zon3.SpamDetector.csproj index 4217294..cbe452d 100644 --- a/Zon3.SpamDetector/Zon3.SpamDetector.csproj +++ b/Zon3.SpamDetector/Zon3.SpamDetector.csproj @@ -1,15 +1,15 @@  - net6.0 + net8.0 true Zon3.SpamDetector Zon3.SpamDetector - 1.4.0 + 1.5.0 Jens Bråkenhielm - Copyright 2020-2022 (c) Jens Bråkenhielm + Copyright 2020-2024 (c) Jens Bråkenhielm Piranha Module detecting comment spam using Akismet true MIT @@ -28,10 +28,10 @@ - - - - + + + +