Website: add automated MDM nurture emails (#20760)
Changes: - Added three new emails for MDM-focused users who are psystage 3, 4 ,and 5. - Added a new email layout - Added a script to send nurture emails to users with an MDM primary buying situation who signed up after 2024-07-22 that - have been in psychological stage 3 for 24 hours - have been in psychological stage 4 for 24 hours - have been in psychological stage 5 for six weeks - Updated the USer model to have. four new attributes: - `psychologicalStageLastChangedAt` - `stageThreeNurtureEmailSentAt` - `stageFourNurtureEmailSentAt` - `stageFiveNurtureEmailSentAt` - Added a new custom config variable `sails.config.custom.contactEmailForNutureEmails`
This commit is contained in:
@@ -114,6 +114,24 @@ module.exports = {
|
||||
layout = 'layout-email';
|
||||
fakeData = {};
|
||||
break;
|
||||
case 'email-nurture-stage-three':
|
||||
layout = 'layout-nurture-email';
|
||||
fakeData = {
|
||||
firstName: 'Sage'
|
||||
};
|
||||
break;
|
||||
case 'email-nurture-stage-four':
|
||||
layout = 'layout-nurture-email';
|
||||
fakeData = {
|
||||
firstName: 'Sage'
|
||||
};
|
||||
break;
|
||||
case 'email-nurture-stage-five':
|
||||
layout = 'layout-nurture-email';
|
||||
fakeData = {
|
||||
firstName: 'Sage'
|
||||
};
|
||||
break;
|
||||
case 'email-deal-registration':
|
||||
layout = 'layout-email';
|
||||
fakeData = {
|
||||
|
||||
+5
-1
@@ -116,6 +116,7 @@ module.exports = {
|
||||
// - nothing » No change (Stage ??)
|
||||
|
||||
let psychologicalStage = userRecord.psychologicalStage;
|
||||
let psychologicalStageLastChangedAt = userRecord.psychologicalStageLastChangedAt;
|
||||
// Get the value of the submitted formData, we do this so we only need to check one variable, instead of (formData.attribute === 'foo');
|
||||
let valueFromFormData = _.values(formData)[0];
|
||||
if(currentStep === 'start') {
|
||||
@@ -211,6 +212,8 @@ module.exports = {
|
||||
|
||||
// Only update CRM records if the user's psychological stage changes.
|
||||
if(psychologicalStage !== userRecord.psychologicalStage) {
|
||||
// Update the psychologicalStageLastChangedAt timestamp if the user's psychological stage
|
||||
psychologicalStageLastChangedAt = Date.now();
|
||||
sails.helpers.salesforce.updateOrCreateContactAndAccount.with({
|
||||
emailAddress: this.req.me.emailAddress,
|
||||
firstName: this.req.me.firstName,
|
||||
@@ -236,7 +239,8 @@ module.exports = {
|
||||
.set({
|
||||
getStartedQuestionnaireAnswers: questionnaireProgress,
|
||||
lastSubmittedGetStartedQuestionnaireStep: currentStep,
|
||||
psychologicalStage
|
||||
psychologicalStage,
|
||||
psychologicalStageLastChangedAt,
|
||||
});
|
||||
// Return the JSON dictionary of form data submitted by this user.
|
||||
return getStartedProgress;
|
||||
|
||||
Vendored
+22
@@ -240,6 +240,28 @@ without necessarily having a billing card.`
|
||||
defaultsTo: '2 - Aware'
|
||||
},
|
||||
|
||||
psychologicalStageLastChangedAt: {
|
||||
type: 'number',
|
||||
description: 'A JS timestamp of when this user\'s psychological stage changed.',
|
||||
extendedDescription: 'Used when deciding whether or not to send a nuture email to this user',
|
||||
},
|
||||
|
||||
stageThreeNurtureEmailSentAt: {
|
||||
type: 'number',
|
||||
description: 'A JS timestamp of when the stage 3 nurture email was sent to the user.'
|
||||
},
|
||||
|
||||
stageFourNurtureEmailSentAt: {
|
||||
type: 'number',
|
||||
description: 'A JS timestamp of when the stage 4 nurture email was sent to the user.'
|
||||
},
|
||||
|
||||
stageFiveNurtureEmailSentAt: {
|
||||
type: 'number',
|
||||
description: 'A JS timestamp of when the stage 5 nurture email was sent to the user.'
|
||||
},
|
||||
|
||||
|
||||
// ╔═╗╔╦╗╔╗ ╔═╗╔╦╗╔═╗
|
||||
// ║╣ ║║║╠╩╗║╣ ║║╚═╗
|
||||
// ╚═╝╩ ╩╚═╝╚═╝═╩╝╚═╝
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 722 KiB |
Vendored
+5
@@ -390,6 +390,11 @@ module.exports.custom = {
|
||||
// customerWorkspaceOneOauthSecret: '…',
|
||||
// customerMigrationWebhookSecret: '…',
|
||||
|
||||
// For nurture emails:
|
||||
// contactEmailForNutureEmails: '…',
|
||||
// activityCaptureEmailForNutureEmails: '…',
|
||||
// contactNameForNurtureEmails: '…',
|
||||
|
||||
// Deal registration form
|
||||
// dealRegistrationContactEmailAddress: '…',
|
||||
|
||||
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
module.exports = {
|
||||
|
||||
|
||||
friendlyName: 'Deliver nurture emails',
|
||||
|
||||
|
||||
description: 'Sends nurture emails to users who have been at psychological stage 3 & 4 for more than a day, and users who have been stage five for six weeks.',
|
||||
|
||||
|
||||
fn: async function () {
|
||||
|
||||
sails.log('Running custom shell script... (`sails run deliver-nurture-emails`)');
|
||||
|
||||
let nowAt = Date.now();
|
||||
let nurtureCampaignStartedAt = new Date('07-22-2024').getTime();
|
||||
let oneHourAgoAt = nowAt - (1000 * 60 * 60);
|
||||
let oneDayAgoAt = nowAt - (1000 * 60 * 60);
|
||||
let sixWeeksAgoAt = nowAt - (1000 * 60 * 60 * 24 * 7 * 6);
|
||||
// Find user records that are over an hour old that were created after July 22nd.
|
||||
let usersWithMdmBuyingSituation = await User.find({
|
||||
primaryBuyingSituation: 'mdm',
|
||||
createdAt: {
|
||||
'>=': nurtureCampaignStartedAt,
|
||||
'<=': oneHourAgoAt,
|
||||
},
|
||||
});
|
||||
|
||||
// Only send emails to stage 3 users who have not received a nurture email for this stage, and that have been stage 3 for at least one day.
|
||||
let stageThreeMdmFocusedUsersWhoHaveNotReceivedAnEmail = _.filter(usersWithMdmBuyingSituation, (user)=>{
|
||||
return user.stageThreeNurtureEmailSentAt === 0
|
||||
&& user.psychologicalStage === '3 - Intrigued';
|
||||
});
|
||||
|
||||
// Only send emails to stage 4 users who have not received a a nurture email for this stage, and that have been stage 4 for at least one day.
|
||||
let stageFourMdmFocusedUsersWhoHaveNotReceivedAnEmail = _.filter(usersWithMdmBuyingSituation, (user)=>{
|
||||
return user.stageFourNurtureEmailSentAt === 0
|
||||
&& user.psychologicalStage === '4 - Has use case';
|
||||
});
|
||||
|
||||
// Only send emails to stage 5 users who have not received a nurture email for this stage, and that have been stage 5 for at least six weeks.
|
||||
let stageFiveMdmFocusedUsersWhoHaveNotReceivedAnEmail = _.filter(usersWithMdmBuyingSituation, (user)=>{
|
||||
return user.stageFiveNurtureEmailSentAt === 0
|
||||
&& user.psychologicalStage === '5 - Personally confident';
|
||||
});
|
||||
|
||||
for(let user of stageThreeMdmFocusedUsersWhoHaveNotReceivedAnEmail) {
|
||||
if(user.psychologicalStageLastChangedAt > oneDayAgoAt) {
|
||||
continue;
|
||||
} else {
|
||||
await sails.helpers.sendTemplateEmail.with({
|
||||
template: 'email-nurture-stage-three',
|
||||
layout: 'layout-nurture-email',
|
||||
templateData: {
|
||||
firstName: user.firstName
|
||||
},
|
||||
to: user.emailAddress,
|
||||
toName: `${user.firstName} ${user.lastName}`,
|
||||
subject: 'Was it any good?',
|
||||
bcc: [sails.config.custom.activityCaptureEmailForNutureEmails],
|
||||
from: sails.config.custom.contactEmailForNutureEmails,
|
||||
fromName: sails.config.custom.contactNameForNurtureEmails,
|
||||
ensureAck: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
await User.update({id: {in: _.pluck(stageThreeMdmFocusedUsersWhoHaveNotReceivedAnEmail, 'id')}})
|
||||
.set({
|
||||
stageThreeNurtureEmailSentAt: nowAt,
|
||||
});
|
||||
|
||||
for(let user of stageFourMdmFocusedUsersWhoHaveNotReceivedAnEmail) {
|
||||
if(user.psychologicalStageLastChangedAt > oneDayAgoAt) {
|
||||
continue;
|
||||
} else {
|
||||
await sails.helpers.sendTemplateEmail.with({
|
||||
template: 'email-nurture-stage-four',
|
||||
layout: 'layout-nurture-email',
|
||||
templateData: {
|
||||
firstName: user.firstName
|
||||
},
|
||||
to: user.emailAddress,
|
||||
toName: `${user.firstName} ${user.lastName}`,
|
||||
subject: 'Deploy open-source MDM',
|
||||
bcc: [sails.config.custom.activityCaptureEmailForNutureEmails],
|
||||
from: sails.config.custom.contactEmailForNutureEmails,
|
||||
fromName: sails.config.custom.contactNameForNurtureEmails,
|
||||
ensureAck: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
await User.update({id: {in: _.pluck(stageFourMdmFocusedUsersWhoHaveNotReceivedAnEmail, 'id')}})
|
||||
.set({
|
||||
stageFourNurtureEmailSentAt: nowAt,
|
||||
});
|
||||
|
||||
for(let user of stageFiveMdmFocusedUsersWhoHaveNotReceivedAnEmail) {
|
||||
if(user.psychologicalStageLastChangedAt > sixWeeksAgoAt) {
|
||||
continue;
|
||||
} else {
|
||||
await sails.helpers.sendTemplateEmail.with({
|
||||
template: 'email-nurture-stage-five',
|
||||
layout: 'layout-nurture-email',
|
||||
templateData: {
|
||||
firstName: user.firstName
|
||||
},
|
||||
to: user.emailAddress,
|
||||
toName: `${user.firstName} ${user.lastName}`,
|
||||
subject: 'Update',
|
||||
bcc: [sails.config.custom.activityCaptureEmailForNutureEmails],
|
||||
from: sails.config.custom.contactEmailForNutureEmails,
|
||||
fromName: sails.config.custom.contactNameForNurtureEmails,
|
||||
ensureAck: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
await User.update({id: {in: _.pluck(stageFiveMdmFocusedUsersWhoHaveNotReceivedAnEmail, 'id')}})
|
||||
.set({
|
||||
stageFiveNurtureEmailSentAt: nowAt,
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
<% /* Note: This is injected into `views/layouts/layout-nurture-email.ejs` */ %>
|
||||
<div style="padding: 0px 0px 0px 0px">
|
||||
<p style=" font-size: 16px; line-height: 24px; margin-bottom: 24px; margin-top: 0px">Hi <%- firstName %>,</p>
|
||||
<p style=" font-size: 16px; line-height: 24px; margin-bottom: 48px; margin-top: 0px">There’s been some big updates to Fleet and the roadmap. One of our founders is holding a small group call this Friday to help folks get set up. Feel free to join us: <a style="color: #6A67FE; text-decoration: none; word-break: break-word;" href="https://calendly.com/fleetdm/chat" target="_blank">https://calendly.com/fleetdm/chat</a></p>
|
||||
|
||||
</div>
|
||||
@@ -0,0 +1,6 @@
|
||||
<% /* Note: This is injected into `views/layouts/layout-nurture-email.ejs` */ %>
|
||||
<div style="padding: 0px 0px 0px 0px">
|
||||
<p style=" font-size: 16px; line-height: 24px; margin-bottom: 24px; margin-top: 0px">Hi <%- firstName %>,</p>
|
||||
<p style=" font-size: 16px; line-height: 24px; margin-bottom: 48px; margin-top: 0px">There’s never a good time to migrate your MDM, but Fleet makes it easier. Here’s what the deployment process looks like: <a style="color: #6A67FE; text-decoration: none; word-break: break-word;" href="https://fleetdm.com/docs/deploy/deploy-fleet" target="_blank">https://fleetdm.com/docs/deploy/deploy-fleet</a></p>
|
||||
|
||||
</div>
|
||||
@@ -0,0 +1,7 @@
|
||||
<% /* Note: This is injected into `views/layouts/layout-nurture-email.ejs` */ %>
|
||||
<div style="padding: 0px 0px 0px 0px">
|
||||
<p style=" font-size: 16px; line-height: 24px; margin-bottom: 24px; margin-top: 0px">Hi <%- firstName %>,</p>
|
||||
<p style=" font-size: 16px; line-height: 24px; margin-bottom: 24px; margin-top: 0px">We noticed that you were checking out Fleet recently. What did you think?</p>
|
||||
<p style=" font-size: 16px; line-height: 24px; margin-bottom: 48px; margin-top: 0px">PS. One of our founders is holding a small group call this Friday to help folks get set up. Feel free to join us: <a style="color: #6A67FE; text-decoration: none; word-break: break-word;" href="https://calendly.com/fleetdm/chat" target="_blank">https://calendly.com/fleetdm/chat</a></p>
|
||||
|
||||
</div>
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
<% /* Default layout for email templates */ %>
|
||||
<div style="width: 100%; max-width: 600px; font-family: 'Inter','Helvetica','arial', sans-serif; box-sizing: border-box; padding: 0; margin: auto; border-radius: 8px; border: 1px solid #E2E4EA; background: #FFF;">
|
||||
<div style="background: transparent; text-align: left;">
|
||||
<a href="https://fleetdm.com"><img style="display: inline-block; width: 100%; max-width: 100%; border-top-left-radius: 8px; border-top-right-radius: 8px;" alt="Logo" src="<%= url.resolve(sails.config.custom.baseUrl,'images/email-header-959x452@2x.png')%>"/></a>
|
||||
</div>
|
||||
<div style="color: #192147; font-size: 16px; box-sizing: border-box; padding: 32px; width: 100%; max-width: 600px; margin-left: auto; margin-right: auto;">
|
||||
<div>
|
||||
<%- body %>
|
||||
<hr style="color: #E2E4EA;"/>
|
||||
<div style="display: inline-flex; padding-top: 32px;">
|
||||
<a href="https://fleetdm.com/slack" target="_blank"><img style="height: 20px; width: 20px; margin-right: 24px;" alt="Join the osquery Slack community" src="<%= url.resolve(sails.config.custom.baseUrl,'images/logo-slack-dark-20x20@2x.png')%>"></a>
|
||||
<a href="https://github.com/fleetdm/fleet" target="_blank"><img style="height: 20px; width: 20px; margin-right: 24px;" alt="View Fleet's GitHub repo" src="<%= url.resolve(sails.config.custom.baseUrl,'images/logo-github-dark-24x24@2x.png')%>"></a>
|
||||
<a href="https://fleetdm.com/slack" target="_blank"><img style="height: 20px; width: 20px; margin-right: 24px;" alt="Conenct with Fleet on LinkedIn" src="<%= url.resolve(sails.config.custom.baseUrl,'images/logo-linkedin-24x24@2x.png')%>"></a>
|
||||
<a href="https://twitter.com/fleetctl" target="_blank"><img style="height: 20px; width: 24px; margin-right: 24px;" alt="Follow Fleet on Twitter" src="<%= url.resolve(sails.config.custom.baseUrl,'images/logo-x-24x24@2x.png')%>"></a>
|
||||
<a href="https://www.youtube.com/channel/UCZyoqZ4exJvoibmTKJrQ-dQ" target="_blank"><img style="width: 20px; margin-top: 3px; margin-right: 24px;" alt="Watch Fleet's Youtube videos" src="<%= url.resolve(sails.config.custom.baseUrl,'images/logo-youtube-29x20@2x.png')%>"></a>
|
||||
</div>
|
||||
<div style="text-align: left; padding-top: 15px; font-size: 12px; color: #3E4771;">
|
||||
<p>© <%= (new Date()).getFullYear() %> Fleet Inc.<br> All trademarks are the property of their respective owners.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user