From 9fcf67d6675013dc884034da3ab8f80a5639ed95 Mon Sep 17 00:00:00 2001 From: Adhityaa Chandrasekar Date: Wed, 19 Dec 2018 22:57:02 -0500 Subject: [PATCH] api,frontend: add Akismet spam flagging integration --- api/Gopkg.lock | 9 +++++++++ api/akismet.go | 31 +++++++++++++++++++++++++++++++ api/comment_new.go | 18 +++++++++++++----- api/config.go | 2 ++ api/page.go | 8 ++++---- api/utils_http.go | 13 +++++++++++++ frontend/js/commento.js | 29 +++++++++++++++++++---------- frontend/sass/commento.scss | 11 +++++++++++ 8 files changed, 102 insertions(+), 19 deletions(-) create mode 100644 api/akismet.go diff --git a/api/Gopkg.lock b/api/Gopkg.lock index 44ffd03..046164f 100644 --- a/api/Gopkg.lock +++ b/api/Gopkg.lock @@ -9,6 +9,14 @@ revision = "64a2037ec6be8a4b0c1d1f706ed35b428b989239" version = "v0.26.0" +[[projects]] + branch = "master" + digest = "1:9769b231d8f5ff406a012aa7f293e45ed69d11617832a1c3c7b8c6ce1558a2a1" + name = "github.com/adtac/go-akismet" + packages = ["akismet"] + pruneopts = "UT" + revision = "0ca9e1023047c869ecd4bd3c20780511597a4a77" + [[projects]] digest = "1:15042ad3498153684d09f393bbaec6b216c8eec6d61f63dff711de7d64ed8861" name = "github.com/golang/protobuf" @@ -143,6 +151,7 @@ analyzer-name = "dep" analyzer-version = 1 input-imports = [ + "github.com/adtac/go-akismet/akismet", "github.com/gorilla/handlers", "github.com/gorilla/mux", "github.com/lib/pq", diff --git a/api/akismet.go b/api/akismet.go new file mode 100644 index 0000000..7396e18 --- /dev/null +++ b/api/akismet.go @@ -0,0 +1,31 @@ +package main + +import ( + "github.com/adtac/go-akismet/akismet" + "os" +) + +func isSpam(domain string, userIp string, userAgent string, name string, email string, url string, markdown string) bool { + akismetKey := os.Getenv("AKISMET_KEY") + if akismetKey == "" { + return false + } + + res, err := akismet.Check(&akismet.Comment{ + Blog: domain, + UserIP: userIp, + UserAgent: userAgent, + CommentType: "comment", + CommentAuthor: name, + CommentAuthorEmail: email, + CommentAuthorURL: url, + CommentContent: markdown, + }, akismetKey) + + if err != nil { + logger.Errorf("error: cannot validate commenet using Akismet: %v", err) + return true + } + + return res +} diff --git a/api/comment_new.go b/api/comment_new.go index b35eb2b..c2ccfb0 100644 --- a/api/comment_new.go +++ b/api/comment_new.go @@ -89,8 +89,12 @@ func commentNewHandler(w http.ResponseWriter, r *http.Request) { var state string if *x.CommenterToken == "anonymous" { - state = "unapproved" commenterHex = "anonymous" + if isSpam(*x.Domain, getIp(r), getUserAgent(r), "Anonymous", "", "", *x.Markdown) { + state = "flagged" + } else { + state = "unapproved" + } } else { c, err := commenterGetByCommenterToken(*x.CommenterToken) if err != nil { @@ -112,10 +116,14 @@ func commentNewHandler(w http.ResponseWriter, r *http.Request) { if isModerator { state = "approved" } else { - if d.RequireModeration { - state = "unapproved" + if isSpam(*x.Domain, getIp(r), getUserAgent(r), c.Name, c.Email, c.Link, *x.Markdown) { + state = "flagged" } else { - state = "approved" + if d.RequireModeration { + state = "unapproved" + } else { + state = "approved" + } } } } @@ -126,5 +134,5 @@ func commentNewHandler(w http.ResponseWriter, r *http.Request) { return } - bodyMarshal(w, response{"success": true, "commentHex": commentHex, "approved": state == "approved"}) + bodyMarshal(w, response{"success": true, "commentHex": commentHex, "state": state}) } diff --git a/api/config.go b/api/config.go index 9ace4ab..333856f 100644 --- a/api/config.go +++ b/api/config.go @@ -43,6 +43,8 @@ func configParse() error { "SMTP_PORT": "", "SMTP_FROM_ADDRESS": "", + "AKISMET_KEY": "", + "GOOGLE_KEY": "", "GOOGLE_SECRET": "", } diff --git a/api/page.go b/api/page.go index 0a2fc92..8bdc8b1 100644 --- a/api/page.go +++ b/api/page.go @@ -3,9 +3,9 @@ package main import () type page struct { - Domain string `json:"domain"` - Path string `json:"path"` - IsLocked bool `json:"isLocked"` - CommentCount int `json:"commentCount"` + Domain string `json:"domain"` + Path string `json:"path"` + IsLocked bool `json:"isLocked"` + CommentCount int `json:"commentCount"` StickyCommentHex string `json:"stickyCommentHex"` } diff --git a/api/utils_http.go b/api/utils_http.go index 8e50158..bf332f8 100644 --- a/api/utils_http.go +++ b/api/utils_http.go @@ -43,3 +43,16 @@ func bodyMarshal(w http.ResponseWriter, x map[string]interface{}) error { w.Write(resp) return nil } + +func getIp(r *http.Request) string { + ip := r.RemoteAddr + if r.Header.Get("X-Forwarded-For") != "" { + ip = r.Header.Get("X-Forwarded-For") + } + + return ip +} + +func getUserAgent(r *http.Request) string { + return r.Header.Get("User-Agent") +} diff --git a/frontend/js/commento.js b/frontend/js/commento.js index 72b63b6..0496a55 100644 --- a/frontend/js/commento.js +++ b/frontend/js/commento.js @@ -51,6 +51,7 @@ var ID_REMOVE = "commento-comment-remove-"; var ID_STICKY = "commento-comment-sticky-"; var ID_CONTENTS = "commento-comment-contents-"; + var ID_NAME = "commento-comment-name-"; var ID_SUBMIT_BUTTON = "commento-submit-button-"; var ID_FOOTER = "commento-footer"; @@ -519,14 +520,17 @@ $(ID_COMMENTS_AREA).innerHTML = ""; commentsRender(); - if (!resp.approved) { - if (id == "root") { - var body = $(ID_SUPER_CONTAINER + id); - prepend(body, messageCreate("Your comment is under moderation.")); - } else { - var body = $(ID_BODY + id); - append(body, messageCreate("Your comment is under moderation.")); - } + var message = ""; + if (resp.state == "unapproved") + message = "Your comment is under moderation."; + else if (resp.state == "flagged") + message = "Your comment was flagged as spam and is under moderation."; + + if (message != "") { + if (id == "root") + prepend($(ID_SUPER_CONTAINER + id), messageCreate(message)); + else + append($(ID_BODY + id), messageCreate(message)); } }); }); @@ -652,6 +656,7 @@ remove.id = ID_REMOVE + comment.commentHex; sticky.id = ID_STICKY + comment.commentHex; contents.id = ID_CONTENTS + comment.commentHex; + name.id = ID_NAME + comment.commentHex; collapse.title = "Collapse"; upvote.title = "Upvote"; @@ -692,8 +697,10 @@ } classAdd(card, "card"); - if (isModerator && comment.state == "unapproved") + if (isModerator && comment.state != "approved") classAdd(card, "dark-card"); + if (comment.state == "flagged") + classAdd(name, "flagged"); classAdd(header, "header"); classAdd(name, "name"); classAdd(subtitle, "subtitle"); @@ -773,7 +780,7 @@ if (parentHex == "root") append(options, sticky); append(options, remove); - if (comment.state == "unapproved") + if (comment.state != "approved") append(options, approve); } else { @@ -823,10 +830,12 @@ } var card = $(ID_CARD + commentHex); + var name = $(ID_NAME + commentHex); var options = $(ID_OPTIONS + commentHex); var tick = $(ID_APPROVE + commentHex); classRemove(card, "dark-card"); + classRemove(name, "flagged"); remove(tick); }); } diff --git a/frontend/sass/commento.scss b/frontend/sass/commento.scss index 2f8927e..ef40905 100644 --- a/frontend/sass/commento.scss +++ b/frontend/sass/commento.scss @@ -124,6 +124,17 @@ width: fit-content; } + .commento-flagged::after { + content: "Flagged"; + text-transform: uppercase; + font-size: 10px; + background: $red-7; + color: white; + margin-left: 8px; + padding: 2px 6px 2px 6px; + border-radius: 100px; + } + .commento-subtitle { display: block; color: #999;