mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2024-12-03 04:17:12 -05:00
12baae7acd
Lead assignments can be incorrect when the user requests licenses needed via the contact form before the related emails have been added to the Close.io lead. We’ll use a custom field for automation sales contact if there’s no email activity.
182 lines
8.2 KiB
CoffeeScript
182 lines
8.2 KiB
CoffeeScript
config = require '../../server_config'
|
|
log = require 'winston'
|
|
request = require 'request'
|
|
|
|
apiKey = config.closeIO?.apiKey
|
|
defaultSalesContactUserID = 'user_Fh0uLUkRIKMk2to61ISq8PneyQonuD2i7hes6RhZgDX'
|
|
|
|
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: (userEmail, done) ->
|
|
# Sales contact email precedence: previous email to contact, previous email to lead, lead custom field, lead status default
|
|
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:#{userEmail}"
|
|
request.get uri, (error, response, body) =>
|
|
return done(error) if error
|
|
leads = JSON.parse(body)
|
|
return done("Unexpected Close leads format: " + body) unless leads.data?
|
|
return done("No existing Close.IO lead found for #{userEmail}") 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?
|
|
activityForThisContact = null
|
|
activityForThisLead = null
|
|
for activity in activities.data when activity?._type is 'Email'
|
|
continue unless /@codecombat\.(?:com)|(?:nl)/ig.test(activity.sender)
|
|
continue if activity.sender.indexOf('brian@codecombat.com') >= 0
|
|
continue if activity.sender.indexOf(config.mail.username) >= 0
|
|
activityForThisLead ?= activity
|
|
for email in activity.to or [] when email?.toLowerCase() is userEmail?.toLowerCase()
|
|
activityForThisContact ?= activity
|
|
|
|
if activityForThisContact
|
|
return done(null, activityForThisContact.sender, activityForThisContact.user_id, lead.id)
|
|
else if activityForThisLead
|
|
return done(null, activityForThisLead.sender, activityForThisLead.user_id, lead.id)
|
|
|
|
if email = lead.custom?['auto_sales_email']
|
|
# Have to lookup Close user Id if email from lead custom field
|
|
uri = "https://#{apiKey}:X@app.close.io/api/v1/user/?_fields=id,email"
|
|
request.get uri, (error, response, body) =>
|
|
return done(error) if error
|
|
users = JSON.parse(body)
|
|
return done("Unexpected Close users format: " + body) unless users.data?
|
|
userID = null
|
|
for user in users.data or [] when user.email?.toLowerCase() is email.toLowerCase()
|
|
userID = user.id
|
|
break
|
|
if userID
|
|
return done(null, email, userID, lead.id)
|
|
else
|
|
@logError("No user found for leadID=#{lead.id} user=#{userEmail} auto_sales_email=#{lead.custom?['auto_sales_email']}")
|
|
return done(null, config.mail.supportSchools, defaultSalesContactUserID, lead.id)
|
|
else
|
|
return done(null, config.mail.supportSchools, defaultSalesContactUserID, lead.id)
|
|
catch error
|
|
log.error("closeIO.getSalesContactEmail Error for #{userEmail}: #{JSON.stringify(error)}")
|
|
return done(error)
|
|
|
|
sendMail: (fromAddress, subject, content, salesContactEmail, leadID, done) ->
|
|
# log.info("DEBUG: closeIO.sendMail #{fromAddress} #{subject} #{salesContactEmail} #{leadID}")
|
|
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) ->
|
|
# log.info("DEBUG: closeIO.processLicenseRequest #{teacherEmail} #{userID} #{leadID} #{licensesRequested} #{amount}")
|
|
|
|
# Update lead with licenses requested
|
|
licensesRequested = parseInt(licensesRequested)
|
|
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 license inquiry #{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: 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()
|