Merged in develop
This commit is contained in:
@@ -81,7 +81,7 @@ schema.methods.syncToUser = async function syncChallengeToUser (user) {
|
||||
// Add challenge to user.challenges
|
||||
if (!_.includes(user.challenges, challenge._id)) {
|
||||
// using concat because mongoose's protection against concurrent array modification isn't working as expected.
|
||||
// see https://github.com/HabitRPG/habitrpg/pull/7787#issuecomment-232972394
|
||||
// see https://github.com/HabitRPG/habitica/pull/7787#issuecomment-232972394
|
||||
user.challenges = user.challenges.concat([challenge._id]);
|
||||
}
|
||||
// Sync tags
|
||||
|
||||
@@ -288,7 +288,7 @@ schema.statics.getGroups = async function getGroups (options = {}) {
|
||||
};
|
||||
|
||||
// When converting to json remove chat messages with more than 1 flag and remove all flags info
|
||||
// unless the user is an admin
|
||||
// unless the user is an admin or said chat is posted by that 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
|
||||
schema.statics.toJSONCleanChat = function groupToJSONCleanChat (group, user) {
|
||||
@@ -298,7 +298,7 @@ schema.statics.toJSONCleanChat = function groupToJSONCleanChat (group, user) {
|
||||
_.remove(toJSON.chat, chatMsg => {
|
||||
chatMsg.flags = {};
|
||||
if (chatMsg._meta) chatMsg._meta = undefined;
|
||||
return chatMsg.flagCount >= 2;
|
||||
return user._id !== chatMsg.uuid && chatMsg.flagCount >= 2;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -229,6 +229,7 @@ export let DailySchema = new Schema(_.defaults({
|
||||
default () {
|
||||
return moment().startOf('day').toDate();
|
||||
},
|
||||
required: true,
|
||||
},
|
||||
repeat: { // used only for 'weekly' frequency,
|
||||
m: {type: Boolean, default: true},
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
chatDefaults,
|
||||
TAVERN_ID,
|
||||
} from '../group';
|
||||
import { defaults } from 'lodash';
|
||||
import { defaults, map, flatten, flow, compact, uniq, partialRight } from 'lodash';
|
||||
import { model as UserNotification } from '../userNotification';
|
||||
import schema from './schema';
|
||||
import payments from '../../libs/payments';
|
||||
@@ -36,6 +36,56 @@ schema.methods.getGroups = function getUserGroups () {
|
||||
return userGroups;
|
||||
};
|
||||
|
||||
/* eslint-disable no-unused-vars */ // The checks below all get access to sndr and rcvr, but not all use both
|
||||
const INTERACTION_CHECKS = Object.freeze({
|
||||
always: [
|
||||
// Revoked chat privileges block all interactions to prevent the evading of harassment protections
|
||||
// See issue #7971 for some discussion
|
||||
(sndr, rcvr) => sndr.flags.chatRevoked && 'chatPrivilegesRevoked',
|
||||
|
||||
// Direct user blocks prevent all interactions
|
||||
(sndr, rcvr) => rcvr.inbox.blocks.includes(sndr._id) && 'notAuthorizedToSendMessageToThisUser',
|
||||
(sndr, rcvr) => sndr.inbox.blocks.includes(rcvr._id) && 'notAuthorizedToSendMessageToThisUser',
|
||||
],
|
||||
|
||||
'send-private-message': [
|
||||
// Private messaging has an opt-out, which does not affect other interactions
|
||||
(sndr, rcvr) => rcvr.inbox.optOut && 'notAuthorizedToSendMessageToThisUser',
|
||||
|
||||
// We allow a player to message themselves so they can test how PMs work or send their own notes to themselves
|
||||
],
|
||||
|
||||
'transfer-gems': [
|
||||
// Unlike private messages, gems can't be sent to oneself
|
||||
(sndr, rcvr) => rcvr._id === sndr._id && 'cannotSendGemsToYourself',
|
||||
],
|
||||
});
|
||||
/* eslint-enable no-unused-vars */
|
||||
|
||||
export const KNOWN_INTERACTIONS = Object.freeze(Object.keys(INTERACTION_CHECKS).filter(key => key !== 'always'));
|
||||
|
||||
// Get an array of error message keys that would be thrown if the given interaction was attempted
|
||||
schema.methods.getObjectionsToInteraction = function getObjectionsToInteraction (interaction, receiver) {
|
||||
if (!KNOWN_INTERACTIONS.includes(interaction)) {
|
||||
throw new Error(`Unknown kind of interaction: "${interaction}", expected one of ${KNOWN_INTERACTIONS.join(', ')}`);
|
||||
}
|
||||
|
||||
let sender = this;
|
||||
let checks = [
|
||||
INTERACTION_CHECKS.always,
|
||||
INTERACTION_CHECKS[interaction],
|
||||
];
|
||||
|
||||
let executeChecks = partialRight(map, (check) => check(sender, receiver));
|
||||
|
||||
return flow(
|
||||
flatten,
|
||||
executeChecks,
|
||||
compact, // Remove passed checks (passed checks return falsy; failed checks return message keys)
|
||||
uniq
|
||||
)(checks);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sends a message to a this. Archives a copy in sender's inbox.
|
||||
@@ -108,23 +158,36 @@ schema.methods.addComputedStatsToJSONObj = function addComputedStatsToUserJSONOb
|
||||
return statsObject;
|
||||
};
|
||||
|
||||
/**
|
||||
* Cancels a subscription.
|
||||
*
|
||||
* @param options
|
||||
* @param options.user The user object who is purchasing
|
||||
* @param options.groupId The id of the group purchasing a subscription
|
||||
* @param options.headers The request headers (only for Amazon subscriptions)
|
||||
* @param options.cancellationReason A text string to control sending an email
|
||||
*
|
||||
* @return a Promise from api.cancelSubscription()
|
||||
*/
|
||||
// @TODO: There is currently a three way relation between the user, payment methods and the payment helper
|
||||
// This creates some odd Dependency Injection issues. To counter that, we use the user as the third layer
|
||||
// To negotiate between the payment providers and the payment helper (which probably has too many responsiblities)
|
||||
// In summary, currently is is best practice to use this method to cancel a user subscription, rather than calling the
|
||||
// payment helper.
|
||||
schema.methods.cancelSubscription = async function cancelSubscription () {
|
||||
schema.methods.cancelSubscription = async function cancelSubscription (options = {}) {
|
||||
let plan = this.purchased.plan;
|
||||
|
||||
options.user = this;
|
||||
if (plan.paymentMethod === amazonPayments.constants.PAYMENT_METHOD) {
|
||||
return await amazonPayments.cancelSubscription({user: this});
|
||||
return await amazonPayments.cancelSubscription(options);
|
||||
} else if (plan.paymentMethod === stripePayments.constants.PAYMENT_METHOD) {
|
||||
return await stripePayments.cancelSubscription({user: this});
|
||||
return await stripePayments.cancelSubscription(options);
|
||||
} else if (plan.paymentMethod === paypalPayments.constants.PAYMENT_METHOD) {
|
||||
return await paypalPayments.subscribeCancel({user: this});
|
||||
return await paypalPayments.subscribeCancel(options);
|
||||
}
|
||||
// Android and iOS subscriptions cannot be cancelled by Habitica.
|
||||
|
||||
return await payments.cancelSubscription({user: this});
|
||||
return await payments.cancelSubscription(options);
|
||||
};
|
||||
|
||||
schema.methods.daysUserHasMissed = function daysUserHasMissed (now, req = {}) {
|
||||
|
||||
@@ -111,8 +111,11 @@ let schema = new Schema({
|
||||
birthday: Number,
|
||||
partyUp: Boolean,
|
||||
partyOn: Boolean,
|
||||
congrats: Number,
|
||||
getwell: Number,
|
||||
royallyLoyal: Boolean,
|
||||
joinedGuild: Boolean,
|
||||
joinedChallenge: Boolean,
|
||||
},
|
||||
|
||||
backer: {
|
||||
@@ -122,7 +125,7 @@ let schema = new Schema({
|
||||
},
|
||||
|
||||
contributor: {
|
||||
// 1-9, see https://trello.com/c/wkFzONhE/277-contributor-gear https://github.com/HabitRPG/habitrpg/issues/3801
|
||||
// 1-9, see https://trello.com/c/wkFzONhE/277-contributor-gear https://github.com/HabitRPG/habitica/issues/3801
|
||||
level: {
|
||||
type: Number,
|
||||
min: 0,
|
||||
@@ -288,6 +291,10 @@ let schema = new Schema({
|
||||
thankyouReceived: Array,
|
||||
birthday: {type: Number, default: 0},
|
||||
birthdayReceived: Array,
|
||||
congrats: {type: Number, default: 0},
|
||||
congratsReceived: Array,
|
||||
getwell: {type: Number, default: 0},
|
||||
getwellReceived: Array,
|
||||
},
|
||||
|
||||
// -------------- Animals -------------------
|
||||
@@ -410,7 +417,7 @@ let schema = new Schema({
|
||||
skin: {type: String, default: '915533'},
|
||||
shirt: {type: String, default: 'blue'},
|
||||
timezoneOffset: {type: Number, default: 0},
|
||||
sound: {type: String, default: 'rosstavoTheme', enum: ['off', 'danielTheBard', 'gokulTheme', 'luneFoxTheme', 'wattsTheme', 'rosstavoTheme', 'dewinTheme', 'airuTheme']},
|
||||
sound: {type: String, default: 'rosstavoTheme', enum: ['off', 'danielTheBard', 'gokulTheme', 'luneFoxTheme', 'wattsTheme', 'rosstavoTheme', 'dewinTheme', 'airuTheme', 'beatscribeNesTheme', 'arashiTheme']},
|
||||
chair: {type: String, default: 'none'},
|
||||
timezoneOffsetAtLastCron: Number,
|
||||
language: String,
|
||||
|
||||
@@ -20,6 +20,7 @@ const NOTIFICATION_TYPES = [
|
||||
'BOSS_DAMAGE', // Not used currently but kept to avoid validation errors
|
||||
'GUILD_PROMPT',
|
||||
'GUILD_JOINED_ACHIEVEMENT',
|
||||
'CHALLENGE_JOINED_ACHIEVEMENT',
|
||||
];
|
||||
|
||||
const Schema = mongoose.Schema;
|
||||
|
||||
Reference in New Issue
Block a user