custom

Spam comments are annoying and sometimes looking at comment allowed by Akismet or some other anti-spam service you think – I’m sure I would do better. It is just so hard to get the plumbing in or I would made my very own solution. With BlogEngine.Net 1.6 it is not, you can easily implement your own anti-spam filter or add existing if you like it more than built-in Akismet. Here is how.

First, open BE 1.6 solution in VS or VWD or whichever tool you are using to work with source code in .NET. Create new class in the App_Code folder, I called mine SpamBlocker. This class should implement ICustomFilter interface, it has just 4 members and looks like this:

namespace BlogEngine.Core
{
    public interface ICustomFilter
    {
        bool Initialize();
        bool Check(Comment comment);
        void Report(Comment comment);
        bool FallThrough { get; }
    }
}

Below is very simple implementation. It based on idea that most reliable way to identify spam is to check if comment contains link to site selling products or services and known for hiring spammers to push their ads all over the internet.

using System.Collections.Specialized;
using BlogEngine.Core;

public class SpamBlocker : ICustomFilter
{
    private static bool _passThrough = true;

    static StringCollection MockService()
    {
        StringCollection spammers = new StringCollection();

        spammers.Add("conference-call-providers.co.uk");
        spammers.Add("telebisyonserye.info");
        spammers.Add("qmw.com.au");

        return spammers;
    }

    public bool Initialize()
    {
        if(MockService().Count > 0) return true;
        return false;
    }

    public bool Check(Comment comment)
    {
        bool spam = false;

        foreach (string s in MockService())
        {
            if(comment.Content.Contains(s))
            {
                spam = true;
                break;
            }
        }

        _passThrough = (spam) ? false : true;
        return spam;
    }

    public void Report(Comment comment)
    {
        if(comment.IsApproved)
        {
            foreach (string s in MockService())
            {
                if(comment.Content.Contains(s))
                {
                    MockService().Remove(s);
                }
            }
        }
    }

    public bool FallThrough
    {
        get { return _passThrough; }
    }
}

Mock service has few sites that spammers promote. Obviously, you would have to put some more work here for real deal, but for now it will do. All we need is a way to identify spam. For the sake of this example, if comment mentions any of these sites – it is spam.

Now lets go over each method and explore what it does.

Initialize() – here you can verify that service is running, do authentication or any other kind of required verification. If return true, that means you are ready to handle comments. We just check if we have any spam sites in the list, if we do – good to go.

Check(Comment comment) – this is where you do your custom thing checking if comment is spam. BlogEngine will pass comment to this method, so you can examine all it’s properties. For simplicity, we check if comment has reference to any of spam sites in the list. If it has, we return “true” meaning that comment is spam and set fall through to false (more on it later).

Report(Comment comment) – this is a way to provide feedback to the service, so that you can make it smarter and identify spam better. If blogger restores comment marked by SpamBlocker as spam, we remove it from the list of spam sites. In the real world, here you can examine comment and try to figure out why you did a mistake marking it as spam or verifying that it is not spam and try to put more smarts in your service/filter.

FallThrough – blog might run several custom filters, and we need a way for them to play well together. When fall through set to true, you saying “I think it is spam/not spam, but I’m ok with others to check on it and provide second opinion”. In this case, last judge wins – so the order in which you set filters to run (priority) is important.

filters spamfolder

Here we pretty sure that when comment has spam site in it – it is spam. So we set fall through to false, telling BlogEngine that, in this case, we want to be the ultimate judge and don’t pass comment to other custom services down the list.

If you go and make a comment with one of those sites in the comment body, it will be rejected by SpamBlocker. So here you have it – your very own custom anti-spam filter in 60 lines of code. Ok, it’ll be much more lines when you done with custom logic to identify spam, and you might even implement web service and make it available to other BlogEngine users for this. But handle it on BlogEngien side is a snap.

One more thing, if you want blogger to be able turn your service on and off, you can easily do so by implementing it as extension, just the way Akismet is implemented in BlogEngine. Then, blogger can turn it off without removing it from the web site all together. I hope you’ll try it for yourself, and good luck fighting those nasty spam comments.

Share/Save/Bookmark
Signature

Comments

2/6/2010 5:02:32 PM #

Ben Amada

This is a good writeup ... thanks!

Ben Amada | Reply

2/7/2010 7:05:51 AM #

Cristiano

Ruslan,
every time I change the properties of a commentary (text, author, email) from the backend , appears this error:

Date: 04/02/2010 22.46.21
Contents Below
Url : http://www.cristianofino.net/admin/Comments/Editor.aspx?id=7c373011-0602-4a8c-b33c-093b58b5cda2
Raw Url : /BlogEngine.NET 1.6/admin/Comments/Editor.aspx?id=7c373011-0602-4a8c-b33c-093b58b5cda2
Message : Valore potenzialmente pericoloso Request.Form rilevato dal client (txtArea="questo è un mio commen...").
Source : System.Web
StackTrace :    in System.Web.HttpRequest.ValidateString(String s, String valueName, String collectionName)
   in System.Web.HttpRequest.ValidateNameValueCollection(NameValueCollection nvc, String collectionName)
   in System.Web.HttpRequest.get_Form()
   in System.Web.UI.Page.GetCollectionBasedOnMethod(Boolean dontReturnNull)
   in System.Web.UI.Page.DeterminePostBackMode()
   in System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)
   in System.Web.UI.Page.ProcessRequest(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)
   in System.Web.UI.Page.ProcessRequest()
   in System.Web.UI.Page.ProcessRequestWithNoAssert(HttpContext context)
   in System.Web.UI.Page.ProcessRequest(HttpContext context)
   in ASP.admin_comments_editor_aspx.ProcessRequest(HttpContext context) in c:\Users\Cristiano\AppData\Local\Temp\Temporary ASP.NET Files\blogengine.net 1.6\bb4ab85a\49028c8e\App_Web_8bh6r85z.2.cs:riga 0
   in System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
   in System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)
