mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-14 13:17:24 +01:00
start linting server
This commit is contained in:
@@ -72,7 +72,7 @@
|
|||||||
"npm": "^6"
|
"npm": "^6"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"lint": "eslint --ext .js --fix ./website/common",
|
"lint": "eslint --ext .js --fix ./website/server",
|
||||||
"test": "npm run lint && gulp test && gulp apidoc",
|
"test": "npm run lint && gulp test && gulp apidoc",
|
||||||
"test:build": "gulp test:prepare:build",
|
"test:build": "gulp test:prepare:build",
|
||||||
"test:api-v3": "gulp test:api-v3",
|
"test:api-v3": "gulp test:api-v3",
|
||||||
|
|||||||
@@ -89,12 +89,14 @@ schema.methods.canModify = function canModifyChallenge (user) {
|
|||||||
// Returns true if user can join the challenge
|
// Returns true if user can join the challenge
|
||||||
schema.methods.canJoin = function canJoinChallenge (user, group) {
|
schema.methods.canJoin = function canJoinChallenge (user, group) {
|
||||||
if (group.type === 'guild' && group.privacy === 'public') return true;
|
if (group.type === 'guild' && group.privacy === 'public') return true;
|
||||||
if (this.isLeader(user)) return true; // for when leader has left private group that contains the challenge
|
// for when leader has left private group that contains the challenge
|
||||||
|
if (this.isLeader(user)) return true;
|
||||||
return user.getGroups().indexOf(this.group) !== -1;
|
return user.getGroups().indexOf(this.group) !== -1;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Returns true if user can view the challenge
|
// Returns true if user can view the challenge
|
||||||
// Different from canJoin because you can see challenges of groups you've been removed from if you're participating in them
|
// Different from canJoin because you can see challenges of groups
|
||||||
|
// you've been removed from if you're participating in them
|
||||||
schema.methods.canView = function canViewChallenge (user, group) {
|
schema.methods.canView = function canViewChallenge (user, group) {
|
||||||
if (this.isMember(user)) return true;
|
if (this.isMember(user)) return true;
|
||||||
return this.canJoin(user, group);
|
return this.canJoin(user, group);
|
||||||
@@ -108,7 +110,8 @@ schema.methods.syncToUser = async function syncChallengeToUser (user) {
|
|||||||
|
|
||||||
// Add challenge to user.challenges
|
// Add challenge to user.challenges
|
||||||
if (!_.includes(user.challenges, challenge._id)) {
|
if (!_.includes(user.challenges, challenge._id)) {
|
||||||
// using concat because mongoose's protection against concurrent array modification isn't working as expected.
|
// using concat because mongoose's protection against
|
||||||
|
// concurrent array modification isn't working as expected.
|
||||||
// see https://github.com/HabitRPG/habitica/pull/7787#issuecomment-232972394
|
// see https://github.com/HabitRPG/habitica/pull/7787#issuecomment-232972394
|
||||||
user.challenges = user.challenges.concat([challenge._id]);
|
user.challenges = user.challenges.concat([challenge._id]);
|
||||||
}
|
}
|
||||||
@@ -119,7 +122,8 @@ schema.methods.syncToUser = async function syncChallengeToUser (user) {
|
|||||||
if (i !== -1) {
|
if (i !== -1) {
|
||||||
if (userTags[i].name !== challenge.shortName) {
|
if (userTags[i].name !== challenge.shortName) {
|
||||||
// update the name - it's been changed since
|
// update the name - it's been changed since
|
||||||
// @TODO: We probably want to remove this. Owner is not allowed to change participant's copy of the tag.
|
// @TODO: We probably want to remove this.
|
||||||
|
// Owner is not allowed to change participant's copy of the tag.
|
||||||
userTags[i].name = challenge.shortName;
|
userTags[i].name = challenge.shortName;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -150,7 +154,11 @@ schema.methods.syncToUser = async function syncChallengeToUser (user) {
|
|||||||
|
|
||||||
if (!matchingTask) { // If the task is new, create it
|
if (!matchingTask) { // If the task is new, create it
|
||||||
matchingTask = new Tasks[chalTask.type](Tasks.Task.sanitize(syncableAttrs(chalTask)));
|
matchingTask = new Tasks[chalTask.type](Tasks.Task.sanitize(syncableAttrs(chalTask)));
|
||||||
matchingTask.challenge = { taskId: chalTask._id, id: challenge._id, shortName: challenge.shortName };
|
matchingTask.challenge = {
|
||||||
|
taskId: chalTask._id,
|
||||||
|
id: challenge._id,
|
||||||
|
shortName: challenge.shortName,
|
||||||
|
};
|
||||||
matchingTask.userId = user._id;
|
matchingTask.userId = user._id;
|
||||||
user.tasksOrder[`${chalTask.type}s`].push(matchingTask._id);
|
user.tasksOrder[`${chalTask.type}s`].push(matchingTask._id);
|
||||||
setNextDue(matchingTask, user);
|
setNextDue(matchingTask, user);
|
||||||
@@ -161,8 +169,10 @@ schema.methods.syncToUser = async function syncChallengeToUser (user) {
|
|||||||
if (orderList.indexOf(matchingTask._id) === -1 && (matchingTask.type !== 'todo' || !matchingTask.completed)) orderList.push(matchingTask._id);
|
if (orderList.indexOf(matchingTask._id) === -1 && (matchingTask.type !== 'todo' || !matchingTask.completed)) orderList.push(matchingTask._id);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!matchingTask.notes) matchingTask.notes = chalTask.notes; // don't override the notes, but provide it if not provided
|
// don't override the notes, but provide it if not provided
|
||||||
if (matchingTask.tags.indexOf(challenge._id) === -1) matchingTask.tags.push(challenge._id); // add tag if missing
|
if (!matchingTask.notes) matchingTask.notes = chalTask.notes;
|
||||||
|
// add tag if missing
|
||||||
|
if (matchingTask.tags.indexOf(challenge._id) === -1) matchingTask.tags.push(challenge._id);
|
||||||
toSave.push(matchingTask.save());
|
toSave.push(matchingTask.save());
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -188,7 +198,11 @@ async function _addTaskFn (challenge, tasks, memberId) {
|
|||||||
|
|
||||||
tasks.forEach(chalTask => {
|
tasks.forEach(chalTask => {
|
||||||
const userTask = new Tasks[chalTask.type](Tasks.Task.sanitize(syncableAttrs(chalTask)));
|
const userTask = new Tasks[chalTask.type](Tasks.Task.sanitize(syncableAttrs(chalTask)));
|
||||||
userTask.challenge = { taskId: chalTask._id, id: challenge._id, shortName: challenge.shortName };
|
userTask.challenge = {
|
||||||
|
taskId: chalTask._id,
|
||||||
|
id: challenge._id,
|
||||||
|
shortName: challenge.shortName,
|
||||||
|
};
|
||||||
userTask.userId = memberId;
|
userTask.userId = memberId;
|
||||||
|
|
||||||
// We want to sync the notes and tags when the task is first added to the challenge
|
// We want to sync the notes and tags when the task is first added to the challenge
|
||||||
@@ -212,7 +226,7 @@ async function _addTaskFn (challenge, tasks, memberId) {
|
|||||||
|
|
||||||
// Update the user
|
// Update the user
|
||||||
toSave.unshift(User.update({ _id: memberId }, updateTasksOrderQ).exec());
|
toSave.unshift(User.update({ _id: memberId }, updateTasksOrderQ).exec());
|
||||||
return await Promise.all(toSave);
|
return Promise.all(toSave);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add a new task to challenge members
|
// Add a new task to challenge members
|
||||||
@@ -237,7 +251,8 @@ schema.methods.updateTask = async function challengeUpdateTask (task) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const taskSchema = Tasks[task.type];
|
const taskSchema = Tasks[task.type];
|
||||||
// Updating instead of loading and saving for performances, risks becoming a problem if we introduce more complexity in tasks
|
// Updating instead of loading and saving for performances,
|
||||||
|
// risks becoming a problem if we introduce more complexity in tasks
|
||||||
await taskSchema.update({
|
await taskSchema.update({
|
||||||
userId: { $exists: true },
|
userId: { $exists: true },
|
||||||
'challenge.id': challenge.id,
|
'challenge.id': challenge.id,
|
||||||
@@ -268,7 +283,7 @@ schema.methods.unlinkTasks = async function challengeUnlinkTasks (user, keep, sa
|
|||||||
};
|
};
|
||||||
|
|
||||||
removeFromArray(user.challenges, challengeId);
|
removeFromArray(user.challenges, challengeId);
|
||||||
this.memberCount--;
|
this.memberCount -= 1;
|
||||||
|
|
||||||
if (keep === 'keep-all') {
|
if (keep === 'keep-all') {
|
||||||
await Tasks.Task.update(findQuery, {
|
await Tasks.Task.update(findQuery, {
|
||||||
@@ -303,7 +318,8 @@ schema.methods.unlinkTasks = async function challengeUnlinkTasks (user, keep, sa
|
|||||||
};
|
};
|
||||||
|
|
||||||
// TODO everything here should be moved to a worker
|
// TODO everything here should be moved to a worker
|
||||||
// actually even for a worker it's probably just too big and will kill mongo, figure out something else
|
// actually even for a worker it's probably just too big
|
||||||
|
// and will kill mongo, figure out something else
|
||||||
schema.methods.closeChal = async function closeChal (broken = {}) {
|
schema.methods.closeChal = async function closeChal (broken = {}) {
|
||||||
const challenge = this;
|
const challenge = this;
|
||||||
|
|
||||||
@@ -329,7 +345,10 @@ schema.methods.closeChal = async function closeChal (broken = {}) {
|
|||||||
// reimburse the leader
|
// reimburse the leader
|
||||||
const winnerCanGetGems = await winner.canGetGems();
|
const winnerCanGetGems = await winner.canGetGems();
|
||||||
if (!winnerCanGetGems) {
|
if (!winnerCanGetGems) {
|
||||||
await User.update({ _id: challenge.leader }, { $inc: { balance: challenge.prize / 4 } }).exec();
|
await User.update(
|
||||||
|
{ _id: challenge.leader },
|
||||||
|
{ $inc: { balance: challenge.prize / 4 } },
|
||||||
|
).exec();
|
||||||
} else {
|
} else {
|
||||||
winner.balance += challenge.prize / 4;
|
winner.balance += challenge.prize / 4;
|
||||||
}
|
}
|
||||||
@@ -357,7 +376,8 @@ schema.methods.closeChal = async function closeChal (broken = {}) {
|
|||||||
const backgroundTasks = [
|
const backgroundTasks = [
|
||||||
// And it's tasks
|
// And it's tasks
|
||||||
Tasks.Task.remove({ 'challenge.id': challenge._id, userId: { $exists: false } }).exec(),
|
Tasks.Task.remove({ 'challenge.id': challenge._id, userId: { $exists: false } }).exec(),
|
||||||
// Set the challenge tag to non-challenge status and remove the challenge from the user's challenges
|
// Set the challenge tag to non-challenge status
|
||||||
|
// and remove the challenge from the user's challenges
|
||||||
User.update({
|
User.update({
|
||||||
challenges: challenge._id,
|
challenges: challenge._id,
|
||||||
'tags.id': challenge._id,
|
'tags.id': challenge._id,
|
||||||
@@ -379,4 +399,4 @@ schema.methods.closeChal = async function closeChal (broken = {}) {
|
|||||||
Promise.all(backgroundTasks);
|
Promise.all(backgroundTasks);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const model = mongoose.model('Challenge', schema);
|
export const model = mongoose.model('Challenge', schema); // eslint-disable-line import/prefer-default-export
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ schema.plugin(baseModel, {
|
|||||||
schema.statics.generate = async function generateCoupons (event, count = 1) {
|
schema.statics.generate = async function generateCoupons (event, count = 1) {
|
||||||
const coupons = _.times(count, () => ({ event }));
|
const coupons = _.times(count, () => ({ event }));
|
||||||
|
|
||||||
return await this.create(coupons);
|
return this.create(coupons);
|
||||||
};
|
};
|
||||||
|
|
||||||
schema.statics.apply = async function applyCoupon (user, req, code) {
|
schema.statics.apply = async function applyCoupon (user, req, code) {
|
||||||
|
|||||||
@@ -204,14 +204,14 @@ function _cleanQuestParty (merge) {
|
|||||||
// return a clean user.quest of a particular user while keeping his progress
|
// return a clean user.quest of a particular user while keeping his progress
|
||||||
function _cleanQuestUser (userProgress) {
|
function _cleanQuestUser (userProgress) {
|
||||||
if (!userProgress) {
|
if (!userProgress) {
|
||||||
userProgress = {
|
userProgress = { // eslint-disable-line no-param-reassign
|
||||||
up: 0,
|
up: 0,
|
||||||
down: 0,
|
down: 0,
|
||||||
collect: {},
|
collect: {},
|
||||||
collectedItems: 0,
|
collectedItems: 0,
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
userProgress = userProgress.toObject();
|
userProgress = userProgress.toObject(); // eslint-disable-line no-param-reassign
|
||||||
}
|
}
|
||||||
|
|
||||||
const clean = {
|
const clean = {
|
||||||
@@ -226,7 +226,8 @@ function _cleanQuestUser (userProgress) {
|
|||||||
|
|
||||||
schema.statics.getGroup = async function getGroup (options = {}) {
|
schema.statics.getGroup = async function getGroup (options = {}) {
|
||||||
const {
|
const {
|
||||||
user, groupId, fields, optionalMembership = false, populateLeader = false, requireMembership = false,
|
user, groupId, fields, optionalMembership = false,
|
||||||
|
populateLeader = false, requireMembership = false,
|
||||||
} = options;
|
} = options;
|
||||||
let query;
|
let query;
|
||||||
|
|
||||||
@@ -286,7 +287,7 @@ schema.statics.getGroups = async function getGroups (options = {}) {
|
|||||||
if (!areValidTypes) throw new BadRequest(shared.i18n.t('groupTypesRequired'));
|
if (!areValidTypes) throw new BadRequest(shared.i18n.t('groupTypesRequired'));
|
||||||
|
|
||||||
types.forEach(type => {
|
types.forEach(type => {
|
||||||
switch (type) {
|
switch (type) { // eslint-disable-line default-case
|
||||||
case 'party': {
|
case 'party': {
|
||||||
queries.push(this.getGroup({
|
queries.push(this.getGroup({
|
||||||
user, groupId: 'party', fields: groupFields, populateLeader,
|
user, groupId: 'party', fields: groupFields, populateLeader,
|
||||||
@@ -318,7 +319,8 @@ schema.statics.getGroups = async function getGroups (options = {}) {
|
|||||||
queries.push(privateGuildsQuery);
|
queries.push(privateGuildsQuery);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// NOTE: when returning publicGuilds we use `.lean()` so all mongoose methods won't be available.
|
// NOTE: when returning publicGuilds we use `.lean()` so all
|
||||||
|
// mongoose methods won't be available.
|
||||||
// Docs are going to be plain javascript objects
|
// Docs are going to be plain javascript objects
|
||||||
case 'publicGuilds': {
|
case 'publicGuilds': {
|
||||||
const query = {
|
const query = {
|
||||||
@@ -326,11 +328,16 @@ schema.statics.getGroups = async function getGroups (options = {}) {
|
|||||||
privacy: 'public',
|
privacy: 'public',
|
||||||
};
|
};
|
||||||
_.assign(query, filters);
|
_.assign(query, filters);
|
||||||
|
|
||||||
const publicGuildsQuery = this.find(query).select(groupFields);
|
const publicGuildsQuery = this.find(query).select(groupFields);
|
||||||
|
|
||||||
if (populateLeader === true) publicGuildsQuery.populate('leader', nameFields);
|
if (populateLeader === true) publicGuildsQuery.populate('leader', nameFields);
|
||||||
if (paginate === true) publicGuildsQuery.limit(GUILDS_PER_PAGE).skip(page * GUILDS_PER_PAGE);
|
if (paginate === true) {
|
||||||
|
publicGuildsQuery.limit(GUILDS_PER_PAGE).skip(page * GUILDS_PER_PAGE);
|
||||||
|
}
|
||||||
publicGuildsQuery.sort(sort).lean().exec();
|
publicGuildsQuery.sort(sort).lean().exec();
|
||||||
queries.push(publicGuildsQuery);
|
queries.push(publicGuildsQuery);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'tavern': {
|
case 'tavern': {
|
||||||
@@ -343,8 +350,10 @@ schema.statics.getGroups = async function getGroups (options = {}) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const groupsArray = _.reduce(await Promise.all(queries), (previousValue, currentValue) => {
|
const groupsArray = _.reduce(await Promise.all(queries), (previousValue, currentValue) => {
|
||||||
if (_.isEmpty(currentValue)) return previousValue; // don't add anything to the results if the query returned null or an empty array
|
// don't add anything to the results if the query returned null or an empty array
|
||||||
return previousValue.concat(Array.isArray(currentValue) ? currentValue : [currentValue]); // otherwise concat the new results to the previousValue
|
if (_.isEmpty(currentValue)) return previousValue;
|
||||||
|
// otherwise concat the new results to the previousValue
|
||||||
|
return previousValue.concat(Array.isArray(currentValue) ? currentValue : [currentValue]);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return groupsArray;
|
return groupsArray;
|
||||||
@@ -355,7 +364,8 @@ schema.statics.getGroups = async function getGroups (options = {}) {
|
|||||||
// Not putting into toJSON because there we can't access user
|
// Not putting into toJSON because there we can't access user
|
||||||
// It also removes the _meta field that can be stored inside a chat message
|
// It also removes the _meta field that can be stored inside a chat message
|
||||||
schema.statics.toJSONCleanChat = async function groupToJSONCleanChat (group, user) {
|
schema.statics.toJSONCleanChat = async function groupToJSONCleanChat (group, user) {
|
||||||
// @TODO: Adding this here for support the old chat, but we should depreciate accessing chat like this
|
// @TODO: Adding this here for support the old chat,
|
||||||
|
// but we should depreciate accessing chat like this
|
||||||
// Also only return chat if requested, eventually we don't want to return chat here
|
// Also only return chat if requested, eventually we don't want to return chat here
|
||||||
if (group && group.chat) {
|
if (group && group.chat) {
|
||||||
await getGroupChat(group);
|
await getGroupChat(group);
|
||||||
@@ -384,7 +394,9 @@ schema.statics.toJSONCleanChat = async function groupToJSONCleanChat (group, use
|
|||||||
if (chatMsg._meta) chatMsg._meta = undefined;
|
if (chatMsg._meta) chatMsg._meta = undefined;
|
||||||
|
|
||||||
// Messages with too many flags are hidden to non-admins and non-authors
|
// Messages with too many flags are hidden to non-admins and non-authors
|
||||||
if (user._id !== chatMsg.uuid && chatMsg.flagCount >= CHAT_FLAG_LIMIT_FOR_HIDING) return undefined;
|
if (user._id !== chatMsg.uuid && chatMsg.flagCount >= CHAT_FLAG_LIMIT_FOR_HIDING) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return chatMsg;
|
return chatMsg;
|
||||||
@@ -442,7 +454,7 @@ function getInviteCount (uuids, emails) {
|
|||||||
* @param res Express res object for use with translations
|
* @param res Express res object for use with translations
|
||||||
* @throws BadRequest An error describing the issue with the invitations
|
* @throws BadRequest An error describing the issue with the invitations
|
||||||
*/
|
*/
|
||||||
schema.statics.validateInvitations = async function getInvitationError (invites, res, group = null) {
|
schema.statics.validateInvitations = async function getInvitationErr (invites, res, group = null) {
|
||||||
const {
|
const {
|
||||||
uuids,
|
uuids,
|
||||||
emails,
|
emails,
|
||||||
@@ -493,7 +505,8 @@ schema.methods.removeGroupInvitations = async function removeGroupInvitations ()
|
|||||||
const userUpdates = usersToRemoveInvitationsFrom.map(user => {
|
const userUpdates = usersToRemoveInvitationsFrom.map(user => {
|
||||||
if (group.type === 'party') {
|
if (group.type === 'party') {
|
||||||
removeFromArray(user.invitations.parties, { id: group._id });
|
removeFromArray(user.invitations.parties, { id: group._id });
|
||||||
user.invitations.party = user.invitations.parties.length > 0 ? user.invitations.parties[user.invitations.parties.length - 1] : {};
|
user.invitations.party = user.invitations.parties.length > 0
|
||||||
|
? user.invitations.parties[user.invitations.parties.length - 1] : {};
|
||||||
this.markModified('invitations.party');
|
this.markModified('invitations.party');
|
||||||
} else {
|
} else {
|
||||||
removeFromArray(user.invitations.guilds, { id: group._id });
|
removeFromArray(user.invitations.guilds, { id: group._id });
|
||||||
@@ -521,7 +534,7 @@ schema.methods.getMemberCount = async function getMemberCount () {
|
|||||||
query = { 'party._id': this._id };
|
query = { 'party._id': this._id };
|
||||||
}
|
}
|
||||||
|
|
||||||
return await User.count(query).exec();
|
return User.count(query).exec();
|
||||||
};
|
};
|
||||||
|
|
||||||
schema.methods.sendChat = function sendChat (options = {}) {
|
schema.methods.sendChat = function sendChat (options = {}) {
|
||||||
@@ -549,7 +562,11 @@ schema.methods.sendChat = function sendChat (options = {}) {
|
|||||||
// - groups that never send notifications (e.g., Tavern)
|
// - groups that never send notifications (e.g., Tavern)
|
||||||
// - groups with very many users
|
// - groups with very many users
|
||||||
// - messages that have already been flagged to hide them
|
// - messages that have already been flagged to hide them
|
||||||
if (NO_CHAT_NOTIFICATIONS.indexOf(this._id) !== -1 || this.memberCount > LARGE_GROUP_COUNT_MESSAGE_CUTOFF || newChatMessage.flagCount >= CHAT_FLAG_LIMIT_FOR_HIDING) {
|
if (
|
||||||
|
NO_CHAT_NOTIFICATIONS.indexOf(this._id) !== -1
|
||||||
|
|| this.memberCount > LARGE_GROUP_COUNT_MESSAGE_CUTOFF
|
||||||
|
|| newChatMessage.flagCount >= CHAT_FLAG_LIMIT_FOR_HIDING
|
||||||
|
) {
|
||||||
return newChatMessage;
|
return newChatMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -597,7 +614,8 @@ schema.methods.sendChat = function sendChat (options = {}) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
schema.methods.startQuest = async function startQuest (user) {
|
schema.methods.startQuest = async function startQuest (user) {
|
||||||
// not using i18n strings because these errors are meant for devs who forgot to pass some parameters
|
// not using i18n strings because these errors are meant
|
||||||
|
// for devs who forgot to pass some parameters
|
||||||
if (this.type !== 'party') throw new InternalServerError('Must be a party to use this method');
|
if (this.type !== 'party') throw new InternalServerError('Must be a party to use this method');
|
||||||
if (!this.quest.key) throw new InternalServerError('Party does not have a pending quest');
|
if (!this.quest.key) throw new InternalServerError('Party does not have a pending quest');
|
||||||
if (this.quest.active) throw new InternalServerError('Quest is already active');
|
if (this.quest.active) throw new InternalServerError('Quest is already active');
|
||||||
@@ -786,7 +804,7 @@ function _getUserUpdateForQuestReward (itemToAward, allAwardedItems) {
|
|||||||
};
|
};
|
||||||
const dropK = itemToAward.key;
|
const dropK = itemToAward.key;
|
||||||
|
|
||||||
switch (itemToAward.type) {
|
switch (itemToAward.type) { // eslint-disable-line default-case
|
||||||
case 'gear': {
|
case 'gear': {
|
||||||
// TODO This means they can lose their new gear on death, is that what we want?
|
// TODO This means they can lose their new gear on death, is that what we want?
|
||||||
updates.$set[`items.gear.owned.${dropK}`] = true;
|
updates.$set[`items.gear.owned.${dropK}`] = true;
|
||||||
@@ -860,7 +878,7 @@ schema.methods.finishQuest = async function finishQuest (quest) {
|
|||||||
this.markModified('quest');
|
this.markModified('quest');
|
||||||
|
|
||||||
if (this._id === TAVERN_ID) {
|
if (this._id === TAVERN_ID) {
|
||||||
return await User.update({}, updates, { multi: true }).exec();
|
return User.update({}, updates, { multi: true }).exec();
|
||||||
}
|
}
|
||||||
|
|
||||||
const promises = participants.map(userId => {
|
const promises = participants.map(userId => {
|
||||||
@@ -921,15 +939,18 @@ schema.methods.finishQuest = async function finishQuest (quest) {
|
|||||||
}).toObject(),
|
}).toObject(),
|
||||||
};
|
};
|
||||||
|
|
||||||
promises.push(participants.map(userId => _updateUserWithRetries(userId, questAchievementUpdate, null, questAchievementQuery)));
|
promises.push(participants.map(userId => _updateUserWithRetries(
|
||||||
|
userId, questAchievementUpdate, null, questAchievementQuery,
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
await Promise.all(promises);
|
return Promise.all(promises);
|
||||||
};
|
};
|
||||||
|
|
||||||
function _isOnQuest (user, progress, group) {
|
function _isOnQuest (user, progress, group) {
|
||||||
return group && progress && group.quest && group.quest.active && group.quest.members[user._id] === true;
|
return group && progress && group.quest && group.quest.active
|
||||||
|
&& group.quest.members[user._id] === true;
|
||||||
}
|
}
|
||||||
|
|
||||||
schema.methods._processBossQuest = async function processBossQuest (options) {
|
schema.methods._processBossQuest = async function processBossQuest (options) {
|
||||||
@@ -989,8 +1010,11 @@ schema.methods._processBossQuest = async function processBossQuest (options) {
|
|||||||
promises.push(rageMessage.save());
|
promises.push(rageMessage.save());
|
||||||
group.quest.progress.rage = 0;
|
group.quest.progress.rage = 0;
|
||||||
|
|
||||||
// TODO To make Rage effects more expandable, let's turn these into functions in quest.boss.rage
|
// TODO To make Rage effects more expandable,
|
||||||
if (quest.boss.rage.healing) group.quest.progress.hp += group.quest.progress.hp * quest.boss.rage.healing;
|
// let's turn these into functions in quest.boss.rage
|
||||||
|
if (quest.boss.rage.healing) {
|
||||||
|
group.quest.progress.hp += group.quest.progress.hp * quest.boss.rage.healing;
|
||||||
|
}
|
||||||
if (group.quest.progress.hp > quest.boss.hp) group.quest.progress.hp = quest.boss.hp;
|
if (group.quest.progress.hp > quest.boss.hp) group.quest.progress.hp = quest.boss.hp;
|
||||||
if (quest.boss.rage.mpDrain) {
|
if (quest.boss.rage.mpDrain) {
|
||||||
updates.$set = { 'stats.mp': 0 };
|
updates.$set = { 'stats.mp': 0 };
|
||||||
@@ -1006,10 +1030,13 @@ schema.methods._processBossQuest = async function processBossQuest (options) {
|
|||||||
updates,
|
updates,
|
||||||
{ multi: true },
|
{ multi: true },
|
||||||
).exec();
|
).exec();
|
||||||
// Apply changes the currently cronning user locally so we don't have to reload it to get the updated state
|
// Apply changes the currently cronning user locally
|
||||||
|
// so we don't have to reload it to get the updated state
|
||||||
// TODO how to mark not modified? https://github.com/Automattic/mongoose/pull/1167
|
// TODO how to mark not modified? https://github.com/Automattic/mongoose/pull/1167
|
||||||
// must be notModified or otherwise could overwrite future changes: if the user is saved it'll save
|
// must be notModified or otherwise could overwrite future changes:
|
||||||
// the modified user.stats.hp but that must not happen as the hp value has already been updated by the User.update above
|
// if the user is saved it'll save
|
||||||
|
// the modified user.stats.hp but that must not happen as the hp value has already been updated
|
||||||
|
// by the User.update above
|
||||||
// if (down) user.stats.hp += down;
|
// if (down) user.stats.hp += down;
|
||||||
|
|
||||||
// Boss slain, finish quest
|
// Boss slain, finish quest
|
||||||
@@ -1028,7 +1055,7 @@ schema.methods._processBossQuest = async function processBossQuest (options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
promises.unshift(group.save());
|
promises.unshift(group.save());
|
||||||
return await Promise.all(promises);
|
return Promise.all(promises);
|
||||||
};
|
};
|
||||||
|
|
||||||
schema.methods._processCollectionQuest = async function processCollectionQuest (options) {
|
schema.methods._processCollectionQuest = async function processCollectionQuest (options) {
|
||||||
@@ -1041,7 +1068,8 @@ schema.methods._processCollectionQuest = async function processCollectionQuest (
|
|||||||
const quest = questScrolls[group.quest.key];
|
const quest = questScrolls[group.quest.key];
|
||||||
const itemsFound = {};
|
const itemsFound = {};
|
||||||
|
|
||||||
const possibleItemKeys = Object.keys(quest.collect).filter(key => group.quest.progress.collect[key] !== quest.collect[key].count);
|
const possibleItemKeys = Object.keys(quest.collect)
|
||||||
|
.filter(key => group.quest.progress.collect[key] !== quest.collect[key].count);
|
||||||
|
|
||||||
const possibleItemsToCollect = possibleItemKeys.reduce((accumulator, current, index) => {
|
const possibleItemsToCollect = possibleItemKeys.reduce((accumulator, current, index) => {
|
||||||
accumulator[possibleItemKeys[index]] = quest.collect[current];
|
accumulator[possibleItemKeys[index]] = quest.collect[current];
|
||||||
@@ -1054,8 +1082,8 @@ schema.methods._processCollectionQuest = async function processCollectionQuest (
|
|||||||
if (!itemsFound[item]) {
|
if (!itemsFound[item]) {
|
||||||
itemsFound[item] = 0;
|
itemsFound[item] = 0;
|
||||||
}
|
}
|
||||||
itemsFound[item]++;
|
itemsFound[item] += 1;
|
||||||
group.quest.progress.collect[item]++;
|
group.quest.progress.collect[item] += 1;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add 0 for all items not found
|
// Add 0 for all items not found
|
||||||
@@ -1086,7 +1114,7 @@ schema.methods._processCollectionQuest = async function processCollectionQuest (
|
|||||||
const needsCompleted = _.find(quest.collect, (v, k) => group.quest.progress.collect[k] < v.count);
|
const needsCompleted = _.find(quest.collect, (v, k) => group.quest.progress.collect[k] < v.count);
|
||||||
|
|
||||||
if (needsCompleted) {
|
if (needsCompleted) {
|
||||||
return await Promise.all([group.save(), foundChat.save()]);
|
return Promise.all([group.save(), foundChat.save()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
await group.finishQuest(quest);
|
await group.finishQuest(quest);
|
||||||
@@ -1099,7 +1127,7 @@ schema.methods._processCollectionQuest = async function processCollectionQuest (
|
|||||||
|
|
||||||
const promises = [group.save(), foundChat.save(), allItemsFoundChat.save()];
|
const promises = [group.save(), foundChat.save(), allItemsFoundChat.save()];
|
||||||
|
|
||||||
return await Promise.all(promises);
|
return Promise.all(promises);
|
||||||
};
|
};
|
||||||
|
|
||||||
schema.statics.processQuestProgress = async function processQuestProgress (user, progress) {
|
schema.statics.processQuestProgress = async function processQuestProgress (user, progress) {
|
||||||
@@ -1122,7 +1150,9 @@ schema.statics.processQuestProgress = async function processQuestProgress (user,
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// to set a boss: `db.groups.update({_id:TAVERN_ID},{$set:{quest:{key:'dilatory',active:true,progress:{hp:1000,rage:1500}}}}).exec()`
|
// to set a boss:
|
||||||
|
// `db.groups.update({_id:TAVERN_ID},
|
||||||
|
// {$set:{quest:{key:'dilatory',active:true,progress:{hp:1000,rage:1500}}}}).exec()`
|
||||||
// we export an empty object that is then populated with the query-returned data
|
// we export an empty object that is then populated with the query-returned data
|
||||||
export const tavernQuest = {};
|
export const tavernQuest = {};
|
||||||
const tavernQ = { _id: TAVERN_ID, 'quest.key': { $ne: null } };
|
const tavernQ = { _id: TAVERN_ID, 'quest.key': { $ne: null } };
|
||||||
@@ -1144,15 +1174,15 @@ process.nextTick(() => {
|
|||||||
|
|
||||||
// returns a promise
|
// returns a promise
|
||||||
schema.statics.tavernBoss = async function tavernBoss (user, progress) {
|
schema.statics.tavernBoss = async function tavernBoss (user, progress) {
|
||||||
if (!progress) return;
|
if (!progress) return null;
|
||||||
if (user.preferences.sleep) return;
|
if (user.preferences.sleep) return null;
|
||||||
|
|
||||||
// hack: prevent crazy damage to world boss
|
// hack: prevent crazy damage to world boss
|
||||||
const dmg = Math.min(900, Math.abs(progress.up || 0));
|
const dmg = Math.min(900, Math.abs(progress.up || 0));
|
||||||
const rage = -Math.min(900, Math.abs(progress.down || 0));
|
const rage = -Math.min(900, Math.abs(progress.down || 0));
|
||||||
|
|
||||||
const tavern = await this.findOne(tavernQ).exec();
|
const tavern = await this.findOne(tavernQ).exec();
|
||||||
if (!(tavern && tavern.quest && tavern.quest.key)) return;
|
if (!(tavern && tavern.quest && tavern.quest.key)) return null;
|
||||||
|
|
||||||
const quest = shared.content.quests[tavern.quest.key];
|
const quest = shared.content.quests[tavern.quest.key];
|
||||||
|
|
||||||
@@ -1171,7 +1201,9 @@ schema.statics.tavernBoss = async function tavernBoss (user, progress) {
|
|||||||
_.assign(tavernQuest, { extra: null });
|
_.assign(tavernQuest, { extra: null });
|
||||||
return tavern.save();
|
return tavern.save();
|
||||||
}
|
}
|
||||||
// Deal damage. Note a couple things here, str & def are calculated. If str/def are defined in the database,
|
|
||||||
|
// Deal damage. Note a couple things here, str & def are calculated.
|
||||||
|
// If str/def are defined in the database,
|
||||||
// use those first - which allows us to update the boss on the go if things are too easy/hard.
|
// use those first - which allows us to update the boss on the go if things are too easy/hard.
|
||||||
if (!tavern.quest.extra) tavern.quest.extra = {};
|
if (!tavern.quest.extra) tavern.quest.extra = {};
|
||||||
tavern.quest.progress.hp -= dmg / (tavern.quest.extra.def || quest.boss.def);
|
tavern.quest.progress.hp -= dmg / (tavern.quest.extra.def || quest.boss.def);
|
||||||
@@ -1222,7 +1254,11 @@ schema.statics.tavernBoss = async function tavernBoss (user, progress) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (quest.boss.desperation && tavern.quest.progress.hp < quest.boss.desperation.threshold && !tavern.quest.extra.desperate) {
|
if (
|
||||||
|
quest.boss.desperation
|
||||||
|
&& tavern.quest.progress.hp < quest.boss.desperation.threshold
|
||||||
|
&& !tavern.quest.extra.desperate
|
||||||
|
) {
|
||||||
const progressChat = tavern.sendChat({
|
const progressChat = tavern.sendChat({
|
||||||
message: quest.boss.desperation.text('en'),
|
message: quest.boss.desperation.text('en'),
|
||||||
info: {
|
info: {
|
||||||
@@ -1272,7 +1308,8 @@ schema.methods.leave = async function leaveGroup (user, keep = 'keep-all', keepC
|
|||||||
userId: { $exists: false },
|
userId: { $exists: false },
|
||||||
'group.assignedUsers': user._id,
|
'group.assignedUsers': user._id,
|
||||||
}).exec();
|
}).exec();
|
||||||
const assignedTasksToRemoveUserFrom = assignedTasks.map(task => this.unlinkTask(task, user, keep, false));
|
const assignedTasksToRemoveUserFrom = assignedTasks
|
||||||
|
.map(task => this.unlinkTask(task, user, keep, false));
|
||||||
await Promise.all(assignedTasksToRemoveUserFrom);
|
await Promise.all(assignedTasksToRemoveUserFrom);
|
||||||
|
|
||||||
// the user could be modified by calls to `unlinkTask` for challenge and group tasks
|
// the user could be modified by calls to `unlinkTask` for challenge and group tasks
|
||||||
@@ -1294,7 +1331,8 @@ schema.methods.leave = async function leaveGroup (user, keep = 'keep-all', keepC
|
|||||||
|
|
||||||
// If user is the last one in group and group is private, delete it
|
// If user is the last one in group and group is private, delete it
|
||||||
if (group.memberCount <= 1 && group.privacy === 'private') {
|
if (group.memberCount <= 1 && group.privacy === 'private') {
|
||||||
// double check the member count is correct so we don't accidentally delete a group that still has users in it
|
// double check the member count is correct
|
||||||
|
// so we don't accidentally delete a group that still has users in it
|
||||||
let members;
|
let members;
|
||||||
if (group.type === 'guild') {
|
if (group.type === 'guild') {
|
||||||
members = await User.find({ guilds: group._id }).select('_id').exec();
|
members = await User.find({ guilds: group._id }).select('_id').exec();
|
||||||
@@ -1306,37 +1344,44 @@ schema.methods.leave = async function leaveGroup (user, keep = 'keep-all', keepC
|
|||||||
|
|
||||||
if (members.length === 0) {
|
if (members.length === 0) {
|
||||||
promises.push(group.remove());
|
promises.push(group.remove());
|
||||||
return await Promise.all(promises);
|
return Promise.all(promises);
|
||||||
}
|
}
|
||||||
} else if (group.leader === user._id) { // otherwise If the leader is leaving (or if the leader previously left, and this wasn't accounted for)
|
// otherwise If the leader is leaving
|
||||||
|
// (or if the leader previously left, and this wasn't accounted for)
|
||||||
|
} else if (group.leader === user._id) {
|
||||||
const query = group.type === 'party' ? { 'party._id': group._id } : { guilds: group._id };
|
const query = group.type === 'party' ? { 'party._id': group._id } : { guilds: group._id };
|
||||||
query._id = { $ne: user._id };
|
query._id = { $ne: user._id };
|
||||||
const seniorMember = await User.findOne(query).select('_id').exec();
|
const seniorMember = await User.findOne(query).select('_id').exec();
|
||||||
|
|
||||||
// could be missing in case of public guild (that can have 0 members) with 1 member who is leaving
|
// could be missing in case of public guild (that can have 0 members)
|
||||||
|
// with 1 member who is leaving
|
||||||
if (seniorMember) update.$set = { leader: seniorMember._id };
|
if (seniorMember) update.$set = { leader: seniorMember._id };
|
||||||
}
|
}
|
||||||
// otherwise If the leader is leaving (or if the leader previously left, and this wasn't accounted for)
|
// otherwise If the leader is leaving
|
||||||
|
// (or if the leader previously left, and this wasn't accounted for)
|
||||||
update.$inc = { memberCount: -1 };
|
update.$inc = { memberCount: -1 };
|
||||||
if (group.leader === user._id) {
|
if (group.leader === user._id) {
|
||||||
const query = group.type === 'party' ? { 'party._id': group._id } : { guilds: group._id };
|
const query = group.type === 'party' ? { 'party._id': group._id } : { guilds: group._id };
|
||||||
query._id = { $ne: user._id };
|
query._id = { $ne: user._id };
|
||||||
const seniorMember = await User.findOne(query).select('_id').exec();
|
const seniorMember = await User.findOne(query).select('_id').exec();
|
||||||
|
|
||||||
// could be missing in case of public guild (that can have 0 members) with 1 member who is leaving
|
// could be missing in case of public guild (that can have 0 members)
|
||||||
|
// with 1 member who is leaving
|
||||||
if (seniorMember) update.$set = { leader: seniorMember._id };
|
if (seniorMember) update.$set = { leader: seniorMember._id };
|
||||||
}
|
}
|
||||||
promises.push(group.update(update).exec());
|
promises.push(group.update(update).exec());
|
||||||
|
|
||||||
return await Promise.all(promises);
|
return Promise.all(promises);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates all linked tasks for a group task
|
* Updates all linked tasks for a group task
|
||||||
*
|
*
|
||||||
* @param taskToSync The group task that will be synced
|
* @param taskToSync The group task that will be synced
|
||||||
* @param options.newCheckListItem The new checklist item that needs to be synced to all assigned users
|
* @param options.newCheckListItem The new checklist item
|
||||||
* @param options.removedCheckListItem The removed checklist item that needs to be removed from all assigned users
|
* that needs to be synced to all assigned users
|
||||||
|
* @param options.removedCheckListItem The removed checklist item that
|
||||||
|
* needs to be removed from all assigned users
|
||||||
*
|
*
|
||||||
* @return The created tasks
|
* @return The created tasks
|
||||||
*/
|
*/
|
||||||
@@ -1395,7 +1440,8 @@ schema.methods.updateTask = async function updateTask (taskToSync, options = {})
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Updating instead of loading and saving for performances, risks becoming a problem if we introduce more complexity in tasks
|
// Updating instead of loading and saving for performances,
|
||||||
|
// risks becoming a problem if we introduce more complexity in tasks
|
||||||
await taskSchema.update(updateQuery, updateCmd, { multi: true }).exec();
|
await taskSchema.update(updateQuery, updateCmd, { multi: true }).exec();
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1461,14 +1507,19 @@ schema.methods.syncTask = async function groupSyncTask (taskToSync, user) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!matchingTask.notes) matchingTask.notes = taskToSync.notes; // don't override the notes, but provide it if not provided
|
// don't override the notes, but provide it if not provided
|
||||||
if (matchingTask.tags.indexOf(group._id) === -1) matchingTask.tags.push(group._id); // add tag if missing
|
if (!matchingTask.notes) matchingTask.notes = taskToSync.notes;
|
||||||
|
// add tag if missing
|
||||||
|
if (matchingTask.tags.indexOf(group._id) === -1) matchingTask.tags.push(group._id);
|
||||||
|
|
||||||
toSave.push(matchingTask.save(), taskToSync.save(), user.save());
|
toSave.push(matchingTask.save(), taskToSync.save(), user.save());
|
||||||
return Promise.all(toSave);
|
return Promise.all(toSave);
|
||||||
};
|
};
|
||||||
|
|
||||||
schema.methods.unlinkTask = async function groupUnlinkTask (unlinkingTask, user, keep, saveUser = true) {
|
schema.methods.unlinkTask = async function groupUnlinkTask (
|
||||||
|
unlinkingTask, user,
|
||||||
|
keep, saveUser = true,
|
||||||
|
) {
|
||||||
const findQuery = {
|
const findQuery = {
|
||||||
'group.taskId': unlinkingTask._id,
|
'group.taskId': unlinkingTask._id,
|
||||||
userId: user._id,
|
userId: user._id,
|
||||||
@@ -1501,7 +1552,7 @@ schema.methods.unlinkTask = async function groupUnlinkTask (unlinkingTask, user,
|
|||||||
// save the user once outside of this function
|
// save the user once outside of this function
|
||||||
if (saveUser) promises.push(user.save());
|
if (saveUser) promises.push(user.save());
|
||||||
|
|
||||||
return Promise.all(promises);
|
await Promise.all(promises);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1537,7 +1588,7 @@ schema.methods.removeTask = async function groupRemoveTask (task) {
|
|||||||
group.markModified('tasksOrder');
|
group.markModified('tasksOrder');
|
||||||
removalPromises.push(group.save());
|
removalPromises.push(group.save());
|
||||||
|
|
||||||
return await Promise.all(removalPromises);
|
return Promise.all(removalPromises);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Returns true if the user has reached the spam message limit
|
// Returns true if the user has reached the spam message limit
|
||||||
@@ -1550,10 +1601,10 @@ schema.methods.checkChatSpam = function groupCheckChatSpam (user) {
|
|||||||
|
|
||||||
const currentTime = Date.now();
|
const currentTime = Date.now();
|
||||||
let userMessages = 0;
|
let userMessages = 0;
|
||||||
for (let i = 0; i < this.chat.length; i++) {
|
for (let i = 0; i < this.chat.length; i += 1) {
|
||||||
const message = this.chat[i];
|
const message = this.chat[i];
|
||||||
if (message.uuid === user._id && currentTime - message.timestamp <= SPAM_WINDOW_LENGTH) {
|
if (message.uuid === user._id && currentTime - message.timestamp <= SPAM_WINDOW_LENGTH) {
|
||||||
userMessages++;
|
userMessages += 1;
|
||||||
if (userMessages >= SPAM_MESSAGE_LIMIT) {
|
if (userMessages >= SPAM_MESSAGE_LIMIT) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -1568,7 +1619,8 @@ schema.methods.checkChatSpam = function groupCheckChatSpam (user) {
|
|||||||
schema.methods.isSubscribed = function isSubscribed () {
|
schema.methods.isSubscribed = function isSubscribed () {
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
const { plan } = this.purchased;
|
const { plan } = this.purchased;
|
||||||
return plan && plan.customerId && (!plan.dateTerminated || moment(plan.dateTerminated).isAfter(now));
|
return plan && plan.customerId
|
||||||
|
&& (!plan.dateTerminated || moment(plan.dateTerminated).isAfter(now));
|
||||||
};
|
};
|
||||||
|
|
||||||
schema.methods.hasNotCancelled = function hasNotCancelled () {
|
schema.methods.hasNotCancelled = function hasNotCancelled () {
|
||||||
@@ -1593,12 +1645,15 @@ schema.methods.updateGroupPlan = async function updateGroupPlan (removingMember)
|
|||||||
|
|
||||||
if (this.purchased.plan.paymentMethod === stripePayments.constants.PAYMENT_METHOD) {
|
if (this.purchased.plan.paymentMethod === stripePayments.constants.PAYMENT_METHOD) {
|
||||||
await stripePayments.chargeForAdditionalGroupMember(this);
|
await stripePayments.chargeForAdditionalGroupMember(this);
|
||||||
} else if (this.purchased.plan.paymentMethod === amazonPayments.constants.PAYMENT_METHOD && !removingMember) {
|
} else if (
|
||||||
|
this.purchased.plan.paymentMethod === amazonPayments.constants.PAYMENT_METHOD
|
||||||
|
&& !removingMember
|
||||||
|
) {
|
||||||
await amazonPayments.chargeForAdditionalGroupMember(this);
|
await amazonPayments.chargeForAdditionalGroupMember(this);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export let model = mongoose.model('Group', schema);
|
export const model = mongoose.model('Group', schema);
|
||||||
|
|
||||||
// initialize tavern if !exists (fresh installs)
|
// initialize tavern if !exists (fresh installs)
|
||||||
// do not run when testing as it's handled by the tests and can easily cause a race condition
|
// do not run when testing as it's handled by the tests and can easily cause a race condition
|
||||||
|
|||||||
@@ -128,7 +128,8 @@ export function messageDefaults (msg, user, client, flagCount = 0, info = {}) {
|
|||||||
contributor: user.contributor && user.contributor.toObject(),
|
contributor: user.contributor && user.contributor.toObject(),
|
||||||
backer: user.backer && user.backer.toObject(),
|
backer: user.backer && user.backer.toObject(),
|
||||||
user: user.profile.name,
|
user: user.profile.name,
|
||||||
username: user.flags && user.flags.verifiedUsername && user.auth && user.auth.local && user.auth.local.username,
|
username: user.flags && user.flags.verifiedUsername
|
||||||
|
&& user.auth && user.auth.local && user.auth.local.username,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
message.uuid = 'system';
|
message.uuid = 'system';
|
||||||
|
|||||||
@@ -17,12 +17,16 @@ export const schema = new mongoose.Schema({
|
|||||||
mysteryItems: { $type: Array, default: () => [] },
|
mysteryItems: { $type: Array, default: () => [] },
|
||||||
lastReminderDate: Date, // indicates the last time a subscription reminder was sent
|
lastReminderDate: Date, // indicates the last time a subscription reminder was sent
|
||||||
lastBillingDate: Date, // Used only for Amazon Payments to keep track of billing date
|
lastBillingDate: Date, // Used only for Amazon Payments to keep track of billing date
|
||||||
additionalData: mongoose.Schema.Types.Mixed, // Example for Google: {'receipt': 'serialized receipt json', 'signature': 'signature string'}
|
// Example for Google: {'receipt': 'serialized receipt json', 'signature': 'signature string'}
|
||||||
nextPaymentProcessing: Date, // indicates when the queue server should process this subscription again.
|
additionalData: mongoose.Schema.Types.Mixed,
|
||||||
|
// indicates when the queue server should process this subscription again.
|
||||||
|
nextPaymentProcessing: Date,
|
||||||
nextBillingDate: Date, // Next time google will bill this user.
|
nextBillingDate: Date, // Next time google will bill this user.
|
||||||
consecutive: {
|
consecutive: {
|
||||||
count: { $type: Number, default: 0 },
|
count: { $type: Number, default: 0 },
|
||||||
offset: { $type: Number, default: 0 }, // when gifted subs, offset++ for each month. offset-- each new-month (cron). count doesn't ++ until offset==0
|
// when gifted subs, offset++ for each month. offset-- each new-month (cron).
|
||||||
|
// count doesn't ++ until offset==0
|
||||||
|
offset: { $type: Number, default: 0 },
|
||||||
gemCapExtra: { $type: Number, default: 0 },
|
gemCapExtra: { $type: Number, default: 0 },
|
||||||
trinkets: { $type: Number, default: 0 },
|
trinkets: { $type: Number, default: 0 },
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -56,8 +56,9 @@ reminderSchema.plugin(baseModel, {
|
|||||||
_id: false,
|
_id: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Important
|
// NOTE IMPORTANTE
|
||||||
// When something changes here remember to update the client side model at common/script/libs/taskDefaults
|
// When something changes here remember to update the client side model
|
||||||
|
// at common/script/libs/taskDefaults
|
||||||
export const TaskSchema = new Schema({
|
export const TaskSchema = new Schema({
|
||||||
type: {
|
type: {
|
||||||
$type: String, enum: tasksTypes, required: true, default: tasksTypes[0],
|
$type: String, enum: tasksTypes, required: true, default: tasksTypes[0],
|
||||||
@@ -94,7 +95,8 @@ export const TaskSchema = new Schema({
|
|||||||
$type: String,
|
$type: String,
|
||||||
validate: [v => validator.isUUID(v), 'Invalid uuid.'],
|
validate: [v => validator.isUUID(v), 'Invalid uuid.'],
|
||||||
}],
|
}],
|
||||||
value: { $type: Number, default: 0, required: true }, // redness or cost for rewards Required because it must be settable (for rewards)
|
// redness or cost for rewards Required because it must be settable (for rewards)
|
||||||
|
value: { $type: Number, default: 0, required: true },
|
||||||
priority: {
|
priority: {
|
||||||
$type: Number,
|
$type: Number,
|
||||||
default: 1,
|
default: 1,
|
||||||
@@ -129,7 +131,11 @@ export const TaskSchema = new Schema({
|
|||||||
requested: { $type: Boolean, default: false },
|
requested: { $type: Boolean, default: false },
|
||||||
requestedDate: { $type: Date },
|
requestedDate: { $type: Date },
|
||||||
},
|
},
|
||||||
sharedCompletion: { $type: String, enum: _.values(SHARED_COMPLETION), default: SHARED_COMPLETION.single },
|
sharedCompletion: {
|
||||||
|
$type: String,
|
||||||
|
enum: _.values(SHARED_COMPLETION),
|
||||||
|
default: SHARED_COMPLETION.single,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
reminders: [reminderSchema],
|
reminders: [reminderSchema],
|
||||||
@@ -162,8 +168,13 @@ TaskSchema.plugin(baseModel, {
|
|||||||
timestamps: true,
|
timestamps: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
TaskSchema.statics.findByIdOrAlias = async function findByIdOrAlias (identifier, userId, additionalQueries = {}) {
|
TaskSchema.statics.findByIdOrAlias = async function findByIdOrAlias (
|
||||||
// not using i18n strings because these errors are meant for devs who forgot to pass some parameters
|
identifier,
|
||||||
|
userId,
|
||||||
|
additionalQueries = {},
|
||||||
|
) {
|
||||||
|
// not using i18n strings because these errors
|
||||||
|
// are meant for devs who forgot to pass some parameters
|
||||||
if (!identifier) throw new InternalServerError('Task identifier is a required argument');
|
if (!identifier) throw new InternalServerError('Task identifier is a required argument');
|
||||||
if (!userId) throw new InternalServerError('User identifier is a required argument');
|
if (!userId) throw new InternalServerError('User identifier is a required argument');
|
||||||
|
|
||||||
@@ -224,7 +235,8 @@ TaskSchema.methods.scoreChallengeTask = async function scoreChallengeTask (delta
|
|||||||
lastHistoryEntry.date = Number(new Date());
|
lastHistoryEntry.date = Number(new Date());
|
||||||
|
|
||||||
if (chalTask.type === 'habit') {
|
if (chalTask.type === 'habit') {
|
||||||
// @TODO remove this extra check after migration has run to set scoredUp and scoredDown in every task
|
// @TODO remove this extra check after migration has run to set scoredUp and scoredDown
|
||||||
|
// in every task
|
||||||
lastHistoryEntry.scoredUp = lastHistoryEntry.scoredUp || 0;
|
lastHistoryEntry.scoredUp = lastHistoryEntry.scoredUp || 0;
|
||||||
lastHistoryEntry.scoredDown = lastHistoryEntry.scoredDown || 0;
|
lastHistoryEntry.scoredDown = lastHistoryEntry.scoredDown || 0;
|
||||||
|
|
||||||
@@ -251,7 +263,8 @@ TaskSchema.methods.scoreChallengeTask = async function scoreChallengeTask (delta
|
|||||||
|
|
||||||
// Only preen task history once a day when the task is scored first
|
// Only preen task history once a day when the task is scored first
|
||||||
if (chalTask.history.length > 365) {
|
if (chalTask.history.length > 365) {
|
||||||
chalTask.history = preenHistory(chalTask.history, true); // true means the challenge will retain as many entries as a subscribed user
|
// true means the challenge will retain as many entries as a subscribed user
|
||||||
|
chalTask.history = preenHistory(chalTask.history, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -259,16 +272,15 @@ TaskSchema.methods.scoreChallengeTask = async function scoreChallengeTask (delta
|
|||||||
await chalTask.save();
|
await chalTask.save();
|
||||||
};
|
};
|
||||||
|
|
||||||
export let Task = mongoose.model('Task', TaskSchema);
|
export const Task = mongoose.model('Task', TaskSchema);
|
||||||
|
|
||||||
// habits and dailies shared fields
|
// habits and dailies shared fields
|
||||||
const habitDailySchema = () =>
|
// Schema for history not defined because it causes serious perf problems
|
||||||
// Schema not defined because it causes serious perf problems
|
// date is a date stored as a Number value
|
||||||
// date is a date stored as a Number value
|
// value is a Number
|
||||||
// value is a Number
|
// scoredUp and scoredDown only exist for habits and are numbers
|
||||||
// scoredUp and scoredDown only exist for habits and are numbers
|
|
||||||
({ history: Array })
|
const habitDailySchema = () => ({ history: Array });
|
||||||
;
|
|
||||||
|
|
||||||
// dailys and todos shared fields
|
// dailys and todos shared fields
|
||||||
const dailyTodoSchema = () => ({
|
const dailyTodoSchema = () => ({
|
||||||
@@ -322,8 +334,10 @@ export const DailySchema = new Schema(_.defaults({
|
|||||||
su: { $type: Boolean, default: true },
|
su: { $type: Boolean, default: true },
|
||||||
},
|
},
|
||||||
streak: { $type: Number, default: 0 },
|
streak: { $type: Number, default: 0 },
|
||||||
daysOfMonth: { $type: [Number], default: [] }, // Days of the month that the daily should repeat on
|
// Days of the month that the daily should repeat on
|
||||||
weeksOfMonth: { $type: [Number], default: [] }, // Weeks of the month that the daily should repeat on
|
daysOfMonth: { $type: [Number], default: [] },
|
||||||
|
// Weeks of the month that the daily should repeat on
|
||||||
|
weeksOfMonth: { $type: [Number], default: [] },
|
||||||
isDue: { $type: Boolean },
|
isDue: { $type: Boolean },
|
||||||
nextDue: [{ $type: String }],
|
nextDue: [{ $type: String }],
|
||||||
yesterDaily: { $type: Boolean, default: true, required: true },
|
yesterDaily: { $type: Boolean, default: true, required: true },
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ export const schema = new Schema({
|
|||||||
* @TODO Remove once https://github.com/HabitRPG/habitica/issues/9923
|
* @TODO Remove once https://github.com/HabitRPG/habitica/issues/9923
|
||||||
* is fixed
|
* is fixed
|
||||||
*/
|
*/
|
||||||
schema.statics.convertNotificationsToSafeJson = function convertNotificationsToSafeJson (notifications) {
|
schema.statics.convertNotificationsToSafeJson = function convNotifsToSafeJson (notifications) {
|
||||||
if (!notifications) return notifications;
|
if (!notifications) return notifications;
|
||||||
|
|
||||||
let filteredNotifications = notifications.filter(n => {
|
let filteredNotifications = notifications.filter(n => {
|
||||||
|
|||||||
Reference in New Issue
Block a user