codecombat/server/lib/closeIO.coffee

149 lines
6.2 KiB
CoffeeScript

config = require '../../server_config'
log = require 'winston'
request = require 'request'
apiKey = config.closeIO?.apiKey
module.exports =
logError: (msg) ->
log.error("Close.io Error: #{msg}")
createSalesLead: (user, email, newLeadData) ->
return @logError('No API key available') unless apiKey
@getLead email, (error, lead) =>
return @logError(JSON.stringify(error)) if error
return if lead
@createLead(user, email, newLeadData)
createLead: (user, email, newLeadData) ->
name = newLeadData.name ? email
postData =
display_name: newLeadData.organization ? name
name: newLeadData.organization ? name
contacts: [{
emails: [{email: email}]
name: name
}]
custom: {}
postData.contacts[0].phones = [phone: newLeadData.phone] if newLeadData.phone
for key, val of newLeadData
continue if key in ['name', 'organization', 'phone']
continue if _.isEmpty(val)
postData.custom[key] = val
postData.custom['userID'] = user.get('_id').valueOf() if user
options =
uri: 'https://' + apiKey + ':X@app.close.io/api/v1/lead/',
body: JSON.stringify(postData)
request.post options, (error, response, body) =>
return @logError(JSON.stringify(error)) if error
getLead: (email, done) ->
uri = 'https://' + apiKey + ':X@app.close.io/api/v1/lead/?query=email_address:' + email
request.get uri, (error, response, body) =>
return done(error) if error
leads = JSON.parse(body)
return done("Unexpected leads format: " + body) unless leads.data?
if leads.data?.length is 1
return done(null, leads.data[0])
else if leads.data?.length > 1
return done('ERROR: multiple leads returned for ' + email + ' ' + leads.data.length)
return done()
getSalesContactEmail: (email, done) ->
try
# NOTE: does not work on + email addresses due to Close.io API bug
uri = "https://#{apiKey}:X@app.close.io/api/v1/lead/?query=email_address:#{email}"
request.get uri, (error, response, body) =>
return done(error) if error
leads = JSON.parse(body)
return done("Unexpected leads format: " + body) unless leads.data?
return done("No existing Close.IO lead found for #{email}") unless leads.data?.length > 0
lead = leads.data[0]
uri = "https://#{apiKey}:X@app.close.io/api/v1/activity/?lead_id=#{lead.id}"
request.get uri, (error, response, body) =>
return done(error) if error
activities = JSON.parse(body)
return done("Unexpected activities format: " + body) unless activities.data?
for activity in activities.data when activity._type is 'Email'
if /@codecombat\.(?:com)|(?:nl)/ig.test(activity.sender) and not activity.sender?.indexOf(config.mail.username) >= 0
return done(null, activity.sender, activity.user_id, lead.id)
return done(null, config.mail.supportSchools, lead.id)
catch error
log.error("closeIO.getSalesContactEmail Error for #{email}: #{JSON.stringify(error)}")
return done(error)
sendMail: (fromAddress, subject, content, salesContactEmail, leadID, done) ->
# log.info("DEBUG: closeIO.sendMail #{fromAddress} #{subject} #{content}")
matches = salesContactEmail.match(/^[a-zA-Z_]+ <(\w+@[a-zA-Z_]+?\.[a-zA-Z]{2,3})>$|(\w+@[a-zA-Z_]+?\.[a-zA-Z]{2,3})/i)
salesContactEmail = matches?[1] ? matches?[2] ? config.mail.supportSchools
salesContactEmail = config.mail.supportSchools if salesContactEmail?.indexOf('brian@codecombat.com') >= 0
postData =
to: [salesContactEmail]
sender: config.mail.username
subject: subject
body_text: content
lead_id: leadID
status: 'outbox'
options =
uri: "https://#{apiKey}:X@app.close.io/api/v1/activity/email/"
body: JSON.stringify(postData)
request.post options, (error, response, body) =>
return done(error) if error
result = JSON.parse(body)
if result.errors or result['field-errors']
errorMessage = "Close.io Send email POST error for #{fromAddress} #{JSON.stringify(result.errors)} #{JSON.stringify(result['field-errors'])}"
return done(errorMessage)
return done()
processLicenseRequest: (teacherEmail, userID, leadID, licensesRequested, amount, done) ->
# Update lead with licenses requested
putData = 'custom.licensesRequested': licensesRequested
options =
uri: "https://#{apiKey}:X@app.close.io/api/v1/lead/#{leadID}/"
body: JSON.stringify(putData)
request.put options, (error, response, body) =>
return done(error) if error
result = JSON.parse(body)
if result.errors or result['field-errors']
errorMessage = "Update Close.io lead PUT error for #{teacherEmail} #{leadID}"
return done(errorMessage)
# Create call task
postData =
_type: "lead"
lead_id: leadID
assigned_to: userID
text: "Call #{teacherEmail}"
is_complete: false
options =
uri: "https://#{apiKey}:X@app.close.io/api/v1/task/"
body: JSON.stringify(postData)
request.post options, (error, response, body) =>
return done(error) if error
result = JSON.parse(body)
if result.errors or result['field-errors']
errorMessage = "Create Close.io call task POST error for #{teacherEmail} #{leadID}"
return done(errorMessage)
# Create opportunity
dateWon = new Date()
dateWon.setUTCMonth(dateWon.getUTCMonth() + 2)
postData =
note: "#{licensesRequested} licenses requested"
confidence: 5
date_won: dateWon.toISOString().substring(0, 10)
lead_id: leadID
status: 'Active'
value: parseInt(licensesRequested) * amount
value_period: "annual"
options =
uri: "https://#{apiKey}:X@app.close.io/api/v1/opportunity/"
body: JSON.stringify(postData)
request.post options, (error, response, body) =>
return done(error) if error
result = JSON.parse(body)
if result.errors or result['field-errors']
errorMessage = "Create Close.io opportunity POST error for #{teacherEmail} #{leadID}"
return done(errorMessage)
return done()