From 484ae2539b4497b14cd398d677c93afec77501ba Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Fri, 2 Feb 2018 15:00:47 -0700 Subject: [PATCH 1/5] added akismet support --- .eslintignore | 46 +++--- .gitignore | 1 + graph/index.js | 2 +- plugins/talk-plugin-akismet/.eslintrc.json | 3 + .../talk-plugin-akismet/client/.eslintrc.json | 3 + .../client/components/CheckSpamHook.js | 54 +++++++ .../client/components/SpamDetail.js | 20 +++ .../client/components/SpamLabel.js | 9 ++ .../client/containers/CheckSpamHook.js | 8 + .../client/containers/SpamCommentDetail.js | 12 ++ .../containers/SpamCommentFlagDetail.js | 12 ++ .../client/containers/SpamDetail.js | 21 +++ .../client/containers/SpamLabel.js | 22 +++ plugins/talk-plugin-akismet/client/index.js | 15 ++ .../client/translations.yml | 15 ++ plugins/talk-plugin-akismet/client/utils.js | 6 + plugins/talk-plugin-akismet/config.js | 12 ++ plugins/talk-plugin-akismet/errors.js | 13 ++ plugins/talk-plugin-akismet/index.js | 123 +++++++++++++++ plugins/talk-plugin-akismet/package.json | 12 ++ plugins/talk-plugin-akismet/yarn.lock | 141 ++++++++++++++++++ 21 files changed, 527 insertions(+), 23 deletions(-) create mode 100644 plugins/talk-plugin-akismet/.eslintrc.json create mode 100644 plugins/talk-plugin-akismet/client/.eslintrc.json create mode 100644 plugins/talk-plugin-akismet/client/components/CheckSpamHook.js create mode 100644 plugins/talk-plugin-akismet/client/components/SpamDetail.js create mode 100644 plugins/talk-plugin-akismet/client/components/SpamLabel.js create mode 100644 plugins/talk-plugin-akismet/client/containers/CheckSpamHook.js create mode 100644 plugins/talk-plugin-akismet/client/containers/SpamCommentDetail.js create mode 100644 plugins/talk-plugin-akismet/client/containers/SpamCommentFlagDetail.js create mode 100644 plugins/talk-plugin-akismet/client/containers/SpamDetail.js create mode 100644 plugins/talk-plugin-akismet/client/containers/SpamLabel.js create mode 100644 plugins/talk-plugin-akismet/client/index.js create mode 100644 plugins/talk-plugin-akismet/client/translations.yml create mode 100644 plugins/talk-plugin-akismet/client/utils.js create mode 100644 plugins/talk-plugin-akismet/config.js create mode 100644 plugins/talk-plugin-akismet/errors.js create mode 100644 plugins/talk-plugin-akismet/index.js create mode 100644 plugins/talk-plugin-akismet/package.json create mode 100644 plugins/talk-plugin-akismet/yarn.lock diff --git a/.eslintignore b/.eslintignore index 7ee579147..50d3f7109 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,32 +1,34 @@ +**/*.html dist docs -**/*.html +node_modules plugins/* -!plugins/talk-plugin-facebook-auth +public + +!plugins/talk-plugin-akismet !plugins/talk-plugin-auth -!plugins/talk-plugin-respect -!plugins/talk-plugin-offtopic -!plugins/talk-plugin-like -!plugins/talk-plugin-mod -!plugins/talk-plugin-love -!plugins/talk-plugin-viewing-options +!plugins/talk-plugin-author-menu !plugins/talk-plugin-comment-content -!plugins/talk-plugin-permalink +!plugins/talk-plugin-deep-reply-count +!plugins/talk-plugin-facebook-auth !plugins/talk-plugin-featured-comments -!plugins/talk-plugin-sort-newest -!plugins/talk-plugin-sort-oldest -!plugins/talk-plugin-sort-most-replied +!plugins/talk-plugin-flag-details +!plugins/talk-plugin-ignore-user +!plugins/talk-plugin-like +!plugins/talk-plugin-love +!plugins/talk-plugin-member-since +!plugins/talk-plugin-mod +!plugins/talk-plugin-moderation-actions +!plugins/talk-plugin-offtopic +!plugins/talk-plugin-permalink +!plugins/talk-plugin-remember-sort +!plugins/talk-plugin-respect !plugins/talk-plugin-sort-most-liked !plugins/talk-plugin-sort-most-loved +!plugins/talk-plugin-sort-most-replied !plugins/talk-plugin-sort-most-respected -!plugins/talk-plugin-author-menu -!plugins/talk-plugin-member-since -!plugins/talk-plugin-ignore-user -!plugins/talk-plugin-moderation-actions -!plugins/talk-plugin-toxic-comments -!plugins/talk-plugin-remember-sort -!plugins/talk-plugin-deep-reply-count +!plugins/talk-plugin-sort-newest +!plugins/talk-plugin-sort-oldest !plugins/talk-plugin-subscriber -!plugins/talk-plugin-flag-details -public -node_modules +!plugins/talk-plugin-toxic-comments +!plugins/talk-plugin-viewing-options \ No newline at end of file diff --git a/.gitignore b/.gitignore index 70b6d482e..a27a08f15 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,7 @@ browserstack.err plugins.json plugins/* +!plugins/talk-plugin-akismet !plugins/talk-plugin-facebook-auth !plugins/talk-plugin-auth !plugins/talk-plugin-respect diff --git a/graph/index.js b/graph/index.js index 2ccaaea11..c05d878e6 100644 --- a/graph/index.js +++ b/graph/index.js @@ -8,7 +8,7 @@ module.exports = { // Schema is created already, so just include it. schema, - // Load in the new context here, this'll create the loaders + mutators for + // Load in the new context here, this will create the loaders + mutators for // the lifespan of this request. context: new Context(req), diff --git a/plugins/talk-plugin-akismet/.eslintrc.json b/plugins/talk-plugin-akismet/.eslintrc.json new file mode 100644 index 000000000..64e91163f --- /dev/null +++ b/plugins/talk-plugin-akismet/.eslintrc.json @@ -0,0 +1,3 @@ +{ + "extends": "@coralproject/eslint-config-talk" +} \ No newline at end of file diff --git a/plugins/talk-plugin-akismet/client/.eslintrc.json b/plugins/talk-plugin-akismet/client/.eslintrc.json new file mode 100644 index 000000000..c8a6db18a --- /dev/null +++ b/plugins/talk-plugin-akismet/client/.eslintrc.json @@ -0,0 +1,3 @@ +{ + "extends": "@coralproject/eslint-config-talk/client" +} diff --git a/plugins/talk-plugin-akismet/client/components/CheckSpamHook.js b/plugins/talk-plugin-akismet/client/components/CheckSpamHook.js new file mode 100644 index 000000000..58eeac11f --- /dev/null +++ b/plugins/talk-plugin-akismet/client/components/CheckSpamHook.js @@ -0,0 +1,54 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { t } from 'plugin-api/beta/client/services'; + +/** + * CheckSpamHook adds hooks to the `commentBox` + * that handles checking a comment for spam. + */ +export default class CheckSpamHook extends React.Component { + // checked signifies if we already sent a request with the `checkSpam` set to true. + checked = false; + + componentDidMount() { + this.spamPreHook = this.props.registerHook('preSubmit', input => { + // If we haven't check the spam yet, make sure to include `checkSpam=true` in the mutation. + // Otherwise post comment without checking the spam. + if (!this.checked) { + input.checkSpam = true; + this.checked = true; + } + }); + + this.spamPostHook = this.props.registerHook('postSubmit', result => { + const actions = result.createComment.actions; + if ( + actions && + actions.some( + ({ __typename, reason }) => + __typename === 'FlagAction' && reason === 'SPAM_COMMENT' + ) + ) { + this.props.notify('error', t('talk-plugin-akismet.still_spam')); + } + + // Reset `checked` after comment was successfully posted. + this.checked = false; + }); + } + + componentWillUnmount() { + this.props.unregisterHook(this.spamPreHook); + this.props.unregisterHook(this.spamPostHook); + } + + render() { + return null; + } +} + +CheckSpamHook.propTypes = { + notify: PropTypes.func.isRequired, + registerHook: PropTypes.func.isRequired, + unregisterHook: PropTypes.func.isRequired, +}; diff --git a/plugins/talk-plugin-akismet/client/components/SpamDetail.js b/plugins/talk-plugin-akismet/client/components/SpamDetail.js new file mode 100644 index 000000000..554ffb849 --- /dev/null +++ b/plugins/talk-plugin-akismet/client/components/SpamDetail.js @@ -0,0 +1,20 @@ +import React from 'react'; +import { CommentDetail } from 'plugin-api/beta/client/components'; +import PropTypes from 'prop-types'; +import { t } from 'plugin-api/beta/client/services'; + +const SpamLabel = () => ( + +); + +SpamLabel.propTypes = { + comment: PropTypes.shape({ + actions: PropTypes.array, + spam: PropTypes.spam, + }), +}; + +export default SpamLabel; diff --git a/plugins/talk-plugin-akismet/client/components/SpamLabel.js b/plugins/talk-plugin-akismet/client/components/SpamLabel.js new file mode 100644 index 000000000..c2033ff39 --- /dev/null +++ b/plugins/talk-plugin-akismet/client/components/SpamLabel.js @@ -0,0 +1,9 @@ +import React from 'react'; +import { FlagLabel } from 'plugin-api/beta/client/components/ui'; +import { t } from 'plugin-api/beta/client/services'; + +const SpamLabel = () => ( + {t('talk-plugin-akismet.spam')} +); + +export default SpamLabel; diff --git a/plugins/talk-plugin-akismet/client/containers/CheckSpamHook.js b/plugins/talk-plugin-akismet/client/containers/CheckSpamHook.js new file mode 100644 index 000000000..f580b9632 --- /dev/null +++ b/plugins/talk-plugin-akismet/client/containers/CheckSpamHook.js @@ -0,0 +1,8 @@ +import { bindActionCreators } from 'redux'; +import { connect } from 'plugin-api/beta/client/hocs'; +import { notify } from 'plugin-api/beta/client/actions/notification'; +import CheckSpamHook from '../components/CheckSpamHook'; + +const mapDispatchToProps = dispatch => bindActionCreators({ notify }, dispatch); + +export default connect(null, mapDispatchToProps)(CheckSpamHook); diff --git a/plugins/talk-plugin-akismet/client/containers/SpamCommentDetail.js b/plugins/talk-plugin-akismet/client/containers/SpamCommentDetail.js new file mode 100644 index 000000000..a133be062 --- /dev/null +++ b/plugins/talk-plugin-akismet/client/containers/SpamCommentDetail.js @@ -0,0 +1,12 @@ +import { compose } from 'react-apollo'; +import { excludeIf } from 'plugin-api/beta/client/hocs'; +import SpamDetail from './SpamDetail'; +import { isSpam } from '../utils'; + +const enhance = compose( + excludeIf( + ({ comment: { spam, actions } }) => spam === null || isSpam(actions) + ) +); + +export default enhance(SpamDetail); diff --git a/plugins/talk-plugin-akismet/client/containers/SpamCommentFlagDetail.js b/plugins/talk-plugin-akismet/client/containers/SpamCommentFlagDetail.js new file mode 100644 index 000000000..a49fd89ca --- /dev/null +++ b/plugins/talk-plugin-akismet/client/containers/SpamCommentFlagDetail.js @@ -0,0 +1,12 @@ +import { compose } from 'react-apollo'; +import { excludeIf } from 'plugin-api/beta/client/hocs'; +import SpamDetail from './SpamDetail'; +import { isSpam } from '../utils'; + +const enhance = compose( + excludeIf( + ({ comment: { spam, actions } }) => spam === null || !isSpam(actions) + ) +); + +export default enhance(SpamDetail); diff --git a/plugins/talk-plugin-akismet/client/containers/SpamDetail.js b/plugins/talk-plugin-akismet/client/containers/SpamDetail.js new file mode 100644 index 000000000..419a3c7eb --- /dev/null +++ b/plugins/talk-plugin-akismet/client/containers/SpamDetail.js @@ -0,0 +1,21 @@ +import { compose, gql } from 'react-apollo'; +import { withFragments } from 'plugin-api/beta/client/hocs'; +import SpamDetail from '../components/SpamDetail'; + +const enhance = compose( + withFragments({ + comment: gql` + fragment TalkSpamComments_SpamDetail_Comment on Comment { + spam + actions { + __typename + ... on FlagAction { + reason + } + } + } + `, + }) +); + +export default enhance(SpamDetail); diff --git a/plugins/talk-plugin-akismet/client/containers/SpamLabel.js b/plugins/talk-plugin-akismet/client/containers/SpamLabel.js new file mode 100644 index 000000000..7af7f9f49 --- /dev/null +++ b/plugins/talk-plugin-akismet/client/containers/SpamLabel.js @@ -0,0 +1,22 @@ +import { compose, gql } from 'react-apollo'; +import { withFragments, excludeIf } from 'plugin-api/beta/client/hocs'; +import SpamLabel from '../components/SpamLabel'; +import { isSpam } from '../utils'; + +const enhance = compose( + withFragments({ + comment: gql` + fragment TalkSpamComments_SpamLabel_Comment on Comment { + actions { + __typename + ... on FlagAction { + reason + } + } + } + `, + }), + excludeIf(({ comment: { actions } }) => !isSpam(actions)) +); + +export default enhance(SpamLabel); diff --git a/plugins/talk-plugin-akismet/client/index.js b/plugins/talk-plugin-akismet/client/index.js new file mode 100644 index 000000000..0eca7ab85 --- /dev/null +++ b/plugins/talk-plugin-akismet/client/index.js @@ -0,0 +1,15 @@ +import translations from './translations.yml'; +import CheckSpamHook from './containers/CheckSpamHook'; +import SpamLabel from './containers/SpamLabel'; +import SpamCommentDetail from './containers/SpamCommentDetail'; +import SpamCommentFlagDetail from './containers/SpamCommentFlagDetail'; + +export default { + translations, + slots: { + commentInputDetailArea: [CheckSpamHook], + adminCommentLabels: [SpamLabel], + adminCommentMoreDetails: [SpamCommentDetail], + adminCommentMoreFlagDetails: [SpamCommentFlagDetail], + }, +}; diff --git a/plugins/talk-plugin-akismet/client/translations.yml b/plugins/talk-plugin-akismet/client/translations.yml new file mode 100644 index 000000000..a9a209139 --- /dev/null +++ b/plugins/talk-plugin-akismet/client/translations.yml @@ -0,0 +1,15 @@ +en: + error: + COMMENT_IS_SPAM: | + Are you sure? The language in this comment matches looks like spam. + You can edit the comment or submit it for moderator review. + talk-plugin-akismet: + spam: "Spam" + spam_comment: "Detected Spam" + still_spam: | + This edited comment might still violate our community guidelines. + Our moderation team will review your comment shortly. + flags: + reasons: + comment: + spam_comment: "Detected Spam" \ No newline at end of file diff --git a/plugins/talk-plugin-akismet/client/utils.js b/plugins/talk-plugin-akismet/client/utils.js new file mode 100644 index 000000000..1fcee5d3d --- /dev/null +++ b/plugins/talk-plugin-akismet/client/utils.js @@ -0,0 +1,6 @@ +export function isSpam(actions) { + return actions.some( + action => + action.__typename === 'FlagAction' && action.reason === 'SPAM_COMMENT' + ); +} diff --git a/plugins/talk-plugin-akismet/config.js b/plugins/talk-plugin-akismet/config.js new file mode 100644 index 000000000..dfe528b5f --- /dev/null +++ b/plugins/talk-plugin-akismet/config.js @@ -0,0 +1,12 @@ +const config = { + KEY: process.env.TALK_AKISMET_API_KEY, + SITE: process.env.TALK_AKISMET_SITE, +}; + +if (process.env.NODE_ENV !== 'test' && (!config.KEY || !config.SITE)) { + throw new Error( + 'Please set the TALK_AKISMET_API_KEY and TALK_AKISMET_SITE environment variable to use talk-plugin-akismet-comments. Visit https://akismet.com/ to get started.' + ); +} + +module.exports = config; diff --git a/plugins/talk-plugin-akismet/errors.js b/plugins/talk-plugin-akismet/errors.js new file mode 100644 index 000000000..b93d178b9 --- /dev/null +++ b/plugins/talk-plugin-akismet/errors.js @@ -0,0 +1,13 @@ +const { APIError } = require('errors'); + +// ErrSpam is sent during a `CreateComment` mutation where +// `input.checkSpam` is set to true and the comment contains +// detected spam as determined by the akismet service. +const ErrSpam = new APIError('Comment is spam', { + status: 400, + translation_key: 'COMMENT_IS_SPAM', +}); + +module.exports = { + ErrSpam, +}; diff --git a/plugins/talk-plugin-akismet/index.js b/plugins/talk-plugin-akismet/index.js new file mode 100644 index 000000000..a368da505 --- /dev/null +++ b/plugins/talk-plugin-akismet/index.js @@ -0,0 +1,123 @@ +const debug = require('debug')('talk:plugin:akismet'); +const { ErrSpam } = require('./errors'); +const akismet = require('akismet-api'); +const { get, merge } = require('lodash'); +const { KEY, SITE } = require('./config'); +const client = akismet.client({ + key: KEY, + blog: SITE, +}); + +let enabled = true; +// let enabled = false; +// client.verifyKey((err, valid) => { +// if (err) { +// throw err; +// } + +// if (valid) { +// enabled = true; +// } else { +// throw new Error('Akismet key is invalid'); +// } +// }); + +module.exports = { + typeDefs: ` + input CreateCommentInput { + + # If true, the mutation will fail when the + # body contains detected spam. + checkSpam: Boolean + } + + type Comment { + spam: Boolean + } + `, + hooks: { + RootMutation: { + createComment: { + async pre(_, { input }, { loaders, parent: req }) { + // If the key validation failed, then we can't run with the client. + if (!enabled) { + debug('not enabled, passing'); + return; + } + + let spam = false; + try { + const user_ip = get(req, 'ip', false); + if (!user_ip) { + debug('no ip on request'); + return; + } + + // Get some headers from the request. + const user_agent = req.get('User-Agent'); + if (!user_agent || user_agent.length === 0) { + debug('no user agent on request'); + return; + } + + const referrer = req.get('Referrer'); + if (!referrer || referrer.length === 0) { + debug('no referrer on request'); + return; + } + + // Get the Asset that the comment is being made against. + const asset = await loaders.Assets.getByID.load(input.asset_id); + if (!asset) { + debug('asset not found for new comment'); + return; + } + + // Send off the comment to Akismet to check to see what they say. + spam = await client.checkSpam({ + user_ip, + user_agent, + referrer, + permalink: asset.url, + comment_type: 'comment', + comment_content: input.body, + is_test: true, + }); + + debug(`comment analyzed as ${spam ? 'being' : 'not being'} spam`); + } catch (err) { + console.trace(err); + return; + } + + // Attach scores to metadata. + input.metadata = merge({}, input.metadata || {}, { + akismet: spam, + }); + + if (spam) { + if (input.checkSpam) { + throw ErrSpam; + } + + // Attach reason information for the flag being added. + input.status = 'SYSTEM_WITHHELD'; + input.actions = + input.actions && input.actions.length >= 0 ? input.actions : []; + input.actions.push({ + action_type: 'FLAG', + user_id: null, + group_id: 'SPAM_COMMENT', + metadata: {}, + }); + } + }, + }, + }, + }, + resolvers: { + Comment: { + spam: comment => get(comment, 'metadata.akismet', null), + }, + }, +}; diff --git a/plugins/talk-plugin-akismet/package.json b/plugins/talk-plugin-akismet/package.json new file mode 100644 index 000000000..04df3c8eb --- /dev/null +++ b/plugins/talk-plugin-akismet/package.json @@ -0,0 +1,12 @@ +{ + "name": "@coralproject/talk-plugin-akismet", + "pluginName": "talk-plugin-akismet", + "version": "0.0.1", + "description": "Provides support for preventing spam with the Akismet API", + "main": "index.js", + "author": "The Coral Project Team ", + "license": "Apache-2.0", + "dependencies": { + "akismet-api": "^4.0.1" + } +} diff --git a/plugins/talk-plugin-akismet/yarn.lock b/plugins/talk-plugin-akismet/yarn.lock new file mode 100644 index 000000000..ac97e590f --- /dev/null +++ b/plugins/talk-plugin-akismet/yarn.lock @@ -0,0 +1,141 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +akismet-api@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/akismet-api/-/akismet-api-4.0.1.tgz#1c771442f09316847132aa16171bb4fb708b6519" + dependencies: + bluebird "^3.1.1" + superagent "^3.8.0" + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + +bluebird@^3.1.1: + version "3.5.1" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9" + +combined-stream@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.5.tgz#938370a57b4a51dea2c77c15d5c5fdf895164009" + dependencies: + delayed-stream "~1.0.0" + +component-emitter@^1.2.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" + +cookiejar@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.1.tgz#41ad57b1b555951ec171412a81942b1e8200d34a" + +core-util-is@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + +debug@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" + dependencies: + ms "2.0.0" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + +extend@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444" + +form-data@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.1.tgz#6fb94fbd71885306d73d15cc497fe4cc4ecd44bf" + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.5" + mime-types "^2.1.12" + +formidable@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/formidable/-/formidable-1.1.1.tgz#96b8886f7c3c3508b932d6bd70c4d3a88f35f1a9" + +inherits@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + +isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + +methods@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + +mime-db@~1.30.0: + version "1.30.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.30.0.tgz#74c643da2dd9d6a45399963465b26d5ca7d71f01" + +mime-types@^2.1.12: + version "2.1.17" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.17.tgz#09d7a393f03e995a79f8af857b70a9e0ab16557a" + dependencies: + mime-db "~1.30.0" + +mime@^1.4.1: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + +process-nextick-args@~1.0.6: + version "1.0.7" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" + +qs@^6.5.1: + version "6.5.1" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.1.tgz#349cdf6eef89ec45c12d7d5eb3fc0c870343a6d8" + +readable-stream@^2.0.5: + version "2.3.3" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.3.tgz#368f2512d79f9d46fdfc71349ae7878bbc1eb95c" + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~1.0.6" + safe-buffer "~5.1.1" + string_decoder "~1.0.3" + util-deprecate "~1.0.1" + +safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" + +string_decoder@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.3.tgz#0fc67d7c141825de94282dd536bec6b9bce860ab" + dependencies: + safe-buffer "~5.1.0" + +superagent@^3.8.0: + version "3.8.2" + resolved "https://registry.yarnpkg.com/superagent/-/superagent-3.8.2.tgz#e4a11b9d047f7d3efeb3bbe536d9ec0021d16403" + dependencies: + component-emitter "^1.2.0" + cookiejar "^2.1.0" + debug "^3.1.0" + extend "^3.0.0" + form-data "^2.3.1" + formidable "^1.1.1" + methods "^1.1.1" + mime "^1.4.1" + qs "^6.5.1" + readable-stream "^2.0.5" + +util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" From db322d192f69883f42770b927758a86435314593 Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Tue, 6 Feb 2018 09:58:02 -0700 Subject: [PATCH 2/5] added note --- plugins/talk-plugin-akismet/index.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/talk-plugin-akismet/index.js b/plugins/talk-plugin-akismet/index.js index a368da505..4b0e7f997 100644 --- a/plugins/talk-plugin-akismet/index.js +++ b/plugins/talk-plugin-akismet/index.js @@ -9,6 +9,9 @@ const client = akismet.client({ }); let enabled = true; + +// TODO: when using a developer key, this is possible, the plus plan does not +// allow us to check the key. // let enabled = false; // client.verifyKey((err, valid) => { // if (err) { From 6cb3fc7be8d8a42880ea6db1c73f7ed2a4cfc7fe Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Tue, 6 Feb 2018 10:23:26 -0700 Subject: [PATCH 3/5] added docs for akismet --- docs/_docs/04-03-additional-plugins.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/docs/_docs/04-03-additional-plugins.md b/docs/_docs/04-03-additional-plugins.md index b9f4d4067..8fe72dd01 100644 --- a/docs/_docs/04-03-additional-plugins.md +++ b/docs/_docs/04-03-additional-plugins.md @@ -103,3 +103,16 @@ Source: [plugins/talk-plugin-subscriber](https://github.com/coralproject/talk/tr Enables a `Subscriber` badge to be added to comments where the author has the `SUBSCRIBER` tag. This must match with a custom auth integration that adds the tag to the users that are subscribed to the service. + +## talk-plugin-akismet + +Source: [plugins/talk-plugin-akismet](https://github.com/coralproject/talk/tree/master/plugins/talk-plugin-akismet){:target="_blank"} + +Enables spam detection from [Akismet](https://akismet.com/). Comments will be passed to the Akismet API for spam detection. If a comment +is determined to be spam, it will prompt the user, indicating that the comment might be considered spam. If the user continues after this +point with the still spam-like comment, the comment will be reported as containing spam, and sent for moderator approval. + +Configuration: + +- `TALK_AKISMET_API_KEY` (**required**) - The Akismet API key located on your account page +- `TALK_AKISMET_SITE` (**required**) - The URL for your site that the comment are appearing on (not the root url, the url for the articles) \ No newline at end of file From f4d520fbaacc5b679ec7dd4bb136248ed90492c2 Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Tue, 6 Feb 2018 13:44:28 -0700 Subject: [PATCH 4/5] adjusted translations --- .../client/components/SpamDetail.js | 1 + plugins/talk-plugin-akismet/client/translations.yml | 10 +++++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/plugins/talk-plugin-akismet/client/components/SpamDetail.js b/plugins/talk-plugin-akismet/client/components/SpamDetail.js index 554ffb849..dafdb5a86 100644 --- a/plugins/talk-plugin-akismet/client/components/SpamDetail.js +++ b/plugins/talk-plugin-akismet/client/components/SpamDetail.js @@ -7,6 +7,7 @@ const SpamLabel = () => ( ); diff --git a/plugins/talk-plugin-akismet/client/translations.yml b/plugins/talk-plugin-akismet/client/translations.yml index a9a209139..af7217831 100644 --- a/plugins/talk-plugin-akismet/client/translations.yml +++ b/plugins/talk-plugin-akismet/client/translations.yml @@ -1,14 +1,14 @@ en: error: COMMENT_IS_SPAM: | - Are you sure? The language in this comment matches looks like spam. - You can edit the comment or submit it for moderator review. + The language in this comment looks like spam. You can + edit the comment or submit it anyway for moderator review. talk-plugin-akismet: spam: "Spam" - spam_comment: "Detected Spam" + spam_comment: "Spam" + detected: "Detected by Akismet" still_spam: | - This edited comment might still violate our community guidelines. - Our moderation team will review your comment shortly. + Thank you. Our moderation team will review your comment shortly. flags: reasons: comment: From 17fefcb4bdee784fea255ddad4b17450cc609775 Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Tue, 6 Feb 2018 13:47:57 -0700 Subject: [PATCH 5/5] adjusted docs --- docs/_docs/04-03-additional-plugins.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/_docs/04-03-additional-plugins.md b/docs/_docs/04-03-additional-plugins.md index 8fe72dd01..3d342fad7 100644 --- a/docs/_docs/04-03-additional-plugins.md +++ b/docs/_docs/04-03-additional-plugins.md @@ -112,6 +112,8 @@ Enables spam detection from [Akismet](https://akismet.com/). Comments will be pa is determined to be spam, it will prompt the user, indicating that the comment might be considered spam. If the user continues after this point with the still spam-like comment, the comment will be reported as containing spam, and sent for moderator approval. +**Note: [Akismet](https://akismet.com/) is a premium service, charges may apply.** + Configuration: - `TALK_AKISMET_API_KEY` (**required**) - The Akismet API key located on your account page