DEV Community

Cover image for How to create a Node bot that sends happy emails throughout the year
Jessica Wilkins
Jessica Wilkins

Posted on • Edited on

How to create a Node bot that sends happy emails throughout the year

In this tutorial, I will show you how to make your very own email bot using Node, Express, Nodemailer and node-cron.

You will learn how to program the bot to send emails throughout the day to friends and family. You will also learn how to send messages for specific dates like a friend's birthday.

Prerequisites

It would help to have basic working knowledge of JavaScript.

Checking to make sure Node is installed

Before we can get started writing code, we need to first check and see if Node is installed on the computer.

If you are using a Mac, then open up the Terminal app.
If you are using Windows, then open up the Command Prompt.

In the command line, run node -v. If installed, it will come back with a version number like v16.10.0.

If it is not installed, then you can install Node from the website.

Creating the project folder

First, go to the location where you want to add the folder. I am going to choose the Desktop as my location.

In the command line, use the cd command to change directory.

cd Desktop
Enter fullscreen mode Exit fullscreen mode

Then use the mkdir command to create a new folder in that directory. I am going to name our project email-bot.

mkdir email-bot
Enter fullscreen mode Exit fullscreen mode

You should see the new folder show up in the computer.
project folder

We will use the cd command again to change directories into the email-bot folder.

 cd email-bot
Enter fullscreen mode Exit fullscreen mode

In the command line, you should see that we have successfully changed to the email-bot folder.

email bot folder

Creating the package.json file

A package.json file contains a lot of important information for our project including name, version, scripts and dependencies.

Run the command npm init --yes or npm init --y. That will create a default package.json file that you can modify later.

This is what the default package.json file will look like.
default package json

Creating the server.js file

This file will contain the bulk of our logic for sending emails.

In the command line, use the touch command to add a server.js file.

touch server.js
Enter fullscreen mode Exit fullscreen mode

Creating an .env file

The .env file will contain all of the information for the email addresses.

This is a special type of file that contains sensitive information that you don't want getting into the wrong hands.

That is why it is important to never commit your .env file to GitHub.

In the command line, run touch .env.

touch .env
Enter fullscreen mode Exit fullscreen mode

Creating a .gitignore file

In this file, you will list out the names of files and folder you want Git to ignore when pushing your changes to GitHub.

Later on, we will be adding the .env file to our .gitignore.

To add a .gitignore file, run the command touch .gitignore.

touch .gitignore
Enter fullscreen mode Exit fullscreen mode

Installing Express, Nodemailer, dotenv and node-cron

Express is a framework that is used with Node to help create server-side apps.

The dotenv package, is used to load our environment variables into the server.js file. Our environment variables will be the email addresses from the .env file.

Nodemailer will be used to send the emails.

The node-cron package, will be used to schedule the dates and times for the email delivery.

In the command line, we can install all of these packages at once.

Run this command in the command line.

npm i express nodemailer node-cron dotenv
Enter fullscreen mode Exit fullscreen mode

Now it is time to move to the code editor and start coding. I will be using Visual Studio Code, but you are free to use another code editor.

Open up the project in your editor and check to make sure you have all of the files we created.
folder structure

The package-lock.json file and node_modules folder were created when we installed the packages.

Adding to the .gitignore file

You don't want to push the node_modules folder to GitHub because it is a really large folder with many files and subfolders.

You also don't want to push your .env file to GitHub because it contains very sensitive information that you want to keep hidden.

Open up the .gitignore file and add the node_modules folder and .env file.

I am also going to add the .DS_Store to the .gitignore file. This .DS_Store file was created when we created the email-bot folder.

This is what your .gitignore file should look like.

.env
node_modules/
**/.DS_Store
Enter fullscreen mode Exit fullscreen mode

Adding the environment variables to the .env file

The first variable we are going to add is for the port number. The number we will use is 3000.

When naming environment variables, it is common practice to use all caps and underscores to separate words.

PORT = 3000
Enter fullscreen mode Exit fullscreen mode

The next variable will be for our personal email address.

PERSONAL_EMAIL = your personal email address goes here
Enter fullscreen mode Exit fullscreen mode