TargetSite : Void ValidateString(System.String, System.String, System.String)


Any suggestion ?

Cristiano | Reply

2/7/2010 10:02:10 AM #

rtur.net

Looks same as this one: http://blogengine.codeplex.com/Thread/View.aspx?ThreadId=83345
You need to turn off request validation in the page header.

rtur.net | Reply

2/7/2010 11:59:22 AM #

Cristiano

Thanks a lot, Ruslan
now it works fine but looking at the error log I saw that this other error occurred during the submit of a comment in some post.
This is a example:
Date: 05/02/2010 13.55.38
Contents Below
Url : http://www.cristianofino.net/post.aspx?id=236e159d-023e-48d1-8936-0175672e7785
Raw Url : /post/Aggiornamento-alla-versione-131-per-la-Comment-Toolbar-extension-per-BlogEngineNET-13.aspx
Message : Convalida di viewstate MAC non riuscita. Se questa applicazione è ospitata in una Web farm o in un cluster, verificare che la configurazione <machineKey> specifichi lo stesso validationKey e algoritmo di convalida. Impossibile utilizzare AutoGenerate in un cluster.
Source : System.Web
StackTrace :    in System.Web.UI.ViewStateException.ThrowError(Exception inner, String persistedState, String errorPageMessage, Boolean macValidationError)
   in System.Web.UI.ViewStateException.ThrowMacValidationError(Exception inner, String persistedState)
   in System.Web.UI.ObjectStateFormatter.Deserialize(String inputString)
   in System.Web.UI.ObjectStateFormatter.System.Web.UI.IStateFormatter.Deserialize(String serializedState)
   in System.Web.UI.Util.DeserializeWithAssert(IStateFormatter formatter, String serializedState)
   in System.Web.UI.HiddenFieldPageStatePersister.Load()
   in System.Web.UI.Page.LoadPageStateFromPersistenceMedium()
   in System.Web.UI.Page.LoadAllState()
   in System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)
   in System.Web.UI.Page.ProcessRequest(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)
   in System.Web.UI.Page.ProcessRequest()
   in System.Web.UI.Page.ProcessRequestWithNoAssert(HttpContext context)
   in System.Web.UI.Page.ProcessRequest(HttpContext context)
   in ASP.post_aspx.ProcessRequest(HttpContext context)
   in System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
   in System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)
TargetSite : Void ThrowError(System.Exception, System.String, System.String, Boolean)
Message : Viewstate non valido.
  Client IP: 95.68.64.248
  Port: 2462
  User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; FunWebProducts)
  ViewState: /wEPDwUKMjA2MjE2ODY1OA9kFgJmD2QWAgIID2QWAmYPZBYEAgMPFgIeB1Zpc2libGVnFgQCAQ8PFgYeC05hdmlnYXRlVXJsBUkvcG9zdC9Db21lLXBvc2l6aW9uYXJlLWNvcnJldHRhbWVudGUtbGUtRW1vdGljb25zLWluLXVuYS1wYWdpbmEtSFRNTC5hc3B4HgRUZXh0BUcmbHQ7Jmx0OyBDb21lIHBvc2l6aW9uYXJlIGNvcnJldHRhbWVudGUgbGUgRW1vdGljb25zIGluIHVuYSBwYWdpbmEgSFRNTB4HVG9vbFRpcAUPUG9zdCBwcmVjZWRlbnRlZGQCAg8PFgYfAQVSL3Bvc3QvUmlsYXNjaWF0YS1sYS12ZXJzaW9uZS0xMS1kaS1Ub3AtQ29tbWVudGVycy1leHRlbnNpb24tcGVy
  Referer: http://www.cristianofino.net/post/Aggiornamento-alla-versione-131-per-la-Comment-Toolbar-extension-per-BlogEngineNET-13.aspx
  Path: /post.aspx
Source :
StackTrace :
TargetSite :
Message : Impossibile convalidare i dati.
Source : System.Web
StackTrace :    in System.Web.Configuration.MachineKeySection.GetDecodedData(Byte[] buf, Byte[] modifier, Int32 start, Int32 length, Int32& dataLength)
   in System.Web.UI.ObjectStateFormatter.Deserialize(String inputString)
TargetSite : Byte[] GetDecodedData(Byte[], Byte[], Int32, Int32, Int32 ByRef)


viewstate not valid ? Why ? It had never happened before :-(

Cristiano | Reply

3/10/2010 1:24:23 AM #

Marc

Nicelly written! Specially the fall through information is of good help for me.

Marc | Reply

Add comment

Enter your name, handle, alias, or email.

We'll incarnate your avatar from the services below.

(Will show your Gravatar icon)

  Country flag

biuquoteimgred
  • Comment
  • Preview
Loading



<<  March 2010  >>
SuMoTuWeThFrSa
28123456
78910111213
14151617181920
21222324252627
28293031123
45678910
Enhanced with Snapshots

Subscribe to Rtur.net