mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2025-03-31 07:12:49 -04:00
Merge pull request #1342 from codecombat/feature/mail-system
Feature/mail system
This commit is contained in:
commit
ccb2ad67ac
9 changed files with 415 additions and 6 deletions
17
app/schemas/models/mail_sent.coffee
Normal file
17
app/schemas/models/mail_sent.coffee
Normal file
|
@ -0,0 +1,17 @@
|
|||
c = require './../schemas'
|
||||
#This will represent transactional emails which have been sent
|
||||
|
||||
MailSentSchema = c.object {
|
||||
title: 'Sent mail'
|
||||
description: 'Emails which have been sent through the system'
|
||||
}
|
||||
_.extend MailSentSchema.properties,
|
||||
mailTask: c.objectId {}
|
||||
user: c.objectId links: [{rel: 'extra', href: '/db/user/{($)}'}]
|
||||
sent: c.date title: 'Sent', readOnly: true
|
||||
metadata: c.object {}, {}
|
||||
|
||||
c.extendBasicProperties MailSentSchema, 'mail.sent'
|
||||
|
||||
module.exports = MailSentSchema
|
||||
|
|
@ -59,7 +59,7 @@
|
|||
"express-useragent": "~0.0.9",
|
||||
"gridfs-stream": "0.4.x",
|
||||
"stream-buffers": "0.2.x",
|
||||
"sendwithus": "2.0.x",
|
||||
"sendwithus": "2.1.x",
|
||||
"aws-sdk": "~2.0.0",
|
||||
"bayesian-battle": "0.0.x",
|
||||
"redis": "",
|
||||
|
|
43
server/commons/LockManager.coffee
Normal file
43
server/commons/LockManager.coffee
Normal file
|
@ -0,0 +1,43 @@
|
|||
config = require '../../server_config'
|
||||
redis = require 'redis'
|
||||
log = require 'winston'
|
||||
|
||||
class LockManager
|
||||
constructor: ->
|
||||
unless config.isProduction
|
||||
throw "You shouldn't be instantiating distributed locks unless in production."
|
||||
@redisNotAvailable = true
|
||||
@redisClient = redis.createClient config.redis.port, config.redis.host
|
||||
@redisClient.on "ready", =>
|
||||
log.info "Redis ready!"
|
||||
@redisNotAvailable = false
|
||||
@redisClient.on "error", (err) =>
|
||||
@redisNotAvailable = true
|
||||
log.error "Redis connection error! Err: #{err}"
|
||||
@redisClient.on "end", =>
|
||||
@redisNotAvailable = true
|
||||
log.error "Redis connection ended!"
|
||||
@lockValues = {}
|
||||
@unlockScript = "if redis.call(\"get\",KEYS[1]) == ARGV[1] then return redis.call(\"del\",KEYS[1]) else return 0 end"
|
||||
|
||||
setLock: (lockName, timeoutMs, cb) =>
|
||||
if @redisNotAvailable is true then return cb "Redis not available!"
|
||||
randomNumber = Math.floor(Math.random() * 1000000000)
|
||||
@redisClient.set [lockName,randomNumber, "NX", "PX", timeoutMs], (err, res) =>
|
||||
if err? then return cb err, null
|
||||
if res is "OK"
|
||||
@lockValues[lockName] = randomNumber
|
||||
return cb null, "Lock set!"
|
||||
unless res
|
||||
return cb "Lock already set!", null
|
||||
|
||||
releaseLock: (lockName, cb) =>
|
||||
if @redisNotAvailable is true then return cb "Redis not available!"
|
||||
@redisClient.eval [@unlockScript, 1, lockName, @lockValues[lockName]], (err, res) ->
|
||||
if err? then return cb err, null
|
||||
if res
|
||||
cb null, "The lock was released!"
|
||||
else
|
||||
cb "The lock was not released.", null
|
||||
|
||||
module.exports = new LockManager()
|
|
@ -9,6 +9,7 @@ module.exports.handlers =
|
|||
'thang_type': 'levels/thangs/thang_type_handler'
|
||||
'user': 'users/user_handler'
|
||||
'user_remark': 'users/remarks/user_remark_handler'
|
||||
'mail_sent': 'mail/sent/mail_sent_handler'
|
||||
'achievement': 'achievements/achievement_handler'
|
||||
'earned_achievement': 'achievements/earned_achievement_handler'
|
||||
|
||||
|
|
11
server/mail/sent/MailSent.coffee
Normal file
11
server/mail/sent/MailSent.coffee
Normal file
|
@ -0,0 +1,11 @@
|
|||
mongoose = require 'mongoose'
|
||||
plugins = require '../../plugins/plugins'
|
||||
jsonschema = require '../../../app/schemas/models/mail_sent'
|
||||
|
||||
MailSent = new mongoose.Schema({
|
||||
sent:
|
||||
type: Date
|
||||
'default': Date.now
|
||||
}, {strict: false})
|
||||
|
||||
module.exports = MailSent = mongoose.model('mail.sent', MailSent)
|
12
server/mail/sent/mail_sent_handler.coffee
Normal file
12
server/mail/sent/mail_sent_handler.coffee
Normal file
|
@ -0,0 +1,12 @@
|
|||
MailSent = require './MailSent'
|
||||
Handler = require '../../commons/Handler'
|
||||
|
||||
class MailSentHandler extends Handler
|
||||
modelClass: MailSent
|
||||
editableProperties: ['mailTask','user','sent']
|
||||
jsonSchema: require '../../../app/schemas/models/mail_sent'
|
||||
|
||||
hasAccess: (req) ->
|
||||
req.user?.isAdmin()
|
||||
|
||||
module.exports = new MailSentHandler()
|
|
@ -117,7 +117,8 @@ module.exports.setup = (app) ->
|
|||
)
|
||||
)
|
||||
|
||||
app.get '/auth/unsubscribe', (req, res) ->
|
||||
app.get '/auth/unsubscribe', (req, res) ->
|
||||
req.query.email = decodeURIComponent(req.query.email)
|
||||
email = req.query.email
|
||||
unless req.query.email
|
||||
return errors.badInput res, 'No email provided to unsubscribe.'
|
||||
|
@ -131,7 +132,7 @@ module.exports.setup = (app) ->
|
|||
return errors.serverError res, 'Database failure.' if err
|
||||
res.send "Unsubscribed #{req.query.email} from CodeCombat emails for #{session.levelName} #{session.team} ladder updates. Sorry to see you go! <p><a href='/play/ladder/#{session.levelID}#my-matches'>Ladder preferences</a></p>"
|
||||
res.end()
|
||||
|
||||
|
||||
User.findOne({emailLower: req.query.email.toLowerCase()}).exec (err, user) ->
|
||||
if not user
|
||||
return errors.notFound res, "No user found with email '#{req.query.email}'"
|
||||
|
@ -143,7 +144,11 @@ module.exports.setup = (app) ->
|
|||
emails.recruitNotes ?= {}
|
||||
emails.recruitNotes.enabled = false
|
||||
msg = "Unsubscribed #{req.query.email} from recruiting emails."
|
||||
|
||||
else if req.query.employerNotes
|
||||
emails.employerNotes ?= {}
|
||||
emails.employerNotes.enabled = false
|
||||
|
||||
msg = "Unsubscribed #{req.query.email} from employer emails."
|
||||
else
|
||||
msg = "Unsubscribed #{req.query.email} from all CodeCombat emails. Sorry to see you go!"
|
||||
emailSettings.enabled = false for emailSettings in _.values(emails)
|
||||
|
|
|
@ -1,17 +1,330 @@
|
|||
mail = require '../commons/mail'
|
||||
MailSent = require '../mail/sent/MailSent'
|
||||
User = require '../users/User'
|
||||
async = require 'async'
|
||||
errors = require '../commons/errors'
|
||||
config = require '../../server_config'
|
||||
LevelSession = require '../levels/sessions/LevelSession'
|
||||
Level = require '../levels/Level'
|
||||
log = require 'winston'
|
||||
sendwithus = require '../sendwithus'
|
||||
|
||||
|
||||
if config.isProduction
|
||||
lockManager = require '../commons/LockManager'
|
||||
|
||||
module.exports.setup = (app) ->
|
||||
app.all config.mail.mailchimpWebhook, handleMailchimpWebHook
|
||||
app.get '/mail/cron/ladder-update', handleLadderUpdate
|
||||
if lockManager
|
||||
setupScheduledEmails()
|
||||
|
||||
setupScheduledEmails = ->
|
||||
testForLockManager()
|
||||
mailTasks = [
|
||||
taskFunction: candidateUpdateProfileTask
|
||||
frequencyMs: 10 * 60 * 1000 #10 minutes
|
||||
,
|
||||
taskFunction: internalCandidateUpdateTask
|
||||
frequencyMs: 10 * 60 * 1000 #10 minutes
|
||||
,
|
||||
taskFunction: employerNewCandidatesAvailableTask
|
||||
frequencyMs: 10 * 60 * 1000 #10 minutes
|
||||
]
|
||||
|
||||
for mailTask in mailTasks
|
||||
setInterval mailTask.taskFunction, mailTask.frequencyMs
|
||||
|
||||
testForLockManager = -> unless lockManager then throw "The system isn't configured to do distributed locking!"
|
||||
|
||||
### Candidate Update Reminder Task ###
|
||||
|
||||
candidateUpdateProfileTask = ->
|
||||
mailTaskName = "candidateUpdateProfileTask"
|
||||
lockDurationMs = 2 * 60 * 1000
|
||||
currentDate = new Date()
|
||||
timeRanges = []
|
||||
for weekPair in [[4, 2,'two weeks'], [8, 4, 'four weeks'], [8, 52, 'eight weeks']]
|
||||
timeRanges.push
|
||||
start: generateWeekOffset currentDate, weekPair[0]
|
||||
end: generateWeekOffset currentDate, weekPair[1]
|
||||
name: weekPair[2]
|
||||
lockManager.setLock mailTaskName, lockDurationMs, (err) ->
|
||||
if err? then return log.error "Error getting a distributed lock for task #{mailTaskName}: #{err}"
|
||||
async.each timeRanges, emailTimeRange.bind({mailTaskName: mailTaskName}), (err) ->
|
||||
if err
|
||||
log.error "There was an error sending the candidate profile update reminder emails: #{err}"
|
||||
else
|
||||
log.info "Completed mail task #{mailTaskName}"
|
||||
lockManager.releaseLock mailTaskName, (err) ->
|
||||
if err? then return log.error "There was an error releasing the distributed lock for task #{mailTaskName}: #{err}"
|
||||
|
||||
generateWeekOffset = (originalDate, numberOfWeeks) ->
|
||||
return (new Date(originalDate.getTime() - numberOfWeeks * 7 * 24 * 60 * 60 * 1000)).toISOString()
|
||||
|
||||
emailTimeRange = (timeRange, emailTimeRangeCallback) ->
|
||||
waterfallContext =
|
||||
"timeRange": timeRange
|
||||
"mailTaskName": @mailTaskName
|
||||
async.waterfall [
|
||||
findAllCandidatesWithinTimeRange.bind(waterfallContext)
|
||||
(unfilteredCandidates, cb) ->
|
||||
async.reject unfilteredCandidates, candidateFilter.bind(waterfallContext), cb.bind(null, null)
|
||||
(filteredCandidates, cb) ->
|
||||
async.each filteredCandidates, sendReminderEmailToCandidate.bind(waterfallContext), cb
|
||||
], emailTimeRangeCallback
|
||||
|
||||
findAllCandidatesWithinTimeRange = (cb) ->
|
||||
findParameters =
|
||||
"jobProfile.updated":
|
||||
$gt: @timeRange.start
|
||||
$lte: @timeRange.end
|
||||
"jobProfileApproved": true
|
||||
selection = "_id email jobProfile.name jobProfile.updated emails" #make sure to check for anyNotes too.
|
||||
User.find(findParameters).select(selection).lean().exec cb
|
||||
|
||||
candidateFilter = (candidate, sentEmailFilterCallback) ->
|
||||
if candidate.emails?.anyNotes?.enabled is false or candidate.emails?.recruitNotes?.enabled is false
|
||||
return sentEmailFilterCallback true
|
||||
findParameters =
|
||||
"user": candidate._id
|
||||
"mailTask": @mailTaskName
|
||||
"metadata.timeRangeName": @timeRange.name
|
||||
"metadata.updated": candidate.jobProfile.updated
|
||||
MailSent.find(findParameters).lean().exec (err, sentMail) ->
|
||||
if err?
|
||||
log.error "Error finding mail sent for task #{@mailTaskName} and user #{candidate._id}!"
|
||||
sentEmailFilterCallback true
|
||||
else
|
||||
sentEmailFilterCallback Boolean(sentMail.length)
|
||||
|
||||
findEmployersSignedUpAfterDate = (dateObject, cb) ->
|
||||
countParameters =
|
||||
$or: [{"dateCreated": {$gte: dateObject}},{"signedEmployerAgreement":{$gte: dateObject}}]
|
||||
employerAt: {$exists: true}
|
||||
permissions: "employer"
|
||||
User.count countParameters, cb
|
||||
|
||||
sendReminderEmailToCandidate = (candidate, sendEmailCallback) ->
|
||||
findEmployersSignedUpAfterDate new Date(candidate.jobProfile.updated), (err, employersAfterCount) =>
|
||||
if err?
|
||||
log.error "There was an error finding employers who signed up after #{candidate.jobProfile.updated}: #{err}"
|
||||
return sendEmailCallback err
|
||||
if employersAfterCount < 2
|
||||
employersAfterCount = 2
|
||||
context =
|
||||
email_id: "tem_CtTLsKQufxrxoPMn7upKiL"
|
||||
recipient:
|
||||
address: candidate.email
|
||||
name: candidate.jobProfile.name
|
||||
email_data:
|
||||
new_company: employersAfterCount
|
||||
company_name: "CodeCombat"
|
||||
user_profile: "http://codecombat.com/account/profile/#{candidate._id}"
|
||||
recipient_address: encodeURIComponent(candidate.email)
|
||||
log.info "Sending #{@timeRange.name} update reminder to #{context.recipient.name}(#{context.recipient.address})"
|
||||
newSentMail =
|
||||
mailTask: @mailTaskName
|
||||
user: candidate._id
|
||||
metadata:
|
||||
timeRangeName: @timeRange.name
|
||||
updated: candidate.jobProfile.updated
|
||||
MailSent.create newSentMail, (err) ->
|
||||
if err? then return sendEmailCallback err
|
||||
sendwithus.api.send context, (err, result) ->
|
||||
log.error "Error sending candidate update reminder email: #{err} with result #{result}" if err
|
||||
sendEmailCallback null
|
||||
### End Candidate Update Reminder Task ###
|
||||
### Internal Candidate Update Reminder Email ###
|
||||
internalCandidateUpdateTask = ->
|
||||
mailTaskName = "internalCandidateUpdateTask"
|
||||
lockDurationMs = 2 * 60 * 1000
|
||||
lockManager.setLock mailTaskName, lockDurationMs, (err) ->
|
||||
if err? then return log.error "Error getting a distributed lock for task #{mailTaskName}: #{err}"
|
||||
emailInternalCandidateUpdateReminder.call {"mailTaskName":mailTaskName}, (err) ->
|
||||
if err
|
||||
log.error "There was an error sending the internal candidate update reminder.: #{err}"
|
||||
else
|
||||
log.info "Sent internal candidate update reminder email!"
|
||||
lockManager.releaseLock mailTaskName, (err) ->
|
||||
if err? then return log.error "There was an error releasing the distributed lock for task #{mailTaskName}: #{err}"
|
||||
|
||||
emailInternalCandidateUpdateReminder = (internalCandidateUpdateReminderCallback) ->
|
||||
currentTime = new Date()
|
||||
beginningOfUTCDay = new Date()
|
||||
beginningOfUTCDay.setUTCHours(0,0,0,0)
|
||||
asyncContext =
|
||||
"beginningOfUTCDay": beginningOfUTCDay
|
||||
"currentTime": currentTime
|
||||
"mailTaskName": @mailTaskName
|
||||
async.waterfall [
|
||||
findNonApprovedCandidatesWhoUpdatedJobProfileToday.bind(asyncContext)
|
||||
(unfilteredCandidates, cb) ->
|
||||
async.reject unfilteredCandidates, candidatesUpdatedTodayFilter.bind(asyncContext), cb.bind(null,null)
|
||||
(filteredCandidates, cb) ->
|
||||
async.each filteredCandidates, sendInternalCandidateUpdateReminder.bind(asyncContext), cb
|
||||
], internalCandidateUpdateReminderCallback
|
||||
|
||||
findNonApprovedCandidatesWhoUpdatedJobProfileToday = (cb) ->
|
||||
findParameters =
|
||||
"jobProfile.updated":
|
||||
$lte: @currentTime.toISOString()
|
||||
$gt: @beginningOfUTCDay.toISOString()
|
||||
"jobProfileApproved": false
|
||||
User.find(findParameters).select("_id jobProfile.name jobProfile.updated").lean().exec cb
|
||||
|
||||
candidatesUpdatedTodayFilter = (candidate, cb) ->
|
||||
findParameters =
|
||||
"user": candidate._id
|
||||
"mailTask": @mailTaskName
|
||||
"metadata.beginningOfUTCDay": @beginningOfUTCDay
|
||||
MailSent.find(findParameters).lean().exec (err, sentMail) ->
|
||||
if err?
|
||||
log.error "Error finding mail sent for task #{@mailTaskName} and user #{candidate._id}!"
|
||||
cb true
|
||||
else
|
||||
cb Boolean(sentMail.length)
|
||||
|
||||
sendInternalCandidateUpdateReminder = (candidate, cb) ->
|
||||
context =
|
||||
email_id: "tem_Ac7nhgKqatTHBCgDgjF5pE"
|
||||
recipient:
|
||||
address: "team@codecombat.com"
|
||||
name: "The CodeCombat Team"
|
||||
email_data:
|
||||
new_candidate_profile: "https://codecombat.com/account/profile/#{candidate._id}"
|
||||
log.info "Sending candidate updated reminder for #{candidate.jobProfile.name}"
|
||||
newSentMail =
|
||||
mailTask: @mailTaskName
|
||||
user: candidate._id
|
||||
metadata:
|
||||
beginningOfUTCDay: @beginningOfUTCDay
|
||||
|
||||
MailSent.create newSentMail, (err) ->
|
||||
if err? then return cb err
|
||||
sendwithus.api.send context, (err, result) ->
|
||||
log.error "Error sending interal candidate update email: #{err} with result #{result}" if err
|
||||
cb null
|
||||
|
||||
### End Internal Candidate Update Reminder Email ###
|
||||
### Employer New Candidates Available Email ###
|
||||
employerNewCandidatesAvailableTask = ->
|
||||
mailTaskName = "employerNewCandidatesAvailableTask"
|
||||
lockDurationMs = 2 * 60 * 1000
|
||||
lockManager.setLock mailTaskName, lockDurationMs, (err) ->
|
||||
if err? then return log.error "Error getting a distributed lock for task #{mailTaskName}: #{err}"
|
||||
emailEmployerNewCandidatesAvailable.call {"mailTaskName":mailTaskName}, (err) ->
|
||||
if err
|
||||
log.error "There was an error completing the new candidates available task: #{err}"
|
||||
else
|
||||
log.info "Completed the employer new candidates available task!"
|
||||
lockManager.releaseLock mailTaskName, (err) ->
|
||||
if err? then return log.error "There was an error releasing the distributed lock for task #{mailTaskName}: #{err}"
|
||||
|
||||
emailEmployerNewCandidatesAvailable = (emailEmployerNewCandidatesAvailableCallback) ->
|
||||
currentTime = new Date()
|
||||
asyncContext =
|
||||
"currentTime": currentTime
|
||||
"mailTaskName": @mailTaskName
|
||||
|
||||
async.waterfall [
|
||||
findAllEmployers
|
||||
makeEmployerNamesEasilyAccessible
|
||||
(allEmployers, cb) ->
|
||||
async.reject allEmployers, employersEmailedDigestMoreThanWeekAgoFilter.bind(asyncContext), cb.bind(null,null)
|
||||
(employersToEmail, cb) ->
|
||||
async.each employersToEmail, sendEmployerNewCandidatesAvailableEmail.bind(asyncContext), cb
|
||||
], emailEmployerNewCandidatesAvailableCallback
|
||||
|
||||
findAllEmployers = (cb) ->
|
||||
findParameters =
|
||||
"employerAt":
|
||||
$exists: true
|
||||
permissions: "employer"
|
||||
selection = "_id email employerAt signedEmployerAgreement.data.firstName signedEmployerAgreement.data.lastName activity dateCreated emails"
|
||||
User.find(findParameters).select(selection).lean().exec cb
|
||||
|
||||
makeEmployerNamesEasilyAccessible = (allEmployers, cb) ->
|
||||
for employer, index in allEmployers
|
||||
if employer.signedEmployerAgreement?.data?.firstName
|
||||
employer.name = employer.signedEmployerAgreement.data.firstName + " " + employer.signedEmployerAgreement.data.lastName
|
||||
delete employer.signedEmployerAgreement
|
||||
allEmployers[index] = employer
|
||||
cb null, allEmployers
|
||||
|
||||
employersEmailedDigestMoreThanWeekAgoFilter = (employer, cb) ->
|
||||
if employer.emails?.employerNotes?.enabled is false
|
||||
return sentEmailFilterCallback true
|
||||
findParameters =
|
||||
"user": employer._id
|
||||
"mailTask": @mailTaskName
|
||||
"sent":
|
||||
$gt: new Date(@currentTime.getTime() - 7 * 24 * 60 * 60 * 1000)
|
||||
MailSent.find(findParameters).lean().exec (err, sentMail) ->
|
||||
if err?
|
||||
log.error "Error finding mail sent for task #{@mailTaskName} and employer #employer._id}!"
|
||||
cb true
|
||||
else
|
||||
cb Boolean(sentMail.length)
|
||||
|
||||
sendEmployerNewCandidatesAvailableEmail = (employer, cb) ->
|
||||
lastLoginDate = employer.activity?.login?.last ? employer.dateCreated
|
||||
countParameters =
|
||||
"jobProfileApproved": true
|
||||
$or: [
|
||||
jobProfileApprovedDate:
|
||||
$gt: lastLoginDate.toISOString()
|
||||
,
|
||||
jobProfileApprovedDate:
|
||||
$exists: false
|
||||
"jobProfile.updated":
|
||||
$gt: lastLoginDate.toISOString()
|
||||
]
|
||||
User.count countParameters, (err, numberOfCandidatesSinceLogin) =>
|
||||
if err? then return cb err
|
||||
context =
|
||||
email_id: "tem_CCcHKr95Nvu5bT7c7iHCtm"
|
||||
recipient:
|
||||
address: employer.email
|
||||
email_data:
|
||||
new_candidates: numberOfCandidatesSinceLogin
|
||||
employer_company_name: employer.employerAt
|
||||
company_name: "CodeCombat"
|
||||
recipient_address: encodeURIComponent(employer.email)
|
||||
if employer.name
|
||||
context.recipient.name = employer.name
|
||||
log.info "Sending available candidates update reminder to #{context.recipient.name}(#{context.recipient.address})"
|
||||
newSentMail =
|
||||
mailTask: @mailTaskName
|
||||
user: employer._id
|
||||
MailSent.create newSentMail, (err) ->
|
||||
if err? then return cb err
|
||||
sendwithus.api.send context, (err, result) ->
|
||||
log.error "Error sending employer candidates available email: #{err} with result #{result}" if err
|
||||
cb null
|
||||
|
||||
### End Employer New Candidates Available Email ###
|
||||
|
||||
### New Recruit Leaderboard Email ###
|
||||
###
|
||||
newRecruitLeaderboardEmailTask = ->
|
||||
# tem_kMQFCKX3v4DNAQDsMAsPJC
|
||||
#maxRank and maxRankTime should be recorded if isSimulating is false
|
||||
mailTaskName = "newRecruitLeaderboardEmailTask"
|
||||
lockDurationMs = 6000
|
||||
lockManager.setLock mailTaskName, lockDurationMs, (err, lockResult) ->
|
||||
###
|
||||
### End New Recruit Leaderboard Email ###
|
||||
|
||||
### Employer Matching Candidate Notification Email ###
|
||||
###
|
||||
employerMatchingCandidateNotificationTask = ->
|
||||
# tem_mYsepTfWQ265noKfZJcbBH
|
||||
#save email filters in their own collection
|
||||
mailTaskName = "employerMatchingCandidateNotificationTask"
|
||||
lockDurationMs = 6000
|
||||
lockManager.setLock mailTaskName, lockDurationMs, (err, lockResult) ->
|
||||
###
|
||||
### End Employer Matching Candidate Notification Email ###
|
||||
### Ladder Update Email ###
|
||||
|
||||
DEBUGGING = false
|
||||
LADDER_PREGAME_INTERVAL = 2 * 3600 * 1000 # Send emails two hours before players last submitted.
|
||||
|
@ -28,6 +341,8 @@ isRequestFromDesignatedCronHandler = (req, res) ->
|
|||
return false
|
||||
return true
|
||||
|
||||
|
||||
|
||||
handleLadderUpdate = (req, res) ->
|
||||
log.info('Going to see about sending ladder update emails.')
|
||||
requestIsFromDesignatedCronHandler = isRequestFromDesignatedCronHandler req, res
|
||||
|
@ -151,6 +466,7 @@ getScoreHistoryGraphURL = (session, daysAgo) ->
|
|||
chartData = times.join(',') + '|' + scores.join(',')
|
||||
"https://chart.googleapis.com/chart?chs=600x75&cht=lxy&chtt=Score%3A+#{currentScore}&chts=222222,12,r&chf=a,s,000000FF&chls=2&chd=t:#{chartData}&chxt=y&chxr=0,#{minScore},#{maxScore}"
|
||||
|
||||
### End Ladder Update Email ###
|
||||
handleMailchimpWebHook = (req, res) ->
|
||||
post = req.body
|
||||
|
||||
|
|
|
@ -12,6 +12,10 @@ config.mongo =
|
|||
host: process.env.COCO_MONGO_HOST or 'localhost'
|
||||
db: process.env.COCO_MONGO_DATABASE_NAME or 'coco'
|
||||
mongoose_replica_string: process.env.COCO_MONGO_MONGOOSE_REPLICA_STRING or ''
|
||||
|
||||
config.redis =
|
||||
port: process.env.COCO_REDIS_PORT or 6379
|
||||
host: process.env.COCO_REDIS_HOST or 'localhost'
|
||||
|
||||
if config.unittest
|
||||
config.port += 1
|
||||
|
|
Loading…
Add table
Reference in a new issue