Using CasperJS on Webfaction to automatically send emails with attached images via Gmail
Why?
Recently I had to make a script which runs once per week and gathers images of an admin interface (login required) and sends it to its manager by email for quick overview. This is great because it saves the manager’s time as he doesn’t have to login to the website and click through all of the graphs and wait for them to load, instead he gets a weekly (YMMV) summary. The site was hosted on Webfaction, which, as it turns out, is a cool web host (haven’t come across it before) because it allows you to compile source code and run custom binaries.
How?
The task of automatically logging in the admin dashboard and taking screenshots proved to be an excellent job for CasperJS, an open source navigation scripting & testing utility written in Javascript for the PhantomJS WebKit headless browser.
Installation
In order to run CasperJS scripts, you need to have NodeJS installed. Installing NodeJS on Webfaction is as straightforward as it can get, as it’s done via their control panel in the Applications tab, as shown on the illustration below:
It’s useful to put NodeJS executable in your path because then you can run node from any folder. You can do this by going to your NodeJS application’s bin directory and execute the following command:
export PATH=$PATH:$PWD/bin/
Installing CasperJS is easy via npm (Node package manager):
npm install -g casperjs
However, in order for CasperJS to work, you need to install PhantomJS and you can do it by cloning it from its GitHub repository and running the build.sh script:
mkdir -p $HOME/src cd $HOME/src git clone git://github.com/ariya/phantomjs.git && cd phantomjs git checkout 1.5 ./build.sh cp $HOME/src/phantomjs/bin/phantomjs $HOME/bin
To test if PhantomJS has been successfully installed you can run:
phantomjs --version
To have the binaries in my path every time I log in I added this alias to my .bashrc:
alias nodeset="export PATH=$PATH:/home/webapp/node/bin:/home/webapp/bin"
and my .bash_profile looks like this:
# User specific environment and startup programs PATH=$PATH:$HOME/bin export PATH nodeset
CasperJS script for automatically saving images
var casper = require("casper").create({ verbose: false, logLevel: 'debug' }); var url = "http://your-app.com/", saveDir = "/home/app/savedImages"; casper.start(url, function() { this.fill('form#fm_login', { 'username': 'username', 'password': 'password', }); this.echo("I filled the form"); }); casper.then(function(){ this.click('#btn_login'); this.echo("I'm, logging in"); }); //first graph fetch casper.thenOpen("http://your-app.com/graph_1", function(){ this.wait(3000, function() { this.echo("I've waited for 3 seconds for the graphs to load."); casper.then(function(){ var fileName = saveDir + '/graph_1.png'; this.viewport(1920, 1080); this.captureSelector(fileName, '#page_content'); this.echo('first snapshot taken'); }); }); }); casper.run(function() { this.echo('done').exit(); });
First I initialize casper and set few variables (url, saveDir – note that it has to be full path). Then I start casper by visiting the passed in url and fill the login form by using caspers fill() function. After that I click on the login button and woila – I’m logged in. After that I open up the link with the graph and wait 3 seconds for the graph to load, and then I save the selected area by using the captureSelector() function and a CSS selector of the area I want to save.
NodeJS script for automatically sending email with attachment via Gmail SMTP
This node script uses nodemailer which can be installed using npm:
node install -g nodemailer
This script uses Gmail SMTP (if you want to test it make sure to enter correct Gmail account credentials), but nodemailer offers many more options for sending the email. The simple email sending script is here:
var nodemailer = require("nodemailer"); var rootFolder = "/home/app/savedImages/"; // create reusable transport method (opens pool of SMTP connections) var smtpTransport = nodemailer.createTransport("SMTP",{ service: "Gmail", auth: { user: "[email protected]", pass: "your.gmail.pass" } }); // setup e-mail data with unicode symbols var mailOptions = { from: "Automatic Graph Sender <[email protected]>", // sender address to: "[email protected]", // list of receivers subject: "Automatic Graph Sender", // Subject line text: "Hello, I'm Roboty, an automatic report sending robot. You will find your files in attachment. I wish you a great day! Bye, Roboty", // plaintext body html: "Hello,<br/><br/>I'm Roboty, an automatic report sending robot. You will find your files in attachment.<br/><br/>I wish you a great day!<br/><br/>Bye,<br/>Roboty", attachments: [ { filePath: rootFolder + "graph_1.png"}, { filePath: rootFolder + "graph_2.png"}, //add as many as you wish ] } // send mail with defined transport object smtpTransport.sendMail(mailOptions, function(error, response){ if(error){ console.log(error); }else{ console.log("Message sent: " + response.message); } // if you don't want to use this transport object anymore, uncomment following line smtpTransport.close(); // shut down the connection pool, no more messages });
Cron jobs
The following script will run every day at exactly 19:50h. I added the PHANTOMJS_EXECUTABLE needed by CasperJS executable, then I wrote the path to CasperJS and the script that we want to execute and finally I redirected all the output to the temporary file for later inspection if needed.
50 19 * * * PHANTOMJS_EXECUTABLE=/home/app/bin/phantomjs /home/app/node/bin/casperjs /home/app/casper/saveImages.js > /tmp/outputCheck.txt
Mailing script runs 5 minutes after the image gathering script and also outputs everything to the temporary file used in some other success status checking script.
55 19 * * * /home/emtool/webapps/node/bin/node /home/emtool/my_folder/casper/mail.node.js > /tmp/emailOut.txt
Hope this proved useful to someone, and hit me with comments or questions you may have.
great tut i love it can you make another one for posting gmail comments based on replies from cirtain youtube titles? that would be fun lol
Hi In the following code when i try to execute the nodejs script i am receiving the following error.
[Error: Unsupported configuration, downgrade Nodemailer to v0.7.1 to use it]
Please give me a solution to overcome this..
And what have you tried so far?
I tried install the older version of nodemailer but i could not do that..and apart from that i tried this following code also which was given as a solution for this issue but this to never works…:(
var express = require(‘express’);
var nodemailer = require(“nodemailer”);
var smtpTransport = require(“nodemailer-smtp-transport”)
var app = express();
var smtpTransport = nodemailer.createTransport(smtpTransport({
host : “YOUR SMTP SERVER ADDRESS”,
secureConnection : false,
port: 587,
auth : {
user : “YourEmail”,
pass : “YourEmailPassword”
}
}));
app.get(‘/send’,function(req,res){
var mailOptions={
from : “YourEmail”,
to : “Recipient’sEmail”,
subject : “Your Subject”,
text : “Your Text”,
html : “HTML GENERATED”,
attachments : [
{ // file on disk as an attachment
filename: ‘text3.txt’,
path: ‘Your File path’ // stream this file
}
]
}
console.log(mailOptions);
smtpTransport.sendMail(mailOptions, function(error, response){
if(error){
console.log(error);
res.end(“error”);
}else{
console.log(response.response.toString());
console.log(“Message sent: ” + response.message);
res.end(“sent”);
}
});
});
app.listen(3000,function(){
console.log(“Express Started on Port 3000”);
});
But this code uses express app to send emails..
My scenario is that after taking screen automatic email trigger to my client mail address not to gmail service..
Do we have to use express also????
Why did you use this code? At the end of my tutorial I shared the simple script for sending emails, did you look at that? To answer your question – no you don’t need to use express. They used it in this script which you’ve pulled from I don’t know where…
I have downgraded my nodemailer to version 0.7 using the following command and its successful then i runned your mail script and received the following message
C:\node_modules\nodemailer>node mail.js
{ [Error: connect ECONNREFUSED 74.125.68.109:465]
code: ‘ECONNREFUSED’,
errno: ‘ECONNREFUSED’,
syscall: ‘connect’,
address: ‘74.125.68.109’,
port: 465,
stage: ‘init’ }
I think my company firewall blocks the smtp i have raised the access for firewall block ..once its granted i l run the script again and let you knew thanks:)
Of course, you have to change the credentials for your Gmail account, you figured that out from the tutorial, right?
Yes
auth: {
user: “XXXXXXXXXgmail.com”,
pass: “XXXXXXXXX”
}
Is this the one na??
And however apart from gmail how do i try to send email to other service ..i have to send email to my client mail some thing like walmart.com or chub.com etc…
You’re using Gmail to send the email through it to whatever client you want/need.
Thus here:
auth: {
user: “XXXXXXXXXgmail.com”,
pass: “XXXXXXXXX”
}
your user and pass have to be from your own Gmail account.
And you should define the email of the person you’re sending to in the:
var mailOptions = {
from: "Automatic Graph Sender
to: "[email protected]",
subject: "Hi Email",
text: "Hi",
html: "Hi"
}
I hope this now clears things up for you.
No am asking about instead of gmail i have to send mails from my client mailbox
consider the example.No gmail is required for me.
FROM:[email protected]
TO:[email protected]
var mailOptions = {
from: “[email protected]”,
to: “[email protected]”,
subject: “Hi Email”,
text: “Hi”,
html: “Hi”
}
How can we access other mail clients apart from gmail…specially client mail service..
I would suggest you go over the SMTP examples here: https://github.com/nodemailer/nodemailer
Yes I got it am able to trigger mails…Here’s the code using transporter object without using smtp service.
var nodemailer = require(‘nodemailer’);
var transporter = nodemailer.createTransport();
var rootFolder = “C:/phantomjs/casperjs/bin/”;
console.log(rootFolder);
transporter.sendMail({
from: ‘[email protected]’,
to:’[email protected]’,
subject: ‘WTD Report’,
html: ‘Hi Pfa WTD Report ‘,
attachments: [
{ filePath: rootFolder + “flus.png”},
]
});
transporter.close();
Thanx a ton Nikole you are genius!!!
I’m glad you figured it out in the end and thanks for posting what worked for you. Good luck going further!