Then we will add the password for our personal email address.

EMAIL_PASSWORD = your personal email password goes here
Enter fullscreen mode Exit fullscreen mode

The last variable will be the email address we want to send messages to.

FRIEND_EMAIL = friend's email address will go here
Enter fullscreen mode Exit fullscreen mode

This is what your .env file should look like.

PORT = 3000
PERSONAL_EMAIL = your personal email address goes here
EMAIL_PASSWORD = your personal email password goes here
FRIEND_EMAIL = friend's email address will go here
Enter fullscreen mode Exit fullscreen mode

Adding the dotenv module to the server.js

If you want to load modules into your file, then you will need to use the require() function.

This is the code to load the dotenv module into the server.js file and configure it.

require('dotenv').config();
Enter fullscreen mode Exit fullscreen mode

Loading the environment variables into the server.js

We now need to load all of the variables from the .env file into the server.js file.

To load the variables, we have to use process.env followed by the name of the variable.

This is what the code looks like for the PORT variable.

process.env.PORT
Enter fullscreen mode Exit fullscreen mode

This is what the code will look like when we add all of the environment variables to the server.js file

require('dotenv').config();
const port = process.env.PORT || 3000;
const personalEmail = process.env.PERSONAL_EMAIL;
const emailPassword = process.env.EMAIL_PASSWORD;
const friendEmail = process.env.FRIEND_EMAIL;
Enter fullscreen mode Exit fullscreen mode

For the port variable, it is common to add a logical OR (||) operator followed by the number for the port.

The reason why we do this is because if our process.env.PORT doesn't work, then we tell the computer to use 3000.

Creating an Express server

We first have to add express to our server.js file.

const express = require('express');
Enter fullscreen mode Exit fullscreen mode

Then we create a new express application.

const app = express();
Enter fullscreen mode Exit fullscreen mode

Then we use the listen() method which listens for connections on a given port.

The listen() method will take in a port number and a callback function.

Our callback function will return a console.log which displays the message "The server has started at http://localhost:3000".

app.listen(port, () => {
    console.log(`The server has started at http://localhost:${port}`)
});
Enter fullscreen mode Exit fullscreen mode

Starting the server

Before we start the server, we will be adding one more package called nodemon.

The nodemon package detects changes made to the file and will automatically restart the server for us.

In the command line for the project folder, run npm i nodemon.

npm i nodemon
Enter fullscreen mode Exit fullscreen mode

In the package.json file under the scripts, change the "tests" to "start". Then change the "echo \"Error: no test specified\" && exit 1" to "nodemon server.js".

  "scripts": {
    "start": "nodemon server.js"
  },
Enter fullscreen mode Exit fullscreen mode

Go back to the command line, and run npm start.
You should see this result.
start server

To stop the server, press Ctrl-C on your keyboard.

Creating the email messages

For our bot, we will create two different messages. One good morning message and one Happy Birthday message.

Inside the server.js file, create a variable called morningMsg and assign the string "Good morning! Hope you have a beautiful day!"

const morningMsg = "Good morning! Hope you have a beautiful day!";
Enter fullscreen mode Exit fullscreen mode

We will then create a birthdayMsg and assign the string "Happy Birthday! You rock!!!!"

const birthdayMsg = "Happy Birthday! You rock!!!!";
Enter fullscreen mode Exit fullscreen mode

Creating the message objects

We will create two message objects which contain the information for the email sender, receiver, subject line and message.

This is what the code looks like for the good morning message.

let sendMorningMsg = {
    from: personalEmail,
    to: personalEmail,
    subject: "It's a beautiful morning",
    text: morningMsg
};
Enter fullscreen mode Exit fullscreen mode

This is what the code looks like for the birthday message.

let sendBirthdayMsg = {
    from: personalEmail,
    to: personalEmail,
    subject: "Hooray it's your Birthday",
    text: birthdayMsg
};
Enter fullscreen mode Exit fullscreen mode

For now, the messages will be sent to our personal email address since we are still just testing everything.

When we are finished, we can change the to field to have the friend's email address.

Creating the transporter

