This article first appeared on my personal Blog: Vince’Blog

Project source code: NodeMail, welcome star, perhaps one day out of order can be used

Writing in the front

Since using mail registered a lot of account will receive similar mail, such as the following at first thought it was a picture, a look carefully later not pictures ah, like HTML, so curious baby I Google it, access to more than data after summarizes how to use the front knowledge and do a Node “web mail”.

Confirm the topic

After knowing how to implement the function, THINKING what should I write the subject, using an HTML template to send an email to friends casually show off? No, as a programmer cool how can so low, big, recently the weather changes in temperature is uncertain, his girlfriend is always complaining about much wear less cold and hot, hiyah, or I’ll write a timing for baby to the weather forecast email every day, think of baby, like to see ONE another daily updates on this APP, How about giving a weather forecast with a complimentary “ONE daily subscription”? Clever and romantic, begin to move bricks ~

According to the plot

Originally wanted to finally put the effect of the picture, afraid you see half of the interest, just in front of the plot I finally made the effect of the picture ~

Problems to be solved

1. How to obtain weather forecast and data on ONE?

A: There are two ways to get the data. The first way is to get the weather forecast and ONE API. The second way is to use the Node crawler to get the weather forecast and ONE web page information. Later, I found that ONE does not have AN API interface. In order to unify the two, I decided to use a plug-in on Node called Cheerio, which can easily climb the information on the web page together with superagent.

2. How to make HTML such mail?

A: I have learned the Express framework for some time before, and I am exposed to the concept of template engine. If you pass in data, you can get an HTML file, and then combine with node fs module, you can get the HTML file, and then use node’s email plug-in to send HTML emails.

3. How to send emails using Node?

Nodemailer is a Node plugin that is compatible with Email vendors. It allows you to send files to Node scripts using your Email account and SMTP authorization code

4. How to send at the same time every day?

There are various hacks that can be used to write such a scheduled task, but since the Node community has a timing wheel, we can use it directly. Nod-schedule is a generator that can be configured to perform tasks at specific times of the month, week, and day. This is the perfect time to send emails to your baby every morning.

Everything is ready to do a romantic programmer

Write the code

Web crawler

Here we use a combination of Superagent and Cheerio to implement the crawler:

  • Analyze the structure of the web DOM, as shown in the figure below:

  • Use superagent to get all the DOM of the specified page:
