2018-05-06 20:12:00 +00:00
import nconf from 'nconf' ;
import _ from 'lodash' ;
import moment from 'moment' ;
import { model as User } from '../../models/user' ;
import {
model as Group ,
basicFields as basicGroupFields ,
} from '../../models/group' ;
import {
getUserInfo ,
sendTxn as txnEmail ,
} from '../email' ;
2018-12-06 16:13:49 +00:00
const TECH _ASSISTANCE _EMAIL = nconf . get ( 'EMAILS_TECH_ASSISTANCE_EMAIL' ) ;
2018-05-06 20:12:00 +00:00
const JOINED _GROUP _PLAN = 'joined group plan' ;
function _dateDiff ( earlyDate , lateDate ) {
if ( ! earlyDate || ! lateDate || moment ( lateDate ) . isBefore ( earlyDate ) ) return 0 ;
return moment ( lateDate ) . diff ( earlyDate , 'months' , true ) ;
}
/ * *
* Add a subscription to members of a group
*
* @ param group The Group Model that is subscribed to a group plan
*
* @ return undefined
* /
async function addSubscriptionToGroupUsers ( group ) {
let members ;
if ( group . type === 'guild' ) {
2019-10-08 14:57:10 +00:00
members = await User . find ( { guilds : group . _id } ) . select ( '_id purchased items auth profile.name notifications' ) . exec ( ) ;
2018-05-06 20:12:00 +00:00
} else {
2019-10-08 14:57:10 +00:00
members = await User . find ( { 'party._id' : group . _id } ) . select ( '_id purchased items auth profile.name notifications' ) . exec ( ) ;
2018-05-06 20:12:00 +00:00
}
2019-10-08 14:57:10 +00:00
const promises = members . map ( member => this . addSubToGroupUser ( member , group ) ) ;
2018-05-06 20:12:00 +00:00
await Promise . all ( promises ) ;
}
/ * *
* Add a subscription to a new member of a group
*
* @ param member The new member of the group
*
* @ return undefined
* /
async function addSubToGroupUser ( member , group ) {
// These EMAIL_TEMPLATE constants are used to pass strings into templates that are
// stored externally and so their values must not be changed.
const EMAIL _TEMPLATE _SUBSCRIPTION _TYPE _GOOGLE = 'Google_subscription' ;
const EMAIL _TEMPLATE _SUBSCRIPTION _TYPE _IOS = 'iOS_subscription' ;
const EMAIL _TEMPLATE _SUBSCRIPTION _TYPE _GROUP _PLAN = 'group_plan_free_subscription' ;
const EMAIL _TEMPLATE _SUBSCRIPTION _TYPE _LIFETIME _FREE = 'lifetime_free_subscription' ;
const EMAIL _TEMPLATE _SUBSCRIPTION _TYPE _NORMAL = 'normal_subscription' ;
const EMAIL _TEMPLATE _SUBSCRIPTION _TYPE _UNKNOWN = 'unknown_type_of_subscription' ;
const EMAIL _TEMPLATE _SUBSCRIPTION _TYPE _NONE = 'no_subscription' ;
// When changing customerIdsToIgnore or paymentMethodsToIgnore, the code blocks below for
// the `group-member-join` email template will probably need to be changed.
2019-10-08 14:57:10 +00:00
const customerIdsToIgnore = [ this . constants . GROUP _PLAN _CUSTOMER _ID , this . constants . UNLIMITED _CUSTOMER _ID ] ;
const paymentMethodsToIgnore = [ this . constants . GOOGLE _PAYMENT _METHOD , this . constants . IOS _PAYMENT _METHOD ] ;
2018-05-06 20:12:00 +00:00
let previousSubscriptionType = EMAIL _TEMPLATE _SUBSCRIPTION _TYPE _NONE ;
2019-10-08 14:57:10 +00:00
const leader = await User . findById ( group . leader ) . exec ( ) ;
2018-05-06 20:12:00 +00:00
2019-10-08 14:57:10 +00:00
const data = {
2018-05-06 20:12:00 +00:00
user : { } ,
sub : {
key : 'group_plan_auto' ,
} ,
customerId : 'group-plan' ,
paymentMethod : 'Group Plan' ,
headers : { } ,
} ;
let plan = {
planId : 'group_plan_auto' ,
customerId : 'group-plan' ,
dateUpdated : new Date ( ) ,
gemsBought : 0 ,
paymentMethod : 'groupPlan' ,
extraMonths : 0 ,
dateTerminated : null ,
lastBillingDate : null ,
dateCreated : new Date ( ) ,
mysteryItems : [ ] ,
consecutive : {
trinkets : 0 ,
offset : 0 ,
gemCapExtra : 0 ,
} ,
} ;
2019-10-08 14:57:10 +00:00
const memberPlan = member . purchased . plan ;
2018-05-06 20:12:00 +00:00
if ( member . isSubscribed ( ) ) {
2019-10-08 14:57:10 +00:00
const customerHasCancelledGroupPlan = memberPlan . customerId === this . constants . GROUP _PLAN _CUSTOMER _ID && ! member . hasNotCancelled ( ) ;
const ignorePaymentPlan = paymentMethodsToIgnore . indexOf ( memberPlan . paymentMethod ) !== - 1 ;
const ignoreCustomerId = customerIdsToIgnore . indexOf ( memberPlan . customerId ) !== - 1 ;
2018-05-06 20:12:00 +00:00
if ( ignorePaymentPlan ) {
2019-10-08 14:57:10 +00:00
txnEmail ( { email : TECH _ASSISTANCE _EMAIL } , 'admin-user-subscription-details' , [
{ name : 'PROFILE_NAME' , content : member . profile . name } ,
{ name : 'UUID' , content : member . _id } ,
{ name : 'EMAIL' , content : getUserInfo ( member , [ 'email' ] ) . email } ,
{ name : 'PAYMENT_METHOD' , content : memberPlan . paymentMethod } ,
{ name : 'PURCHASED_PLAN' , content : JSON . stringify ( memberPlan ) } ,
{ name : 'ACTION_NEEDED' , content : 'User has joined group plan and has been told to cancel their subscription then email us. Ensure they do that then give them free sub.' } ,
2018-05-06 20:12:00 +00:00
// TODO User won't get email instructions if they've opted out of all emails. See if we can make this email an exception and if not, report here whether they've opted out.
] ) ;
}
if ( ( ignorePaymentPlan || ignoreCustomerId ) && ! customerHasCancelledGroupPlan ) {
// member has been added to group plan but their subscription will not be changed
// automatically so they need a special message in the email
if ( memberPlan . paymentMethod === this . constants . GOOGLE _PAYMENT _METHOD ) {
previousSubscriptionType = EMAIL _TEMPLATE _SUBSCRIPTION _TYPE _GOOGLE ;
} else if ( memberPlan . paymentMethod === this . constants . IOS _PAYMENT _METHOD ) {
previousSubscriptionType = EMAIL _TEMPLATE _SUBSCRIPTION _TYPE _IOS ;
} else if ( memberPlan . customerId === this . constants . UNLIMITED _CUSTOMER _ID ) {
previousSubscriptionType = EMAIL _TEMPLATE _SUBSCRIPTION _TYPE _LIFETIME _FREE ;
} else if ( memberPlan . customerId === this . constants . GROUP _PLAN _CUSTOMER _ID ) {
previousSubscriptionType = EMAIL _TEMPLATE _SUBSCRIPTION _TYPE _GROUP _PLAN ;
} else {
// this triggers a generic message in the email template in case we forget
// to update this code for new special cases
previousSubscriptionType = EMAIL _TEMPLATE _SUBSCRIPTION _TYPE _UNKNOWN ;
}
txnEmail ( member , 'group-member-join' , [
2019-10-08 14:57:10 +00:00
{ name : 'LEADER' , content : leader . profile . name } ,
{ name : 'GROUP_NAME' , content : group . name } ,
{ name : 'PREVIOUS_SUBSCRIPTION_TYPE' , content : previousSubscriptionType } ,
2018-05-06 20:12:00 +00:00
] ) ;
return ;
}
if ( member . hasNotCancelled ( ) ) {
2019-10-08 14:57:10 +00:00
await member . cancelSubscription ( { cancellationReason : JOINED _GROUP _PLAN } ) ;
2018-05-06 20:12:00 +00:00
previousSubscriptionType = EMAIL _TEMPLATE _SUBSCRIPTION _TYPE _NORMAL ;
}
2019-10-08 14:57:10 +00:00
const today = new Date ( ) ;
2018-05-06 20:12:00 +00:00
plan = member . purchased . plan . toObject ( ) ;
let extraMonths = Number ( plan . extraMonths ) ;
if ( plan . dateTerminated ) extraMonths += _dateDiff ( today , plan . dateTerminated ) ;
_ ( plan ) . merge ( { // override with these values
planId : 'group_plan_auto' ,
customerId : 'group-plan' ,
dateUpdated : today ,
paymentMethod : 'groupPlan' ,
extraMonths ,
dateTerminated : null ,
lastBillingDate : null ,
owner : member . _id ,
} ) . defaults ( { // allow non-override if a plan was previously used
gemsBought : 0 ,
dateCreated : today ,
mysteryItems : [ ] ,
} ) . value ( ) ;
}
// save unused hourglass and mystery items
plan . consecutive . trinkets = memberPlan . consecutive . trinkets ;
plan . mysteryItems = memberPlan . mysteryItems ;
member . purchased . plan = plan ;
member . items . mounts [ 'Jackalope-RoyalPurple' ] = true ;
2019-04-01 17:24:18 +00:00
member . markModified ( 'items.mounts' ) ;
2018-05-06 20:12:00 +00:00
data . user = member ;
await this . createSubscription ( data ) ;
txnEmail ( data . user , 'group-member-join' , [
2019-10-08 14:57:10 +00:00
{ name : 'LEADER' , content : leader . profile . name } ,
{ name : 'GROUP_NAME' , content : group . name } ,
{ name : 'PREVIOUS_SUBSCRIPTION_TYPE' , content : previousSubscriptionType } ,
2018-05-06 20:12:00 +00:00
] ) ;
}
/ * *
* Cancels subscriptions of members of a group
*
* @ param group The Group Model that is cancelling a group plan
*
* @ return undefined
* /
async function cancelGroupUsersSubscription ( group ) {
let members ;
if ( group . type === 'guild' ) {
2019-10-08 14:57:10 +00:00
members = await User . find ( { guilds : group . _id } ) . select ( '_id guilds purchased' ) . exec ( ) ;
2018-05-06 20:12:00 +00:00
} else {
2019-10-08 14:57:10 +00:00
members = await User . find ( { 'party._id' : group . _id } ) . select ( '_id guilds purchased' ) . exec ( ) ;
2018-05-06 20:12:00 +00:00
}
2019-10-08 14:57:10 +00:00
const promises = members . map ( member => this . cancelGroupSubscriptionForUser ( member , group ) ) ;
2018-05-06 20:12:00 +00:00
await Promise . all ( promises ) ;
}
async function cancelGroupSubscriptionForUser ( user , group , userWasRemoved = false ) {
if ( user . purchased . plan . customerId !== this . constants . GROUP _PLAN _CUSTOMER _ID ) return ;
2019-10-08 14:57:10 +00:00
const userGroups = user . guilds . toObject ( ) ;
2018-10-09 18:07:50 +00:00
if ( user . party . _id ) userGroups . push ( user . party . _id ) ;
2018-05-06 20:12:00 +00:00
2019-10-08 14:57:10 +00:00
const index = userGroups . indexOf ( group . _id ) ;
2019-08-15 15:06:56 +00:00
if ( index >= 0 ) userGroups . splice ( index , 1 ) ;
2018-05-06 20:12:00 +00:00
2019-10-08 14:57:10 +00:00
const groupPlansQuery = {
2019-08-15 15:06:56 +00:00
// type: { $in: ['guild', 'party'] },
2018-05-06 20:12:00 +00:00
// privacy: 'private',
2019-10-08 14:57:10 +00:00
_id : { $in : userGroups } ,
2019-08-15 15:06:56 +00:00
'purchased.plan.dateTerminated' : { $type : 'null' } ,
2018-05-06 20:12:00 +00:00
} ;
2019-10-08 14:57:10 +00:00
const groupFields = ` ${ basicGroupFields } purchased ` ;
const userGroupPlans = await Group . find ( groupPlansQuery ) . select ( groupFields ) . exec ( ) ;
2018-05-06 20:12:00 +00:00
2019-10-08 14:57:10 +00:00
if ( userGroupPlans . length === 0 ) {
const leader = await User . findById ( group . leader ) . exec ( ) ;
2018-05-06 20:12:00 +00:00
const email = userWasRemoved ? 'group-member-removed' : 'group-member-cancel' ;
txnEmail ( user , email , [
2019-10-08 14:57:10 +00:00
{ name : 'LEADER' , content : leader . profile . name } ,
{ name : 'GROUP_NAME' , content : group . name } ,
2018-05-06 20:12:00 +00:00
] ) ;
2019-10-08 14:57:10 +00:00
await this . cancelSubscription ( { user } ) ;
2018-05-06 20:12:00 +00:00
}
}
2019-10-02 17:45:27 +00:00
export {
2018-05-06 20:12:00 +00:00
addSubscriptionToGroupUsers ,
addSubToGroupUser ,
cancelGroupUsersSubscription ,
cancelGroupSubscriptionForUser ,
} ;