The transporter in nodemailer is responsible for sending our messages from our email account.

Please note:
If you are using Gmail, there are extra steps required for setup because of the authentication and security with Google.

To setup your Gmail account with Nodemailer, please read through this detailed tutorial.

If you are not using Gmail, then follow along with these steps.

The first step is to add Nodemailer to the server.js file.
You can add it with the rest of the imports at the top of the page.

const nodeMailer = require('nodemailer');
Enter fullscreen mode Exit fullscreen mode

We will then use the createTransport() method to add all of our information for the email provider.

let transporter = nodeMailer.createTransport({
    service: 'outlook',
    port: 587,
    secure: false,
    auth: {
        user: personalEmail,
        pass: emailPassword
    }
});
Enter fullscreen mode Exit fullscreen mode

Verify the email address

We can add a condition to test if there was an error connecting to our email address.

I am going to use a ternary operator to check for an error.

transporter.verify((error) => {
    error ? console.log(`There was an error for the email connection: ${error}`) : console.log('Ready to send email')
});
Enter fullscreen mode Exit fullscreen mode

Testing the email function

Let's test out sending an email using the good morning message.

We will first create an async function called morningMessage. Inside that function we will use the sendMail function to send the good morning message.

We are also going to log a success message along with the messageId inside the function.

We will then call the morningMessage function and add a catch for errors.

This is what the complete code looks like.

async function morningMessage() {
    let info = await transporter.sendMail(sendMorningMsg)
    console.log(`Message send: ${info.messageId}`)
}
morningMessage().catch(console.error);
Enter fullscreen mode Exit fullscreen mode

Now, let's go to the command line and start our server using npm start.

Log into your email provider, and you should see the message in the inbox.

email message

If you see this message in the console, There was an error for the email connection, then that means you need to check the transporter object or the values for your email address and password.

If everything checks out, you can stop the server.

Adding node-cron to send emails in the morning

We first have to add the node-cron module to our server.js file. You can add it to the rest of the modules at the top of the page.

const nodeCron = require('node-cron');
Enter fullscreen mode Exit fullscreen mode

Go back to our code for the morningMessage function and place that inside a node-cron schedule function.

The schedule() function takes in a string representation for the scheduled times and a call back function.

nodeCron.schedule("* * * * *", () => {
    async function morningMessage() {
        let info = await transporter.sendMail(sendMorningMsg)
        console.log(`Message send: ${info.messageId}`)
    }
    morningMessage().catch(console.error);
});
Enter fullscreen mode Exit fullscreen mode

To better understand this syntax, "* * * * *" let's take a look at this diagram.

 # ┌────────────── second (optional)
 #  ┌──────────── minute
 #   ┌────────── hour
 #    ┌──────── day of month
 #     ┌────── month
 #      ┌──── day of week
 #      
 #      
 # * * * * * *
Enter fullscreen mode Exit fullscreen mode

There are a total of six * you can use.
The first one is optional and represents seconds.

For example, if you wanted your message to be sent every minute, then you would use * * * * *.

Go ahead and try to test that and see if your message is sent every minute. Run npm start in the command line, and you should see your message show up in your inbox.

Then stop the server.

If you want your message to be sent every two minutes then you would use */2 * * * *.

For the morning message, we want to schedule a message to be sent every morning at 9am.

This is the syntax for the scheduled time "0 9 * * *".

The node-cron module uses military time. We are using the number 9 to represent 9am.

Here is the complete code.

nodeCron.schedule("0 9 * * *", () => {
    async function morningMessage() {
        let info = await transporter.sendMail(sendMorningMsg)
        console.log(`Message send: ${info.messageId}`)
    }
    morningMessage().catch(console.error);
});
Enter fullscreen mode Exit fullscreen mode

Using node-cron to send birthday messages

Underneath the schedule for the morningMessage, create a new schedule for the birthday message.

nodeCron.schedule("* * * * *", () => {
    async function birthdayMessage() {
        let info = await transporter.sendMail(sendBirthdayMsg)
        console.log(`Message send: ${info.messageId}`)
    }
    birthdayMessage().catch(console.error);
});
Enter fullscreen mode Exit fullscreen mode

