Skip to content

Commit

Permalink
Merge branch 'Alys-shadow-muting' into delta
Browse files Browse the repository at this point in the history
  • Loading branch information
paglias committed Jul 22, 2019
2 parents 0cdd741 + 4b2deaa commit fc97c0b
Show file tree
Hide file tree
Showing 13 changed files with 324 additions and 84 deletions.
176 changes: 130 additions & 46 deletions test/api/v3/integration/chat/POST-chat.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
SPAM_MIN_EXEMPT_CONTRIB_LEVEL,
TAVERN_ID,
} from '../../../../../website/server/models/group';
import { CHAT_FLAG_FROM_SHADOW_MUTE } from '../../../../../website/common/script/constants';
import { v4 as generateUUID } from 'uuid';
import { getMatchesByWordArray } from '../../../../../website/server/libs/stringUtils';
import bannedWords from '../../../../../website/server/libs/bannedWords';
Expand Down Expand Up @@ -81,6 +82,10 @@ describe('POST /chat', () => {
});

describe('mute user', () => {
afterEach(() => {
member.update({'flags.chatRevoked': false});
});

it('returns an error when chat privileges are revoked when sending a message to a public guild', async () => {
const userWithChatRevoked = await member.update({'flags.chatRevoked': true});
await expect(userWithChatRevoked.post(`/groups/${groupWithChat._id}/chat`, { message: testMessage})).to.eventually.be.rejected.and.eql({
Expand All @@ -89,6 +94,129 @@ describe('POST /chat', () => {
message: t('chatPrivilegesRevoked'),
});
});

it('does not error when chat privileges are revoked when sending a message to a private guild', async () => {
const { group, members } = await createAndPopulateGroup({
groupDetails: {
name: 'Private Guild',
type: 'guild',
privacy: 'private',
},
members: 1,
});

const privateGuildMemberWithChatsRevoked = members[0];
await privateGuildMemberWithChatsRevoked.update({'flags.chatRevoked': true});

const message = await privateGuildMemberWithChatsRevoked.post(`/groups/${group._id}/chat`, { message: testMessage});

expect(message.message.id).to.exist;
});

it('does not error when chat privileges are revoked when sending a message to a party', async () => {
const { group, members } = await createAndPopulateGroup({
groupDetails: {
name: 'Party',
type: 'party',
privacy: 'private',
},
members: 1,
});

const privatePartyMemberWithChatsRevoked = members[0];
await privatePartyMemberWithChatsRevoked.update({'flags.chatRevoked': true});

const message = await privatePartyMemberWithChatsRevoked.post(`/groups/${group._id}/chat`, { message: testMessage});

expect(message.message.id).to.exist;
});
});

describe('shadow-mute user', () => {
beforeEach(() => {
sandbox.spy(email, 'sendTxn');
sandbox.stub(IncomingWebhook.prototype, 'send');
});

afterEach(() => {
sandbox.restore();
member.update({'flags.chatShadowMuted': false});
});

it('creates a chat with flagCount already set and notifies mods when sending a message to a public guild', async () => {
const userWithChatShadowMuted = await member.update({'flags.chatShadowMuted': true});
const message = await userWithChatShadowMuted.post(`/groups/${groupWithChat._id}/chat`, { message: testMessage});
expect(message.message.id).to.exist;
expect(message.message.flagCount).to.eql(CHAT_FLAG_FROM_SHADOW_MUTE);

// Email sent to mods
await sleep(0.5);
expect(email.sendTxn).to.be.calledOnce;
expect(email.sendTxn.args[0][1]).to.eql('shadow-muted-post-report-to-mods');

// Slack message to mods
expect(IncomingWebhook.prototype.send).to.be.calledOnce;
/* eslint-disable camelcase */
expect(IncomingWebhook.prototype.send).to.be.calledWith({
text: `@${member.auth.local.username} / ${member.profile.name} posted while shadow-muted`,
attachments: [{
fallback: 'Shadow-Muted Message',
color: 'danger',
author_name: `@${member.auth.local.username} ${member.profile.name} (${member.auth.local.email}; ${member._id})`,
title: 'Shadow-Muted Post in Test Guild',
title_link: `${BASE_URL}/groups/guild/${groupWithChat.id}`,
text: testMessage,
mrkdwn_in: [
'text',
],
}],
});
/* eslint-enable camelcase */
});

it('creates a chat with zero flagCount when sending a message to a private guild', async () => {
const { group, members } = await createAndPopulateGroup({
groupDetails: {
name: 'Private Guild',
type: 'guild',
privacy: 'private',
},
members: 1,
});

const userWithChatShadowMuted = members[0];
await userWithChatShadowMuted.update({'flags.chatShadowMuted': true});

const message = await userWithChatShadowMuted.post(`/groups/${group._id}/chat`, { message: testMessage});

expect(message.message.id).to.exist;
expect(message.message.flagCount).to.eql(0);
});

it('creates a chat with zero flagCount when sending a message to a party', async () => {
const { group, members } = await createAndPopulateGroup({
groupDetails: {
name: 'Party',
type: 'party',
privacy: 'private',
},
members: 1,
});

const userWithChatShadowMuted = members[0];
await userWithChatShadowMuted.update({'flags.chatShadowMuted': true});

const message = await userWithChatShadowMuted.post(`/groups/${group._id}/chat`, { message: testMessage});

expect(message.message.id).to.exist;
expect(message.message.flagCount).to.eql(0);
});

it('creates a chat with zero flagCount when non-shadow-muted user sends a message to a public guild', async () => {
const message = await member.post(`/groups/${groupWithChat._id}/chat`, { message: testMessage});
expect(message.message.id).to.exist;
expect(message.message.flagCount).to.eql(0);
});
});

context('banned word', () => {
Expand Down Expand Up @@ -235,6 +363,7 @@ describe('POST /chat', () => {

afterEach(() => {
sandbox.restore();
user.update({'flags.chatRevoked': false});
});

it('errors and revokes privileges when chat message contains a banned slur', async () => {
Expand Down Expand Up @@ -274,11 +403,6 @@ describe('POST /chat', () => {
error: 'NotAuthorized',
message: t('chatPrivilegesRevoked'),
});

// @TODO: The next test should not depend on this. We should reset the user test in a beforeEach
// Restore chat privileges to continue testing
user.flags.chatRevoked = false;
await user.update({'flags.chatRevoked': false});
});

it('does not allow slurs in private groups', async () => {
Expand Down Expand Up @@ -327,10 +451,6 @@ describe('POST /chat', () => {
error: 'NotAuthorized',
message: t('chatPrivilegesRevoked'),
});

// Restore chat privileges to continue testing
members[0].flags.chatRevoked = false;
await members[0].update({'flags.chatRevoked': false});
});

it('errors when slur is typed in mixed case', async () => {
Expand All @@ -345,42 +465,6 @@ describe('POST /chat', () => {
});
});

it('does not error when sending a message to a private guild with a user with revoked chat', async () => {
let { group, members } = await createAndPopulateGroup({
groupDetails: {
name: 'Private Guild',
type: 'guild',
privacy: 'private',
},
members: 1,
});

let privateGuildMemberWithChatsRevoked = members[0];
await privateGuildMemberWithChatsRevoked.update({'flags.chatRevoked': true});

let message = await privateGuildMemberWithChatsRevoked.post(`/groups/${group._id}/chat`, { message: testMessage});

expect(message.message.id).to.exist;
});

it('does not error when sending a message to a party with a user with revoked chat', async () => {
let { group, members } = await createAndPopulateGroup({
groupDetails: {
name: 'Party',
type: 'party',
privacy: 'private',
},
members: 1,
});

let privatePartyMemberWithChatsRevoked = members[0];
await privatePartyMemberWithChatsRevoked.update({'flags.chatRevoked': true});

let message = await privatePartyMemberWithChatsRevoked.post(`/groups/${group._id}/chat`, { message: testMessage});

expect(message.message.id).to.exist;
});

it('creates a chat', async () => {
const newMessage = await user.post(`/groups/${groupWithChat._id}/chat`, { message: testMessage});
const groupMessages = await user.get(`/groups/${groupWithChat._id}/chat`);
Expand Down Expand Up @@ -533,7 +617,7 @@ describe('POST /chat', () => {
});

it('contributor should not receive spam alert', async () => {
let userSocialite = await member.update({'contributor.level': SPAM_MIN_EXEMPT_CONTRIB_LEVEL, 'flags.chatRevoked': false});
let userSocialite = await member.update({'contributor.level': SPAM_MIN_EXEMPT_CONTRIB_LEVEL});

// Post 1 more message than the spam limit to ensure they do not reach the limit
for (let i = 0; i < SPAM_MESSAGE_LIMIT + 1; i++) {
Expand Down
12 changes: 9 additions & 3 deletions test/api/v3/integration/hall/PUT-hall_heores_heroId.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,16 +105,22 @@ describe('PUT /heroes/:heroId', () => {

it('updates chatRevoked flag', async () => {
let hero = await generateUser();

await user.put(`/hall/heroes/${hero._id}`, {
flags: {chatRevoked: true},
});

await hero.sync();

expect(hero.flags.chatRevoked).to.eql(true);
});

it('updates chatShadowMuted flag', async () => {
let hero = await generateUser();
await user.put(`/hall/heroes/${hero._id}`, {
flags: {chatShadowMuted: true},
});
await hero.sync();
expect(hero.flags.chatShadowMuted).to.eql(true);
});

it('updates contributor level', async () => {
let hero = await generateUser({
contributor: {level: 5},
Expand Down
14 changes: 11 additions & 3 deletions website/client/components/chat/chatCard.vue
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
<template lang="pug">
div
.mentioned-icon(v-if='isUserMentioned')
.message-hidden(v-if='!inbox && msg.flagCount === 1 && user.contributor.admin') Message flagged once, not hidden
.message-hidden(v-if='!inbox && msg.flagCount > 1 && user.contributor.admin') Message hidden
.message-hidden(v-if='!inbox && user.contributor.admin && msg.flagCount') {{flagCountDescription}}
.card-body
user-link(:userId="msg.uuid", :name="msg.user", :backer="msg.backer", :contributor="msg.contributor")
p.time
Expand Down Expand Up @@ -137,7 +136,8 @@ import copyIcon from 'assets/svg/copy.svg';
import likeIcon from 'assets/svg/like.svg';
import likedIcon from 'assets/svg/liked.svg';
import reportIcon from 'assets/svg/report.svg';
import {highlightUsers} from '../../libs/highlightUsers';
import { highlightUsers } from '../../libs/highlightUsers';
import { CHAT_FLAG_LIMIT_FOR_HIDING, CHAT_FLAG_FROM_SHADOW_MUTE } from '../../../common/script/constants';
export default {
components: {userLink},
Expand Down Expand Up @@ -210,6 +210,12 @@ export default {
isMessageReported () {
return this.msg.flags && this.msg.flags[this.user.id] || this.reported;
},
flagCountDescription () {
if (!this.msg.flagCount) return '';
if (this.msg.flagCount < CHAT_FLAG_LIMIT_FOR_HIDING) return 'Message flagged once, not hidden';
if (this.msg.flagCount < CHAT_FLAG_FROM_SHADOW_MUTE) return 'Message hidden';
return 'Message hidden (shadow-muted)';
},
},
methods: {
async like () {
Expand Down Expand Up @@ -274,6 +280,8 @@ export default {
},
},
mounted () {
this.CHAT_FLAG_LIMIT_FOR_HIDING = CHAT_FLAG_LIMIT_FOR_HIDING;
this.CHAT_FLAG_FROM_SHADOW_MUTE = CHAT_FLAG_FROM_SHADOW_MUTE;
this.$emit('chat-card-mounted', this.msg.id);
},
};
Expand Down
6 changes: 6 additions & 0 deletions website/client/components/hall/heroes.vue
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@
h4.expand-toggle(:class="{'open': expandAuth}", @click="expandAuth = !expandAuth") Auth
div(v-if="expandAuth")
pre {{hero.auth}}
.form-group
.checkbox
label
input(type='checkbox', v-if='hero.flags', v-model='hero.flags.chatShadowMuted')
strong Chat Shadow Muting On
.form-group
.checkbox
label
Expand Down Expand Up @@ -180,6 +185,7 @@ export default {
if (!this.hero.flags) {
this.hero.flags = {
chatRevoked: false,
chatShadowMuted: false,
};
}
this.expandItems = false;
Expand Down

0 comments on commit fc97c0b

Please sign in to comment.