mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-16 22:27:26 +01:00
feat(api): Stop alerting about new messages for large groups
closes #6706 closes #6762
This commit is contained in:
@@ -163,6 +163,7 @@
|
||||
"partyOnName": "Party On",
|
||||
"partyUpAchievement": "Joined a Party with another person! Have fun battling monsters and supporting each other.",
|
||||
"partyOnAchievement": "Joined a Party with at least four people! Enjoy your increased accountability as you unite with your friends to vanquish your foes!",
|
||||
"largeGroupNote": "Note: This Guild is now too large to support notifications! Be sure to check back every day to see new messages.",
|
||||
"groupIdRequired": "\"groupId\" must be a valid UUID",
|
||||
"groupNotFound": "Group not found or you don't have access.",
|
||||
"groupTypesRequired": "You must supply a valid \"type\" query string.",
|
||||
|
||||
@@ -3,4 +3,5 @@ export const MAX_LEVEL = 100;
|
||||
export const MAX_STAT_POINTS = MAX_LEVEL;
|
||||
export const ATTRIBUTES = ['str', 'int', 'per', 'con'];
|
||||
|
||||
export const TAVERN_ID = '00000000-0000-4000-A000-000000000000';
|
||||
export const TAVERN_ID = '00000000-0000-4000-A000-000000000000';
|
||||
export const LARGE_GROUP_COUNT_MESSAGE_CUTOFF = 5000;
|
||||
|
||||
@@ -17,13 +17,18 @@ import { shouldDo, daysSince } from './cron';
|
||||
api.shouldDo = shouldDo;
|
||||
api.daysSince = daysSince;
|
||||
|
||||
// TODO under api.constants? and capitalize exported names too
|
||||
import {
|
||||
MAX_HEALTH,
|
||||
MAX_LEVEL,
|
||||
MAX_STAT_POINTS,
|
||||
TAVERN_ID,
|
||||
LARGE_GROUP_COUNT_MESSAGE_CUTOFF,
|
||||
} from './constants';
|
||||
|
||||
api.constants = {
|
||||
LARGE_GROUP_COUNT_MESSAGE_CUTOFF,
|
||||
};
|
||||
// TODO Move these under api.constants
|
||||
api.maxLevel = MAX_LEVEL;
|
||||
api.maxHealth = MAX_HEALTH;
|
||||
api.maxStatPoints = MAX_STAT_POINTS;
|
||||
|
||||
@@ -3,6 +3,8 @@ import { model as Group } from '../../../../../website/server/models/group';
|
||||
import { model as User } from '../../../../../website/server/models/user';
|
||||
import { quests as questScrolls } from '../../../../../common/script/content';
|
||||
import * as email from '../../../../../website/server/libs/api-v3/email';
|
||||
import validator from 'validator';
|
||||
import { TAVERN_ID } from '../../../../../common/script/';
|
||||
|
||||
describe('Group Model', () => {
|
||||
let party, questLeader, participatingMember, nonParticipatingMember, undecidedMember;
|
||||
@@ -395,6 +397,147 @@ describe('Group Model', () => {
|
||||
});
|
||||
|
||||
context('Instance Methods', () => {
|
||||
describe('#sendChat', () => {
|
||||
beforeEach(() => {
|
||||
sandbox.spy(User, 'update');
|
||||
});
|
||||
|
||||
it('puts message at top of chat array', () => {
|
||||
let oldMessage = {
|
||||
text: 'a message',
|
||||
};
|
||||
party.chat.push(oldMessage, oldMessage, oldMessage);
|
||||
|
||||
party.sendChat('a new message', {_id: 'user-id', profile: { name: 'user name' }});
|
||||
|
||||
expect(party.chat).to.have.a.lengthOf(4);
|
||||
expect(party.chat[0].text).to.eql('a new message');
|
||||
expect(party.chat[0].uuid).to.eql('user-id');
|
||||
});
|
||||
|
||||
it('formats message', () => {
|
||||
party.sendChat('a new message', {
|
||||
_id: 'user-id',
|
||||
profile: { name: 'user name' },
|
||||
contributor: { toObject () { return 'contributor object'; } },
|
||||
backer: { toObject () { return 'backer object'; } },
|
||||
});
|
||||
|
||||
let chat = party.chat[0];
|
||||
|
||||
expect(chat.text).to.eql('a new message');
|
||||
expect(validator.isUUID(chat.id)).to.eql(true);
|
||||
expect(chat.timestamp).to.be.a('number');
|
||||
expect(chat.likes).to.eql({});
|
||||
expect(chat.flags).to.eql({});
|
||||
expect(chat.flagCount).to.eql(0);
|
||||
expect(chat.uuid).to.eql('user-id');
|
||||
expect(chat.contributor).to.eql('contributor object');
|
||||
expect(chat.backer).to.eql('backer object');
|
||||
expect(chat.user).to.eql('user name');
|
||||
});
|
||||
|
||||
it('formats message as system if no user is passed in', () => {
|
||||
party.sendChat('a system message');
|
||||
|
||||
let chat = party.chat[0];
|
||||
|
||||
expect(chat.text).to.eql('a system message');
|
||||
expect(validator.isUUID(chat.id)).to.eql(true);
|
||||
expect(chat.timestamp).to.be.a('number');
|
||||
expect(chat.likes).to.eql({});
|
||||
expect(chat.flags).to.eql({});
|
||||
expect(chat.flagCount).to.eql(0);
|
||||
expect(chat.uuid).to.eql('system');
|
||||
expect(chat.contributor).to.not.exist;;
|
||||
expect(chat.backer).to.not.exist;;
|
||||
expect(chat.user).to.not.exist;;
|
||||
});
|
||||
|
||||
it('cuts down chat to 200 messages', () => {
|
||||
for (var i = 0; i < 220; i++) {
|
||||
party.chat.push({ text: 'a message' });
|
||||
};
|
||||
|
||||
expect(party.chat).to.have.a.lengthOf(220);
|
||||
|
||||
party.sendChat('message');
|
||||
|
||||
expect(party.chat).to.have.a.lengthOf(200);
|
||||
});
|
||||
|
||||
it('updates users about new messages in party', () => {
|
||||
party.sendChat('message');
|
||||
|
||||
expect(User.update).to.be.calledOnce;
|
||||
expect(User.update).to.be.calledWithMatch({
|
||||
'party._id': party._id,
|
||||
_id: { $ne: '' },
|
||||
}, {
|
||||
$set: {
|
||||
[`newMessages.${party._id}`]: {
|
||||
name: party.name,
|
||||
value: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('updates users about new messages in group', () => {
|
||||
let group = new Group({
|
||||
type: 'guild',
|
||||
});
|
||||
|
||||
group.sendChat('message');
|
||||
|
||||
expect(User.update).to.be.calledOnce;
|
||||
expect(User.update).to.be.calledWithMatch({
|
||||
'guilds': group._id,
|
||||
_id: { $ne: '' },
|
||||
}, {
|
||||
$set: {
|
||||
[`newMessages.${group._id}`]: {
|
||||
name: group.name,
|
||||
value: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('does not send update to user that sent the message', () => {
|
||||
party.sendChat('message', {_id: 'user-id', profile: { name: 'user' }});
|
||||
|
||||
expect(User.update).to.be.calledOnce;
|
||||
expect(User.update).to.be.calledWithMatch({
|
||||
'party._id': party._id,
|
||||
_id: { $ne: 'user-id' },
|
||||
}, {
|
||||
$set: {
|
||||
[`newMessages.${party._id}`]: {
|
||||
name: party.name,
|
||||
value: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('skips sending new message notification for guilds with > 5000 members', () => {
|
||||
party.memberCount = 5001;
|
||||
|
||||
party.sendChat('message');
|
||||
|
||||
expect(User.update).to.not.be.called;
|
||||
});
|
||||
|
||||
it('skips sending messages to the tavern', () => {
|
||||
party._id = TAVERN_ID;
|
||||
|
||||
party.sendChat('message');
|
||||
|
||||
expect(User.update).to.not.be.called;
|
||||
});
|
||||
});
|
||||
|
||||
describe('#startQuest', () => {
|
||||
context('Failure Conditions', () => {
|
||||
it('throws an error if group is not a party', async () => {
|
||||
|
||||
@@ -25,6 +25,9 @@ const Schema = mongoose.Schema;
|
||||
export const INVITES_LIMIT = 100;
|
||||
export const TAVERN_ID = shared.TAVERN_ID;
|
||||
|
||||
const NO_CHAT_NOTIFICATIONS = [TAVERN_ID];
|
||||
const LARGE_GROUP_COUNT_MESSAGE_CUTOFF = shared.constants.LARGE_GROUP_COUNT_MESSAGE_CUTOFF;
|
||||
|
||||
const CRON_SAFE_MODE = nconf.get('CRON_SAFE_MODE') === 'true';
|
||||
const CRON_SEMI_SAFE_MODE = nconf.get('CRON_SEMI_SAFE_MODE') === 'true';
|
||||
|
||||
@@ -300,34 +303,30 @@ export function chatDefaults (msg, user) {
|
||||
return message;
|
||||
}
|
||||
|
||||
const NO_CHAT_NOTIFICATIONS = [TAVERN_ID];
|
||||
|
||||
schema.methods.sendChat = function sendChat (message, user) {
|
||||
this.chat.unshift(chatDefaults(message, user));
|
||||
this.chat.splice(200);
|
||||
|
||||
// Kick off chat notifications in the background.
|
||||
let lastSeenUpdate = {$set: {}};
|
||||
lastSeenUpdate.$set[`newMessages.${this._id}`] = {name: this.name, value: true};
|
||||
|
||||
// do not send notifications for guilds with more than 5000 users and for the tavern
|
||||
if (NO_CHAT_NOTIFICATIONS.indexOf(this._id) !== -1 || this.memberCount > 5000) {
|
||||
// TODO For Tavern, only notify them if their name was mentioned
|
||||
// var profileNames = [] // get usernames from regex of @xyz. how to handle space-delimited profile names?
|
||||
// User.update({'profile.name':{$in:profileNames}},lastSeenUpdate,{multi:true}).exec();
|
||||
} else {
|
||||
let query = {};
|
||||
|
||||
if (this.type === 'party') {
|
||||
query['party._id'] = this._id;
|
||||
} else {
|
||||
query.guilds = this._id;
|
||||
}
|
||||
|
||||
query._id = { $ne: user ? user._id : ''};
|
||||
|
||||
User.update(query, lastSeenUpdate, {multi: true}).exec();
|
||||
if (NO_CHAT_NOTIFICATIONS.indexOf(this._id) !== -1 || this.memberCount > LARGE_GROUP_COUNT_MESSAGE_CUTOFF) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Kick off chat notifications in the background.
|
||||
let lastSeenUpdate = {$set: {
|
||||
[`newMessages.${this._id}`]: {name: this.name, value: true},
|
||||
}};
|
||||
let query = {};
|
||||
|
||||
if (this.type === 'party') {
|
||||
query['party._id'] = this._id;
|
||||
} else {
|
||||
query.guilds = this._id;
|
||||
}
|
||||
|
||||
query._id = { $ne: user ? user._id : ''};
|
||||
|
||||
User.update(query, lastSeenUpdate, {multi: true}).exec();
|
||||
};
|
||||
|
||||
schema.methods.startQuest = async function startQuest (user) {
|
||||
|
||||
@@ -82,7 +82,7 @@ a.pull-right.gem-wallet(ng-if='group.type!="party"', popover-trigger='mouseenter
|
||||
span.glyphicon.glyphicon-ban-circle(tooltip=env.t('banTip'))
|
||||
a.media-body
|
||||
span(ng-click='clickMember(member._id, true)')
|
||||
| {{member.profile.name}}
|
||||
| {{member.profile.name}}
|
||||
span(ng-if='group.type === "party"')
|
||||
| (#[strong {{member.stats.hp.toFixed(1)}}] #{env.t('hp')})
|
||||
tr(ng-if='::group.memberCount > group.members.length')
|
||||
@@ -145,6 +145,7 @@ a.pull-right.gem-wallet(ng-if='group.type!="party"', popover-trigger='mouseenter
|
||||
.popover-content
|
||||
markdown(text='group._editing ? groupCopy.leaderMessage : group.leaderMessage')
|
||||
div(ng-controller='ChatCtrl')
|
||||
.alert.alert-info.alert-sm(ng-if='group.memberCount > Shared.constants.LARGE_GROUP_COUNT_MESSAGE_CUTOFF')=env.t('largeGroupNote')
|
||||
h3=env.t('chat')
|
||||
include ./chat-box
|
||||
|
||||
|
||||
Reference in New Issue
Block a user