superagent.get(URL).end(function(err,res){
    //
}
Copy the code
  • Cheerio is used to filter the DOM obtained by the superagent and extract the desired DOM
imgUrl:$(todayOne).find('.fp-one-imagen').attr('src'),
type:$(todayOne).find('.fp-one-imagen-footer').text().replace(/(^\s*)|(\s*$)/g.""),
text:$(todayOne).find('.fp-one-cita').text().replace(/(^\s*)|(\s*$)/g."")
Copy the code

The following is the code to crawl ONE, the weather forecast page is also the same reason:

const superagent = require('superagent'); // Send a network request to get the DOM
const cheerio = require('cheerio'); // Get the DOM node as easily as Jquery

const OneUrl = "http://wufazhuce.com/"; // The Web version of ONE

superagent.get(OneUrl).end(function(err,res){
    if(err){
       console.log(err);
    }
    let $ = cheerio.load(res.text);
    let selectItem=$('#carousel-one .carousel-inner .item');
    let todayOne=selectItem[0]; // Get the first page of the round graph, which is the updated content of the day
    let todayOneData={  // Save to a JSON file
        imgUrl:$(todayOne).find('.fp-one-imagen').attr('src'),
        type:$(todayOne).find('.fp-one-imagen-footer').text().replace(/(^\s*)|(\s*$)/g.""),
        text:$(todayOne).find('.fp-one-cita').text().replace(/(^\s*)|(\s*$)/g."")};console.log(todayOneData);
})
Copy the code

The EJS template engine generates HTML

We get the data from the crawler, so we can render the HTML by input date to EJS, we create js script and EJS template file in the directory:

  • app.js
const ejs = require('ejs'); // Ejs template engine
const fs  = require('fs'); // Read and write files
const path = require('path'); // Path configuration

// The data passed to EJS
let data={
    title:'nice to meet you~'
}

// Get mail.ejs from directory to get a template
const template = ejs.compile(fs.readFileSync(path.resolve(__dirname, 'mail.ejs'), 'utf8'));
// Pass the data into the template to generate HTML
const html = template(data);

console.log(html)

Copy the code
  • mail.ejs
<! DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, Word-wrap: break-word! Important; "> <meta http-equiv=" x-uA-compatible "content="ie=edge"> <title>Document</title> </head> <h1> <%= title %> </h1> </body> </html>Copy the code

Send email using Node

Here we can send text or HTML. Note that the email password is not the password you use to log in to the email, but the SMTP authorization code. What is SMTP authorization code? Your email account can use this SMTP authorization code to send emails in other places. Generally, THE SMTP authorization code can be seen in the Settings of the email official website. Set the following comments.

const nodemailer = require('nodemailer'); // The node plug-in for sending emails

let transporter = nodemailer.createTransport({
    service: '126'./ / the sender email, support list: https://nodemailer.com/smtp/well-known/
    port: 465./ / SMTP port
    secureConnection: true.// SSL secure link
    auth: {   // The sender's password
      user: 'account @ 126. com'./ / account
      pass: 'SMTP Authorization code'.// Obtain the SMTP authorization code from the email Settings}});let mailOptions = {
    from: '" Nickname of sender "< address @126.com>'.// The sender's nickname and address
    to: '[email protected]'.// The recipient's email address
    subject: 'A little warm mail'.// Email subject
    text: 'test mail'.// The text of the message
    // HTML: HTML // can also be sent in HTML
  };
  
// Send mail
transporter.sendMail(mailOptions, (error, info) => {  
    if (error) {
    return console.log(error);
    }
    console.log('Email sent successfully ID:', info.messageId);
});  
Copy the code

Node executes tasks periodically

Here we use node-schedule to schedule tasks, as shown in the following example:

var schedule = require("node-schedule");  

//1. Execution at the specified time
var date = new Date(2017.12.10.15.50.0);  
schedule.scheduleJob(date, function(){  
   console.log("Carry out the mission");
});

//2. Execute in seconds
// For example: execute every 5 seconds
var rule1     = new schedule.RecurrenceRule();  
var times1    = [1.6.11.16.21.26.31.36.41.46.51.56];  
rule1.second  = times1;  
schedule.scheduleJob(rule1, function(){
    console.log("Carry out the mission");    
});

//3. Execute by unit
// For example: execute every 5 minutes
var rule2     = new schedule.RecurrenceRule();  
var times2    = [1.6.11.16.21.26.31.36.41.46.51.56];  
rule2.minute  = times2;  
schedule.scheduleJob(rule2, function(){  
    console.log("Carry out the mission");    
});  

//4. Execute in days
// For example, it is executed at 6:30 every day
var rule = new schedule.RecurrenceRule();
rule.dayOfWeek = [0.new schedule.Range(1.6)];
rule.hour = 6;
rule.minute =30;
var j = schedule.scheduleJob(rule, function(){ &emsp; &emsp; &emsp; &emsp;console.log("Carry out the mission");
        getData();
});
Copy the code

Ideas and Steps

When all the problems are solved, it is to combine the code into a complete program, the idea is very simple, let’s step by step analysis:

  1. Since data acquisition is asynchronous and it is not possible to determine which data is obtained first, we can encapsulate the data acquisition functions into a Promise object, and finally use promise. all together to determine the completion of all data acquisition before sending the mail
// One of the data fetch functions, and the others are similar
function getOneData(){
    let p = new Promise(function(resolve,reject){
        superagent.get(OneUrl).end(function(err, res) {
            if (err) {
                reject(err);
            }
            let $ = cheerio.load(res.text);
            let selectItem = $("#carousel-one .carousel-inner .item");
            let todayOne = selectItem[0];
            let todayOneData = {
              imgUrl: $(todayOne)
                .find(".fp-one-imagen")
                .attr("src"),
              type: $(todayOne)
                .find(".fp-one-imagen-footer")
                .text()
                .replace(/(^\s*)|(\s*$)/g.""),
              text: $(todayOne)
                .find(".fp-one-cita")
                .text()
                .replace(/(^\s*)|(\s*$)/g."")}; resolve(todayOneData) }); })return p
}

Copy the code
  1. The crawl data is processed uniformly as EJS parameters and the mail template is sent.

function getAllDataAndSendMail(){
    let HtmlData = {};
    // how long with
    let today = new Date(a);let initDay = new Date(startDay);
    let lastDay = Math.floor((today - initDay) / 1000 / 60 / 60 / 24);
    let todaystr =
      today.getFullYear() +
      "/" +
      (today.getMonth() + 1) +
      "/" +
      today.getDate();
    HtmlData["lastDay"] = lastDay;
    HtmlData["todaystr"] = todaystr;

    Promise.all([getOneData(),getWeatherTips(),getWeatherData()]).then(
        function(data){
            HtmlData["todayOneData"] = data[0];
            HtmlData["weatherTip"] = data[1];
            HtmlData["threeDaysData"] = data[2];
            sendMail(HtmlData)
        }
    ).catch(function(err){
        getAllDataAndSendMail() // Get it again
        console.log('Failed to get data:',err); })}Copy the code
  1. Specifies the code for sending an email

function sendMail(HtmlData) {
    const template = ejs.compile(
      fs.readFileSync(path.resolve(__dirname, "email.ejs"), "utf8"));const html = template(HtmlData);
  
    let transporter = nodemailer.createTransport({
      service: EmianService,
      port: 465.secureConnection: true.auth: EamilAuth
    });
  
    let mailOptions = {
      from: EmailFrom,
      to: EmailTo,
      subject: EmailSubject,
      html: html
    };
    transporter.sendMail(mailOptions, (error, info={}) => {
      if (error) {
        console.log(error);
        sendMail(HtmlData); // Send again
      }
      console.log("Message sent: %s", info.messageId);
    });
  }
Copy the code

Installation and use

If you think the content of this email is suitable for the person you are sending it to, you can follow these steps and change a few parameters to run the program.

  1. git clone https://github.com/Vincedream/NodeMail
  2. Open main.js and modify the configuration items
/ / day
let startDay = "2016/6/24";

// The local pinyin needs to be confirmed in the inky weather URL below
const local = "zhejiang/hangzhou";

// Sender email manufacturer
let EmianService = "163";
// SMTP authorization code of the sender's email account
let EamilAuth = {
  user: "[email protected]".pass: "xxxxxx"
};
// The nickname and email address of the sender
let EmailFrom = '"name" <[email protected]>';

// By the recipient's mailbox
let EmailTo = "[email protected]";
// Email subject
let EmailSubject = "A warm little email.";

// The daily sending time
let EmailHour = 6;
let EmialMinminute= 30;
Copy the code
  1. The input terminalnpm installInstall dependencies and enter againnode main.jsOf course, your computer will not sleep, it is recommended that you deploy to your cloud server to run.

The last

The source code and demo have been put on Github, do you want to give it a try?

GitHub:github.com/Vincedream/…