For the schedule, we want to see a message once a year on a friends birthday at 11am.

For example, this is the syntax if your friends birthday is April 17th.

"0 11 17 April *"
Enter fullscreen mode Exit fullscreen mode

That will send once a year on their birthday.

This is what the full birthday message looks like.

nodeCron.schedule("0 11 17 April *", () => {
    async function birthdayMessage() {
        let info = await transporter.sendMail(sendBirthdayMsg)
        console.log(`Message send: ${info.messageId}`)
    }
    birthdayMessage().catch(console.error);
});
Enter fullscreen mode Exit fullscreen mode

Changing the recipient field in the message objects

When you are finished testing your bot, remember to change the to field to include your friend's email instead of your own.

to: friendEmail,
Enter fullscreen mode Exit fullscreen mode

To test it out, make sure to start your local server a few minutes before the scheduled morning time.

Then check in with your friend to see if they received it.
You can also add a cc field to the message object, so you get a copy of the email as well.

cc: personalEmail,
Enter fullscreen mode Exit fullscreen mode

Final code

This is the complete code for our server.js file.

//imports for the env variables and packages
require('dotenv').config();
const port = process.env.PORT || 3000;
const personalEmail = process.env.PERSONAL_EMAIL;
const emailPassword = process.env.EMAIL_PASSWORD;
const friendEmail = process.env.FRIEND_EMAIL;
const express = require('express');
const app = express();
const nodeMailer = require('nodemailer');
const nodeCron = require('node-cron');

//messages for bot
const morningMsg = "Good morning! Hope you have a beautiful day!";
const birthdayMsg = "Happy Birthday! You rock!!!!";

//message objects
let sendMorningMsg = {
    from: personalEmail,
    to: friendEmail,
    cc: personalEmail,
    subject: "It's a beautiful morning",
    text: morningMsg
};

let sendBirthdayMsg = {
    from: personalEmail,
    to: friendEmail,
    cc: personalEmail,
    subject: "Hooray it's your Birthday",
    text: birthdayMsg
};

//transporter to send emails from our account 
let transporter = nodeMailer.createTransport({
    service: 'outlook',
    port: 587,
    secure: false,
    auth: {
        user: personalEmail,
        pass: emailPassword
    }
});


//verifies a proper email connection 
transporter.verify((error) => {
    error ? console.log(`There was an error for the email connection: ${error}`) : console.log('Ready to send email')
});


//sends a morning message to our friend at 9am everyday
nodeCron.schedule("0 9 * * *", () => {
    async function morningMessage() {
        let info = await transporter.sendMail(sendMorningMsg)
        console.log(`Message send: ${info.messageId}`)
    }
    morningMessage().catch(console.error);
});

// sends a message once a year to our friend on their birthday 
nodeCron.schedule("0 11 17 April *", () => {
    async function birthdayMessage() {
        let info = await transporter.sendMail(sendBirthdayMsg)
        console.log(`Message send: ${info.messageId}`)
    }
    birthdayMessage().catch(console.error);
});

//listens for connections 
app.listen(port, () => {
    console.log(`The server has started at http://localhost:${port}`)
});

Enter fullscreen mode Exit fullscreen mode

Thank you so much for making it to the end of the tutorial. 😄

To learn more about the features for node-cron, please visit the documentation.

To learn more about the features for Nodemailer, please visit the documentation

Nodemailer Project GitHub Repo

Happy coding!

Top comments (4)

Collapse
 
ayuechuan profile image
ayuechuan

A good article, can the complete code be uploaded to github? For self-learning

Collapse
 
codergirl1991 profile image
Jessica Wilkins

I just created the github repo.
github.com/jdwilkin4/Nodemailer-bo...

Thanks for reading!

Collapse
 
lukeecart profile image
Luke Cartwright

This is brilliant! I've been working on something similar with the same packages. Have you thought about hosting it somewhere?

Collapse
 
codergirl1991 profile image
Jessica Wilkins

I am glad you like the article.
I was thinking about doing a follow up article on how to style the emails and host it through Heroku.