Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for Spam Filtering Using Akismet. #599. #614

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions source/DasBlog All.sln
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DasBlog.Test.Integration",
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DasBlog.CLI", "DasBlog.CLI\DasBlog.CLI.csproj", "{3DEF6C9E-293A-4E41-9CEC-3466E3EEC754}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Subtext.Akismet", "Subtext.Akismet\Subtext.Akismet.csproj", "{3937987F-789E-4AB9-BAC8-8773ADCC5ED9}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug - All|Any CPU = Debug - All|Any CPU
Expand Down
14 changes: 13 additions & 1 deletion source/DasBlog.Services/ConfigFile/SiteConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,19 @@ public SiteConfig() { }
public string SpamBlockingServiceApiKey { get; set; }

[XmlIgnore]
public ISpamBlockingService SpamBlockingService { get; set; }
public ISpamBlockingService SpamBlockingService
{
get
{
//TODO: this may eventually be configurable, if Akismet alternatives show up
if (! EnableSpamBlockingService|| SpamBlockingServiceApiKey.Length == 0)
{
return null;
}
return new AkismetSpamBlockingService(this.SpamBlockingServiceApiKey, this.Root);
}
set {}
}
public bool EnableSpamModeration { get; set; }
public int EntriesPerPage { get; set; }
public bool EnableDailyReportEmail { get; set; }
Expand Down
1 change: 1 addition & 0 deletions source/DasBlog.Services/DasBlog.Services.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\DasBlog.Web.Core\DasBlog.Core.csproj" />
<ProjectReference Include="..\Subtext.Akismet\Subtext.Akismet.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Kveer.XmlRPC" Version="1.2.0" />
Expand Down
77 changes: 77 additions & 0 deletions source/DasBlog.Services/SpamBlocking/AkismetSpamBlockingService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
using System;
using newtelligence.DasBlog.Runtime;
using Subtext.Akismet;
using AkismetComment = Subtext.Akismet.Comment;

namespace DasBlog.Services
{
/// <summary>
/// Uses the Akismet service to determine if a comment is SPAM
/// </summary>
/// <remarks>http://akismet.com/</remarks>
public class AkismetSpamBlockingService : ISpamBlockingService
{
AkismetClient akismetClient;

public AkismetSpamBlockingService(string apiKey, string blogUrl)
{
akismetClient = new AkismetClient(apiKey, new Uri(blogUrl));
}

public bool IsSpam(IFeedback feedback)
{
IComment akismetFormattedComment = ConvertToAkismetComment(feedback);
return akismetClient.CheckCommentForSpam(akismetFormattedComment);
}

public void ReportSpam(IFeedback feedback)
{
IComment akismetFormattedComment = ConvertToAkismetComment(feedback);
akismetClient.SubmitSpam(akismetFormattedComment);
}

public void ReportNotSpam(IFeedback feedback)
{
IComment akismetFormattedComment = ConvertToAkismetComment(feedback);
akismetClient.SubmitHam(akismetFormattedComment);
}

private AkismetComment ConvertToAkismetComment(IFeedback feedback)
{
System.Net.IPAddress ipAddress = System.Net.IPAddress.None;
if (feedback.AuthorIPAddress != null)
{
try
{
ipAddress = System.Net.IPAddress.Parse(feedback.AuthorIPAddress);
}
catch(FormatException){}
}
AkismetComment comment = new AkismetComment(ipAddress, feedback.AuthorUserAgent);
comment.Author = feedback.Author;
comment.AuthorEmail = feedback.AuthorEmail;
if (feedback.AuthorHomepage != null && feedback.AuthorHomepage.Length > 0)
{
try
{
comment.AuthorUrl = new Uri(feedback.AuthorHomepage);
}
catch(UriFormatException){}
}
comment.Content = feedback.Content;
comment.Referer = feedback.Referer;
if (feedback.TargetEntryId != null & feedback.TargetEntryId.Trim().Length > 0)
{
try
{
//TODO: comeback
//comment.Permalink = new Uri(SiteUtilities.GetPermaLinkUrl(feedback.TargetEntryId));
}
catch(UriFormatException){}
}
comment.CommentType = feedback.FeedbackType;
return comment;
}

}
}
48 changes: 41 additions & 7 deletions source/DasBlog.Web.UI/Controllers/BlogPostController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
using System.Linq;
using System.Net;
using reCAPTCHA.AspNetCore;
using DasBlog.Services.ConfigFile.Interfaces;

namespace DasBlog.Web.Controllers
{
Expand Down Expand Up @@ -434,18 +435,29 @@ public IActionResult AddComment(AddCommentViewModel addcomment)
}
}

if (errors.Count > 0)
{
return CommentError(addcomment, errors);
}

var commt = mapper.Map<NBR.Comment>(addcomment);
commt.AuthorIPAddress = HttpContext.Connection.RemoteIpAddress.ToString();
commt.AuthorUserAgent = HttpContext.Request.Headers["User-Agent"].ToString();
commt.EntryId = Guid.NewGuid().ToString();
commt.IsPublic = !dasBlogSettings.SiteConfiguration.CommentsRequireApproval;
commt.CreatedUtc = commt.ModifiedUtc = DateTime.Now.ToUniversalTime();

