mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-18 15:17:25 +01:00
@@ -8,7 +8,7 @@ describe('common.fns.updateStats', () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
user = generateUser();
|
user = generateUser();
|
||||||
// user.addNotification = sinon.spy();
|
user.addNotification = sinon.spy();
|
||||||
});
|
});
|
||||||
|
|
||||||
context('No Hp', () => {
|
context('No Hp', () => {
|
||||||
@@ -110,14 +110,14 @@ describe('common.fns.updateStats', () => {
|
|||||||
expect(user.stats.points).to.eql(10);
|
expect(user.stats.points).to.eql(10);
|
||||||
});
|
});
|
||||||
|
|
||||||
xit('add user notification when drops are enabled', () => {
|
it('add user notification when drops are enabled', () => {
|
||||||
user.stats.lvl = 3;
|
user.stats.lvl = 3;
|
||||||
updateStats(user, { });
|
updateStats(user, { });
|
||||||
expect(user.addNotification).to.be.calledOnce;
|
expect(user.addNotification).to.be.calledOnce;
|
||||||
expect(user.addNotification).to.be.calledWith('DROPS_ENABLED');
|
expect(user.addNotification).to.be.calledWith('DROPS_ENABLED');
|
||||||
});
|
});
|
||||||
|
|
||||||
xit('add user notification when the user levels up', () => {
|
it('add user notification when the user levels up', () => {
|
||||||
const initialLvl = user.stats.lvl;
|
const initialLvl = user.stats.lvl;
|
||||||
updateStats(user, {
|
updateStats(user, {
|
||||||
exp: 3000,
|
exp: 3000,
|
||||||
@@ -129,7 +129,7 @@ describe('common.fns.updateStats', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
xit('add user notification when rebirth is enabled', () => {
|
it('add user notification when rebirth is enabled', () => {
|
||||||
user.stats.lvl = 51;
|
user.stats.lvl = 51;
|
||||||
updateStats(user, { });
|
updateStats(user, { });
|
||||||
expect(user.addNotification).to.be.calledTwice; // once is for drops enabled
|
expect(user.addNotification).to.be.calledTwice; // once is for drops enabled
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ module.exports = function updateStats (user, stats, req = {}, analytics) {
|
|||||||
if (stats.exp >= experienceToNextLevel) {
|
if (stats.exp >= experienceToNextLevel) {
|
||||||
user.stats.exp = stats.exp;
|
user.stats.exp = stats.exp;
|
||||||
|
|
||||||
// const initialLvl = user.stats.lvl;
|
const initialLvl = user.stats.lvl;
|
||||||
|
|
||||||
while (stats.exp >= experienceToNextLevel) {
|
while (stats.exp >= experienceToNextLevel) {
|
||||||
stats.exp -= experienceToNextLevel;
|
stats.exp -= experienceToNextLevel;
|
||||||
@@ -50,13 +50,12 @@ module.exports = function updateStats (user, stats, req = {}, analytics) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// @TODO: Tmp disable to see if this is causing concurrency
|
const newLvl = user.stats.lvl;
|
||||||
// const newLvl = user.stats.lvl;
|
|
||||||
//
|
if (user.addNotification) user.addNotification('LEVELED_UP', {
|
||||||
// if (user.addNotification) user.addNotification('LEVELED_UP', {
|
initialLvl,
|
||||||
// initialLvl,
|
newLvl,
|
||||||
// newLvl,
|
});
|
||||||
// });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
user.stats.exp = stats.exp;
|
user.stats.exp = stats.exp;
|
||||||
|
|||||||
@@ -12,7 +12,10 @@ import * as Tasks from './task';
|
|||||||
import validator from 'validator';
|
import validator from 'validator';
|
||||||
import { removeFromArray } from '../libs/collectionManipulators';
|
import { removeFromArray } from '../libs/collectionManipulators';
|
||||||
import payments from '../libs/payments/payments';
|
import payments from '../libs/payments/payments';
|
||||||
import { groupChatReceivedWebhook } from '../libs/webhook';
|
import {
|
||||||
|
groupChatReceivedWebhook,
|
||||||
|
questActivityWebhook,
|
||||||
|
} from '../libs/webhook';
|
||||||
import {
|
import {
|
||||||
InternalServerError,
|
InternalServerError,
|
||||||
BadRequest,
|
BadRequest,
|
||||||
@@ -648,20 +651,24 @@ schema.methods.startQuest = async function startQuest (user) {
|
|||||||
removeFromArray(nonUserQuestMembers, user._id);
|
removeFromArray(nonUserQuestMembers, user._id);
|
||||||
|
|
||||||
// remove any users from quest.members who aren't in the party
|
// remove any users from quest.members who aren't in the party
|
||||||
let partyId = this._id;
|
// and get the data necessary to send webhooks
|
||||||
let questMembers = this.quest.members;
|
const members = [];
|
||||||
await Promise.all(Object.keys(this.quest.members).map(memberId => {
|
|
||||||
return User.findOne({_id: memberId, 'party._id': partyId})
|
await User.find({
|
||||||
.select('_id')
|
_id: {$in: Object.keys(this.quest.members)},
|
||||||
.lean()
|
})
|
||||||
.exec()
|
.select('party.quest party._id items.quests auth preferences.emailNotifications preferences.pushNotifications pushDevices profile.name webhooks')
|
||||||
.then((member) => {
|
.lean()
|
||||||
if (!member) {
|
.exec()
|
||||||
delete questMembers[memberId];
|
.then(partyMembers => {
|
||||||
|
partyMembers.forEach(member => {
|
||||||
|
if (!member.party || member.party._id !== this._id) {
|
||||||
|
delete this.quest.members[member._id];
|
||||||
|
} else {
|
||||||
|
members.push(member);
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
});
|
});
|
||||||
}));
|
});
|
||||||
|
|
||||||
if (userIsParticipating) {
|
if (userIsParticipating) {
|
||||||
user.party.quest.key = this.quest.key;
|
user.party.quest.key = this.quest.key;
|
||||||
@@ -670,20 +677,23 @@ schema.methods.startQuest = async function startQuest (user) {
|
|||||||
user.markModified('party.quest');
|
user.markModified('party.quest');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const promises = [];
|
||||||
|
|
||||||
// Remove the quest from the quest leader items (if they are the current user)
|
// Remove the quest from the quest leader items (if they are the current user)
|
||||||
if (this.quest.leader === user._id) {
|
if (this.quest.leader === user._id) {
|
||||||
user.items.quests[this.quest.key] -= 1;
|
user.items.quests[this.quest.key] -= 1;
|
||||||
user.markModified('items.quests');
|
user.markModified('items.quests');
|
||||||
|
promises.push(user.save());
|
||||||
} else { // another user is starting the quest, update the leader separately
|
} else { // another user is starting the quest, update the leader separately
|
||||||
await User.update({_id: this.quest.leader}, {
|
promises.push(User.update({_id: this.quest.leader}, {
|
||||||
$inc: {
|
$inc: {
|
||||||
[`items.quests.${this.quest.key}`]: -1,
|
[`items.quests.${this.quest.key}`]: -1,
|
||||||
},
|
},
|
||||||
}).exec();
|
}).exec());
|
||||||
}
|
}
|
||||||
|
|
||||||
// update the remaining users
|
// update the remaining users
|
||||||
await User.update({
|
promises.push(User.update({
|
||||||
_id: { $in: nonUserQuestMembers },
|
_id: { $in: nonUserQuestMembers },
|
||||||
}, {
|
}, {
|
||||||
$set: {
|
$set: {
|
||||||
@@ -691,7 +701,9 @@ schema.methods.startQuest = async function startQuest (user) {
|
|||||||
'party.quest.progress.down': 0,
|
'party.quest.progress.down': 0,
|
||||||
'party.quest.completed': null,
|
'party.quest.completed': null,
|
||||||
},
|
},
|
||||||
}, { multi: true }).exec();
|
}, { multi: true }).exec());
|
||||||
|
|
||||||
|
await Promise.all(promises);
|
||||||
|
|
||||||
// update the users who are not participating
|
// update the users who are not participating
|
||||||
// Do not block updates
|
// Do not block updates
|
||||||
@@ -703,38 +715,45 @@ schema.methods.startQuest = async function startQuest (user) {
|
|||||||
},
|
},
|
||||||
}, { multi: true }).exec();
|
}, { multi: true }).exec();
|
||||||
|
|
||||||
// send notifications in the background without blocking
|
|
||||||
User.find(
|
|
||||||
{ _id: { $in: nonUserQuestMembers } },
|
|
||||||
'party.quest items.quests auth.facebook auth.local preferences.emailNotifications preferences.pushNotifications pushDevices profile.name'
|
|
||||||
).exec().then((membersToNotify) => {
|
|
||||||
let membersToEmail = _.filter(membersToNotify, (member) => {
|
|
||||||
// send push notifications and filter users that disabled emails
|
|
||||||
return member.preferences.emailNotifications.questStarted !== false &&
|
|
||||||
member._id !== user._id;
|
|
||||||
});
|
|
||||||
sendTxnEmail(membersToEmail, 'quest-started', [
|
|
||||||
{ name: 'PARTY_URL', content: '/party' },
|
|
||||||
]);
|
|
||||||
let membersToPush = _.filter(membersToNotify, (member) => {
|
|
||||||
// send push notifications and filter users that disabled emails
|
|
||||||
return member.preferences.pushNotifications.questStarted !== false &&
|
|
||||||
member._id !== user._id;
|
|
||||||
});
|
|
||||||
_.each(membersToPush, (member) => {
|
|
||||||
sendPushNotification(member,
|
|
||||||
{
|
|
||||||
title: quest.text(),
|
|
||||||
message: `${shared.i18n.t('questStarted')}: ${quest.text()}`,
|
|
||||||
identifier: 'questStarted',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
const newMessage = this.sendChat(`\`Your quest, ${quest.text('en')}, has started.\``, null, {
|
const newMessage = this.sendChat(`\`Your quest, ${quest.text('en')}, has started.\``, null, {
|
||||||
participatingMembers: this.getParticipatingQuestMembers().join(', '),
|
participatingMembers: this.getParticipatingQuestMembers().join(', '),
|
||||||
});
|
});
|
||||||
|
|
||||||
await newMessage.save();
|
await newMessage.save();
|
||||||
|
|
||||||
|
const membersToEmail = [];
|
||||||
|
const pushTitle = quest.text();
|
||||||
|
const pushMessage = `${shared.i18n.t('questStarted')}: ${quest.text()}`;
|
||||||
|
|
||||||
|
// send notifications and webhooks in the background without blocking
|
||||||
|
members.forEach(member => {
|
||||||
|
if (member._id !== user._id) {
|
||||||
|
// send push notifications and filter users that disabled emails
|
||||||
|
if (member.preferences.emailNotifications.questStarted !== false) {
|
||||||
|
membersToEmail.push(member);
|
||||||
|
}
|
||||||
|
|
||||||
|
// send push notifications and filter users that disabled emails
|
||||||
|
if (member.preferences.pushNotifications.questStarted !== false) {
|
||||||
|
sendPushNotification(member, {
|
||||||
|
title: pushTitle,
|
||||||
|
message: pushMessage,
|
||||||
|
identifier: 'questStarted',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send webhooks
|
||||||
|
questActivityWebhook.send(member, {
|
||||||
|
type: 'questStarted',
|
||||||
|
group: this,
|
||||||
|
quest,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Send emails in bulk
|
||||||
|
sendTxnEmail(membersToEmail, 'quest-started', [
|
||||||
|
{ name: 'PARTY_URL', content: '/party' },
|
||||||
|
]);
|
||||||
};
|
};
|
||||||
|
|
||||||
schema.methods.sendGroupChatReceivedWebhooks = function sendGroupChatReceivedWebhooks (chat) {
|
schema.methods.sendGroupChatReceivedWebhooks = function sendGroupChatReceivedWebhooks (chat) {
|
||||||
@@ -753,15 +772,14 @@ schema.methods.sendGroupChatReceivedWebhooks = function sendGroupChatReceivedWeb
|
|||||||
query.guilds = this._id;
|
query.guilds = this._id;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* User.find(query).select({webhooks: 1}).lean().exec().then((users) => {
|
User.find(query).select({webhooks: 1}).lean().exec().then((users) => {
|
||||||
users.forEach((user) => {
|
users.forEach((user) => {
|
||||||
let { webhooks } = user;
|
groupChatReceivedWebhook.send(user, {
|
||||||
groupChatReceivedWebhook.send(webhooks, {
|
|
||||||
group: this,
|
group: this,
|
||||||
chat,
|
chat,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}); */
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
schema.statics.cleanQuestProgress = _cleanQuestProgress;
|
schema.statics.cleanQuestProgress = _cleanQuestProgress;
|
||||||
@@ -907,6 +925,31 @@ schema.methods.finishQuest = async function finishQuest (quest) {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Send webhooks in background
|
||||||
|
// @TODO move the find users part to a worker as well, not just the http request
|
||||||
|
User.find({
|
||||||
|
_id: {$in: participants},
|
||||||
|
webhooks: {
|
||||||
|
$elemMatch: {
|
||||||
|
type: 'questActivity',
|
||||||
|
'options.questFinished': true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.select('_id webhooks')
|
||||||
|
.lean()
|
||||||
|
.exec()
|
||||||
|
.then(participantsWithWebhook => {
|
||||||
|
participantsWithWebhook.forEach(participantWithWebhook => {
|
||||||
|
// Send webhooks
|
||||||
|
questActivityWebhook.send(participantWithWebhook, {
|
||||||
|
type: 'questFinished',
|
||||||
|
group: this,
|
||||||
|
quest,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
return await Promise.all(promises);
|
return await Promise.all(promises);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user