Automatically mute users who attempt to post a slur, fixes #8062 (#8177)

* Initial psuedo-code for checking for slurs in messages

* Initial working prototype for blocking posting of slurs. Moved check from group.js to the chat api. Still needs: to permanently revoke chat privileges, to notify the moderators, a better method for checking for the blacklisted words, and a way to get the real list of words to check.

* Permanently revoke chat privileges when attempting to post a slur.

* Removed console logs

* Fixing rebase

* Do not moderate private groups

* Moved slur check to a generic check for banned words function

* Moved list of slurs to a separate file, fixed misplacement of return in ContainsBannedWords() function

* Slurs are blocked in both public and private groups

* Added code to send a slack message for slurs

* Fixed formatting issues

* Incorporated tectContainsBannedWords() function from PR 8197, added an argument to specify the list of banned words to check

* Added initial tests for blocking slurs and revoking chat priviliges

* Uncommented line to save revoked privileges

* Check that privileges are revoked in private groups

* Moved code to email/slack mods to chat api file

* Switched to BadRequest instead of NotFound error

* Restore chat privileges after test

* Using official placeholder slur

* Fixed line to export sendSubscriptionNotification function for slack

* Replaced muteUser function in user methods with a single line in the chat controller file

* Reset chatRevoked flag to false in a single line

* Switched method of setting chatRevoked flag so that it is updated locally and in the database

* First attempt at the muteUser function: revokes user's chat privileges and notifies moderators

* Manual merge for cherry-pick

* Initial working prototype for blocking posting of slurs. Moved check from group.js to the chat api. Still needs: to permanently revoke chat privileges, to notify the moderators, a better method for checking for the blacklisted words, and a way to get the real list of words to check.

* Permanently revoke chat privileges when attempting to post a slur.

* Removed console logs

* Created report to be sent to moderators via email

* Do not moderate private groups

* Moved slur check to a generic check for banned words function

* Moved list of slurs to a separate file, fixed misplacement of return in ContainsBannedWords() function

* Slurs are blocked in both public and private groups

* Added code to send a slack message for slurs

* Fixed formatting issues

* Incorporated tectContainsBannedWords() function from PR 8197, added an argument to specify the list of banned words to check

* Added initial tests for blocking slurs and revoking chat priviliges

* Uncommented line to save revoked privileges

* Check that privileges are revoked in private groups

* Moved code to email/slack mods to chat api file

* Switched to BadRequest instead of NotFound error

* Restore chat privileges after test

* Using official placeholder slur

* Fixed line to export sendSubscriptionNotification function for slack

* Replaced muteUser function in user methods with a single line in the chat controller file

* Reset chatRevoked flag to false in a single line

* Switched method of setting chatRevoked flag so that it is updated locally and in the database

* Removed some code that got re-added after rebase

* Tests for automatic slur muting pass but are incomplete (do not check that chatRevoked flag is true)

* Moved list of banned slurs to server side

* Added warning to bannedSlurs file

* Test chat privileges revoked when posting slur in public chat

* Fix issues left over after rebase (I hope)

* Added code to test for revoked chat privileges after posting a slur in a private group

* Moved banned slur message into locales message

* Added new code to check for banned slurs (parallels banned words code)

* Fixed AUTHOR_MOTAL_URL in sendTxn for slur blocking

* Added tests that email sent on attempted slur in chat post

* Created context for slur-related-tests, fixed sandboxing of email. Successfully tests that email.sendTxn is called, but the email content test fails

* commented out slack (for now) and cleaned up tests of sending email

* Successfully tests that slur-report-to-mods email is sent

* Slack message is sent, and testing works, but some user variables seem to only work when found in chat.js and passed to slack

* Made some fixes for lint, but not sure what to do about the camel case requirement fail, since that's how they're defined in other slack calls

* Slack tests pass, skipped camelcase check around those code blocks

* Fixed InternalServerError caused by slack messaging

* Updated chat privileges revoked error

* fix(locale): typo correction
This commit is contained in:
Alyssa Batula
2017-07-19 17:06:15 -04:00
committed by Sabe Jones
parent 89ee8b1648
commit c350665076
6 changed files with 296 additions and 22 deletions

View File