if (dasBlogSettings.SiteConfiguration.EnableSpamBlockingService)
{
commt = CheckForSpam(commt, dasBlogSettings.SiteConfiguration);
// Spam Moderation is Disabled and the comment is spam. Let's show an error!
// TODO: Discuss what are the pros and cons of showing error vs just silently deleting the
// comment.
if (!dasBlogSettings.SiteConfiguration.EnableSpamModeration && commt.SpamState == NBR.SpamState.Spam)
{
errors.Add("Spam Comment Detected. Please enter a legitimate comment that is not spam to post it.");
}
}

if (errors.Count > 0)
{
return CommentError(addcomment, errors);
}

logger.LogInformation(new EventDataItem(EventCodes.CommentAdded, null, "Comment CONTENT DUMP", commt.Content));

var state = blogManager.AddComment(addcomment.TargetEntryId, commt);
Expand Down Expand Up @@ -484,6 +496,28 @@ public IActionResult AddComment(AddCommentViewModel addcomment)
return Comment(addcomment.TargetEntryId);
}

private NBR.Comment CheckForSpam(NBR.Comment commt, ISiteConfig siteConfiguration)
{
try
{
if (siteConfiguration.SpamBlockingService.IsSpam(commt))
{
commt.SpamState = NBR.SpamState.Spam;
commt.IsPublic = false;
}
else
{
commt.SpamState = NBR.SpamState.NotSpam;
commt.IsPublic = true;
}
}
catch (Exception ex)
{
logger.LogError(new EventDataItem(EventCodes.Error, null, String.Format("The external spam blocking service failed for comment {0}. Original exception: {1}", commt.EntryId, ex)));
}
return commt;
}

[HttpDelete("post/{postid:guid}/comments/{commentid:guid}")]
public IActionResult DeleteComment(Guid postid, Guid commentid)
{
Expand Down Expand Up @@ -586,7 +620,7 @@ private IActionResult HandleNewCategory(PostViewModel post)
var newCategory = post.NewCategory?.Trim();
var newCategoryDisplayName = newCategory;
var newCategoryUrl = NBR.Entry.InternalCompressTitle(newCategory);
// Category names should not include special characters #200
// Category names should not include special characters #200
if (post.AllCategories.Any(c => c.CategoryUrl == newCategoryUrl))
{
ModelState.AddModelError(nameof(post.NewCategory), $"The category, {post.NewCategory}, already exists");
Expand Down
10 changes: 10 additions & 0 deletions source/DasBlog.Web.UI/Models/AdminViewModels/SiteViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -355,9 +355,19 @@ public class SiteViewModel
public string CommentsGravatarRating { get; set; }
public bool CommentsAllowHtml { get; set; }
public bool EnableCoComment { get; set; }

[DisplayName("Enable Spam Blocking Service")]
[Description("Enable Akismet Spam Blocking Service.")]
public bool EnableSpamBlockingService { get; set; }

[DisplayName("Akismet API Key")]
[Description("API Key for Spam Blocking Service")]
public string SpamBlockingServiceApiKey { get; set; }
//public ISpamBlockingService SpamBlockingService { get; set; }


[DisplayName("Enable Spam Moderation")]
[Description("Allow Manual Moderation of Spam")]
public bool EnableSpamModeration { get; set; }
public bool EnableDailyReportEmail { get; set; }
public bool EnableGoogleMaps { get; set; }
Expand Down
34 changes: 30 additions & 4 deletions source/DasBlog.Web.UI/Views/Admin/Settings.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,36 @@

</div>


<div class="form-check row mb-3">
<div class="col-sm-10 offset-sm-2">
<div class="col-sm-2">
@Html.CheckBoxFor(m => @Model.SiteConfig.EnableSpamBlockingService, new { @class = "form-check-input form-check-input" })
</div>
@Html.LabelFor(m => @Model.SiteConfig.EnableSpamBlockingService, null, new { @class = "col-check-label col-sm-10" })
</div>
</div>

<div class="form-check row mb-3">
<div class="col-sm-10 offset-sm-2">
<div class="col-sm-2">
@Html.CheckBoxFor(m => @Model.SiteConfig.EnableSpamModeration, new { @class = "form-check-input form-check-input" })
</div>
@Html.LabelFor(m => @Model.SiteConfig.EnableSpamModeration, null, new { @class = "col-check-label col-sm-10" })
</div>
</div>

<div class="form-group row mb-3">

@Html.LabelFor(m => @Model.SiteConfig.SpamBlockingServiceApiKey, null, new { @class = "col-form-label col-sm-2" })
<div class="col-sm-10">
@Html.TextBoxFor(m => @Model.SiteConfig.SpamBlockingServiceApiKey, null, new { @class = "form-control sm-10" })
</div>
@Html.ValidationMessageFor(m => m.SiteConfig.SpamBlockingServiceApiKey, null, new { @class = "text-danger" })

</div>


<div class="form-check row mb-3">
<div class="col-sm-10 offset-sm-2">
<div class="col-sm-2">
Expand Down Expand Up @@ -779,10 +809,6 @@
@Html.HiddenFor(@m => m.SiteConfig.CommentsGravatarRating)
@Html.HiddenFor(@m => m.SiteConfig.EnableCoComment)

@Html.HiddenFor(@m => m.SiteConfig.EnableSpamBlockingService)
@Html.HiddenFor(@m => m.SiteConfig.SpamBlockingServiceApiKey)
@Html.HiddenFor(@m => m.SiteConfig.EnableSpamModeration)

@Html.HiddenFor(@m => m.SiteConfig.EnableDailyReportEmail)

@Html.HiddenFor(@m => m.SiteConfig.EnableGoogleMaps)
Expand Down
Loading