@@ -15,6 +15,7 @@ import nconf from 'nconf';
import Bluebird from 'bluebird';
import bannedWords from '../../libs/bannedWords';
import { TAVERN_ID } from '../../models/group';
import bannedSlurs from '../../bannedSlurs';
const FLAG_REPORT_EMAILS = nconf.get('FLAG_REPORT_EMAIL').split(',').map((email) => {
return { email, canSend: true };
@@ -53,6 +54,44 @@ async function getAuthorEmailFromMessage (message) {
}
}
// @TODO: Probably move this to a library
function matchExact (r, str) {
let match = str.match(r);
return match !== null && match[0] !== null;
}
let bannedWordRegexs = [];
for (let i = 0; i < bannedWords.length; i += 1) {
let word = bannedWords[i];
let regEx = new RegExp(`\\b([^a-z]+)?${word.toLowerCase()}([^a-z]+)?\\b`);
bannedWordRegexs.push(regEx);
}
function textContainsBannedWords (message) {
for (let i = 0; i < bannedWordRegexs.length; i += 1) {
let regEx = bannedWordRegexs[i];
if (matchExact(regEx, message.toLowerCase())) return true;
}
return false;
}
let bannedSlurRegexs = [];
for (let i = 0; i < bannedSlurs.length; i += 1) {
let word = bannedSlurs[i];
let regEx = new RegExp(`\\b([^a-z]+)?${word.toLowerCase()}([^a-z]+)?\\b`);
bannedSlurRegexs.push(regEx);
}
function textContainsBannedSlur (message) {
for (let i = 0; i < bannedSlurRegexs.length; i += 1) {
let regEx = bannedSlurRegexs[i];
if (matchExact(regEx, message.toLowerCase())) return true;
}
return false;
}
/**
* @api {get} /api/v3/groups/:groupId/chat Get chat messages from a group
* @apiName GetChat
@@ -85,28 +124,6 @@ api.getChat = {
},
};
// @TODO: Probably move this to a library
function matchExact (r, str) {
let match = str.match(r);
return match !== null && match[0] !== null;
}
let bannedWordRegexs = [];
for (let i = 0; i < bannedWords.length; i += 1) {
let word = bannedWords[i];
let regEx = new RegExp(`\\b([^a-z]+)?${word.toLowerCase()}([^a-z]+)?\\b`);
bannedWordRegexs.push(regEx);
}
function textContainsBannedWords (message) {
for (let i = 0; i < bannedWordRegexs.length; i += 1) {
let regEx = bannedWordRegexs[i];
if (matchExact(regEx, message.toLowerCase())) return true;
}
return false;
}
/**
* @api {post} /api/v3/groups/:groupId/chat Post chat message to a group
* @apiName PostChat
@@ -139,6 +156,44 @@ api.postChat = {
let group = await Group.getGroup({user, groupId});
// Check message for banned slurs
if (textContainsBannedSlur(req.body.message)) {
let message = req.body.message;
user.flags.chatRevoked = true;
await user.save();
// Email the mods
let authorEmail = getUserInfo(user, ['email']).email;
let groupUrl = getGroupUrl(group);
let report = [
{name: 'MESSAGE_TIME', content: (new Date()).toString()},
{name: 'MESSAGE_TEXT', content: message},
{name: 'AUTHOR_USERNAME', content: user.profile.name},
{name: 'AUTHOR_UUID', content: user._id},
{name: 'AUTHOR_EMAIL', content: authorEmail},
{name: 'AUTHOR_MODAL_URL', content: `/static/front/#?memberId=${user._id}`},
{name: 'GROUP_NAME', content: group.name},
{name: 'GROUP_TYPE', content: group.type},
{name: 'GROUP_ID', content: group._id},
{name: 'GROUP_URL', content: groupUrl},
];
sendTxn(FLAG_REPORT_EMAILS, 'slur-report-to-mods', report);
// Slack the mods
slack.sendSlurNotification({
authorEmail,
author: user,
group,
message,
});
throw new BadRequest(res.t('bannedSlurUsed'));
}
if (!group) throw new NotFound(res.t('groupNotFound'));
if (group.privacy !== 'private' && user.flags.chatRevoked) {
throw new NotAuthorized(res.t('chatPrivilegesRevoked'));