Saturday, January 19, 2019

Why I didn’t hire an event manager for my wedding



After reading this article, you may find the title of this article somewhat misleading. This is not about how I planned my wedding. In this article I’ll explain how I created a very basic RSVP system to remind the guests of the wedding date and inform their table numbers. Not only that, I’ll cover how I distributed the wedding day tasks within a group of family members and how I reminded them by sending mobile notifications at the time on which the task required to be performed.

RSVP website and SMS

In this section I’ll explain how I created RSVP website along with the SMS notification (guests) and email notification (host) system. First I’ll briefly explain the flow of the design. I assigned each guest a unique id. You can easily generate bulk UUID using this website. After generating sufficient UUIDs for the number of guests, you will notice that only the first 8 characters of the IDs were unique. Therefore you can remove the remaining digits and use only the first 8 characters.


There are three main parts of the application.

  • RSVP website
  • Python script for sending personalised SMS
  • Webtask serverless

RSVP website is a simple angular 5 application and you can find the code from this github repository.

Python script will read a csv file in a particular format and send SMS to the guest using AWS SNS. Keep in mind that AWS SNS does not allow you to send SMS more than a certain budget. So make sure you make a budget increase request to the AWS support before planning to send SMS. Furthermore, I’m using google url shortener API. In order to use that, you may need to acquire an access token first.


Please make sure that your list.csv is in the following format.

name, mobile number, table number, unique id,


Ok, now the most important thing — backend. I decided to use Auth0’s webtask instead of going for AWS lambda because I found it so easy to use and the embedded json datastore was ideal for my use case.

for the backend I’m using 3 serverless functions.

  • wedding.js

this is the main function which returns the guest details when the unique id is provided through the frontend. Webtask has its own embedded json data storage, so I used that in order to store the guest details.

json data format should look like follows,

{
  "f3dca298": {
    "name": "Mr & Mrs John Doe",
    "table": "19"
  },
  "ceabcc5e": {
    "name": "Mr Matt Peiris",
    "table": "26"
  },
  ...  

  • wedding-reply.js

This function is used to get the response from the guest and send it back to the organisers. Make sure you obtain an access key from SendGrid in order to send emails using their API. Furthermore, you can store the access key inside webtask secrets storage.


  • wedding-store.js

This is an optional function which I created to track the number of visits from a certain guest. This will write data to the embedded json data store with the number of times the user visited our website.

Initially you should add a dummy data to the data store in the following format.

{
  "counter": 1,
...
}
I think I have covered everything for the RSVP website and SMS system. Now I’ll move on to the SMS reminder system for the organisers.

SMS reminders for the organisers


Most difficult part of planning any kind of event is assigning people with tasks and give them the ownership. When it comes to a wedding, the bride and groom can’t engage in any kind of task during the time of the wedding. Hence all the tasks should be assigned to a set of friends and family members. In this section I’ll explain how I reminded them about the tasks they were assigned with few minutes before which the task needed to be performed.


I used AWS Lambda function (python) using Serverless framework and specified a set of cloudwatch events to trigger the function with a set of parameters. Lambda function is just a skeleton and the task, mobile number, name of the assigned person, time on which the task need to be performed will be passed through the cloudwatch events.


The events part of my serverless.yml looks like follows.

functions:
  hello:
    handler: handler.hello
    events:
      - schedule:
            rate: cron(15 20 5 12 ? 2018)
            enabled: true
            input:
              name: 'Charini'
              number: '+947xxxxxxxx'
              task: '1.35 am Testing for mobitel, reply yasith if works'
- schedule:
            rate: cron(15 20 5 12 ? 2018)
            enabled: true
            input:
              name: 'Yasith'
              number: '+94xxxxxxxx'
              task: '1.35 am Test message, reply yasith if works'
- schedule:
            rate: cron(0 4 6 12 ? 2018)
            enabled: true
            input:
              name: 'Nimanthika'
              number: '+94xxxxxxxx'
              task: 'Attend to Jayamangala girls / Dancers / Ashtaka (Check whether they got drinks)'
AWS won’t allow you to create more than a certain number of cloudwatch events for a given function, in which case you may need to request them to increase the limit, or you can divide the events within a set of functions. Even the webtask supports cron expressions but I haven’t tried that yet. Maybe you can try using webtask as well.

This article does not provide the background knowledge for the technologies I used here. There are plenty of other resources you can refer if you are not familiar with those technologies.

Initially I was planning to create a SaaS platform for this use case, where the users can create events, assign users, send sms on their own. However it was on hold due to certain other priorities.

If you are an event organiser or an event host who is planning to use this kind of solution, please feel free to contact me through my email yasith1@gmail.com

I hope you enjoyed this article, and if you have any questions, please comment below. I’m more than happy to answer your queries.


Monday, October 8, 2018

Automating static content sharing on social media using Serverless, AWS Lambda, AWS Aurora & AWS CloudWatch events


In this article I’ll explain how to automate static content sharing on social media using AWS products and some 3rd party APIs. Please keep in mind that there are more easy ways to do the same thing using several SaaS products in the market. But I wanted to use the AWS stack in order to get a better hands-on experience.

What I’m doing here is really simple. But once you get to know about the possibilities you can do few adjustments to the architecture or source code to customise according to your requirements.

I’ll now briefly explain the use case. Imagine you have a facebook, twitter and several other social media accounts to which you publish static content on a daily basis. Instead of manually sharing your content on each account I’m going to explain how to do it automatically in a given time.

For the demonstration purposes, I have created a facebook page with the intention of sharing zen quotes daily. I’m storing all the zen quotes in AWS Aurora mysql database. This is not necessary, you can decide where to store depending on your requirement, but I used aurora in order to be thorough with AWS service offerings.

Then I’ll explain the technology stack I used for the implementation. I’m using Serverless framework to deploy Lambda functions on AWS. This article will not cover the core concepts of each technology I’m using. You can find sufficient articles on the internet regarding each of these technologies. Serverless framework will upload the node JS code to AWS S3 bucket and use AWS Cloudformation to create required stack on AWS.

serverless.yml

service: dailyzenquotes

provider:
  name: aws
  runtime: nodejs8.10
  region: us-east-1
  role: arn:aws:iam::649854678814:role/zen-quotes-lambda-role
  
functions:
  publish:
    handler: handler.publish
    timeout: 10
    events:
      - schedule: cron(0 3 * * ? *)

If you go through the above serverless.yml file, you will notice the cron expression I used there. It will basically create an event on AWS cloudwatch to trigger the given Lambda function daily at 3 a.m GMT.

handler.js

'use strict';

const readFromDatabase = require('./readFromDatabase');
const getImageFromQuote = require('./getImageFromQuote');
const postToBuffer = require('./postToBuffer');

module.exports.publish = async (event, context) => {
  var quote = await readFromDatabase();
  console.log('quote : ' + quote);
  var imageUrl = await getImageFromQuote(quote);
  var bufferResponse = await postToBuffer(quote,imageUrl);
  console.log('ritekit image url : ' + imageUrl);
  console.log('buffer final Response : ' + bufferResponse);
  return {
    statusCode: 200,
    body: {
      message: {
        bufferResponse: bufferResponse,
        imageUrl: imageUrl,
        quote: quote
      },
      input: event
    }
  };
};
Now I’ll briefly take you through the code. Once the cloudwatch event is triggered, it invokes the following lambda function which will eventually read a random quote from the database and pass it to RiteKit API.

readFromDatabase.js

'use strict';
const mysql = require('mysql');
const async = require('async');

var connection  = mysql.createConnection({
  host     : process.env.HOST,
  user     : process.env.USER,
  password : process.env.PASSWORD,
  database : process.env.DATABASE,
});

module.exports = function(){
  console.log('Connecting to Database');
  connection.connect();
  console.log('Executing the Query');
  return new Promise( async function( resolve, reject ){
    connection.query('SELECT quote FROM quotes ORDER BY RAND() LIMIT 1', function (error, result, fields) {
      console.log('Executing');
      if (error) {
        console.log('error occured!');
        console.log('error occured : ' + error);
        reject(new Error('Ooops, something broke!'));
      }else {
        console.log('Success!')
        console.log('The quote is: ' + JSON.stringify(result[0].quote));
        console.log('Closing Database Connection');
        connection.end();
        resolve(result[0].quote);
      }
    });
  });
}
RiteKit is a 3rd party service which you can use for free for a certain number of requests. It consumes a text and returns an image which includes that text as a quote.

getImageFromQuote.js

'use strict';

var request = require('request');

module.exports = function (message) {
    // Setting URL and headers for request
    var options = {
      url: 'https://api.ritekit.com/v1/images/quote',
      qs:
       { quote: message,
         brandLogo: 'https://thumb.ibb.co/fnB769/zen.png',
         client_id: process.env.RITEKIT_CLIENT_ID
       },
    };
    // Return new promise
    return new Promise(function(resolve, reject) {
     // Do async job
        request.get(options, function(error, response, body) {
            if (error) {
                console.log("error in rite kit request :" + error);
                reject(error);
            } else {
                console.log("rite kit response : " + body);
                var response = JSON.parse(body);
                resolve(response.url);
            }
        })
    })
}

After that the lambda function invokes the Buffer API to publish the image we retrieved from the previous step to pre configured social media accounts.

postToBuffer.js

'use strict';

var request = require('request');

module.exports = function (message, imageUrl) {
    // Setting URL and headers for request
    var options = {
        method: 'POST',
        url: 'https://api.bufferapp.com/1/updates/create.json',
        qs: {
          access_token: process.env.BUFFER_ACCESS_TOKEN },
          headers:
           {  'Content-Type': 'application/x-www-form-urlencoded' },
        form: {
          text: message,
         'profile_ids[]': process.env.BUFFER_FACEBOOK_ID,
         'media[photo]': imageUrl,
         now: 'true' }
   };
    // Return new promise
    return new Promise(function(resolve, reject) {
     // Do async job
        request(options, function(error, response, body) {
            if (error) {
                console.log("error in buffer request :" + error);
                reject(error);
            } else {
                console.log("buffer response :" + body);
                var response = JSON.parse(body);
                resolve(response.success);
            }
        })
    })
}
Same approach can be used to process and publish a feed of content to different social media accounts by doing few modifications according to the requirement. Even though there are many other easy ways of doing the same thing, the whole purpose of using this approach is to get more familiar with AWS service offerings.

Complete source code can be found in this github repository. Feel free to try it and I’m more than happy to answer your questions if you find any difficulties.

Wednesday, June 6, 2018

Invoking webdav methods using angular 4 httpclient


What is webdav?


According to wikipedia
"Web Distributed Authoring and Versioning (WebDAV) is an extension of the Hypertext Transfer Protocol (HTTP) that allows clients to perform remote Web content authoring operations. WebDAV is defined in RFC 4918 by a working group of the Internet Engineering Task Force.
The WebDAV protocol provides a framework for users to create, change and move documents on a server. The most important features of the WebDAV protocol include the maintenance of properties about an author or modification date, namespace management, collections, and overwrite protection"

Webdav Methods

Webdav extends HTTP and apart from the usual HTTP methods, there are some additional webdav specific methods as listed below.

  • PROPFIND

In WebDAV, documents can have properties, and clients can use this method to retrieve those properties.

  • PROPPATCH

Clients can use this method to set, add, or remove properties of resources.

  • MKCOL

WebDAV lets you group documents into collections, and clients can use this method to create a new collection.

  • COPY

Clients can use this method to create a duplicate of a given resource at a destination URI.

  • MOVE

This is similar to COPY, but the server is expected to delete the source resource as part of this operation.

  • LOCK

This method lets clients lock a given document. This method enables pessimistic concurrency control.

  • UNLOCK

Clients can use this method to unlock a previously locked resource.


Angular 4+ HttpClient with webdav

New angular HttpClient library supports custom methods (like 'MKCOL', 'PROPFIND', etc.) Let's see how we can invoke custom http methods using angular httpclient library.


/**
* Get properties of a certain directory or file
* @param {string} directoryName
* @returns {Observable<any>}
*/
public getProperties(directoryName: string): Observable<any> {
    const headers: HttpHeaders = new HttpHeaders({
      'Authorization': 'Basic xxxxxxxxxxxx', // if your server supports authentication
      'Depth': '100', // depth of the directory tree to be fetched 1 = fetch only the files inside root directory
    });
    return this.http.request('PROPFIND', this.WEBDAV_BASE + '/' + directoryName , { headers: headers , responseType: 'text'})
      .map(res => {
        // your code goes here, play around with 'res'
      })
      .catch(error => error);
  }

/**
* Create collection
* @param {string} directoryName
* @returns {Observable<any>}
*/
public createDirectory(directoryName: string): Observable<any> {
    const headers: HttpHeaders = new HttpHeaders({ 'Authorization': 'Basic xxxxxxxxxxxx' });
    return this.http.request('MKCOL', this.WEBDAV_BASE + '/' + directoryName, { headers: headers , responseType: 'text'})
      .map(res => res)
      .catch(error => error);
  }

This is just a very basic introduction to webdav and how to invoke custom http methods using new angular httpclient library. Feel free to comment if you have any questions.

Update

If you are facing the CORS issues you can implement a custom filter like this


Sunday, October 8, 2017

From Entrepreneur to Intrapreneur


How business leaders foster entrepreneurship inside the organization

While some well established market leaders are trying to stick to their core cultures, predefined business strategies, well-established policies and procedures there are few organizations trying to foster entrepreneurship inside the organization which is known as the intrapreneurship. Knowing how to differentiate those two terms would minimize the distrust, fear and skepticism while keeping them in practice.

Having experience in working for global leaders in the industry and high potential emerging startups in the region, made me realize that intrapreneurship is highly valued by the latter parties. After several startup attempts, with that inner entrepreneurial spirit in mind I found out that intrapreneurship is beneficial for both parties, organization as well as the intrapreneur himself.

Taking ownership plays a vital role in both entrepreneur and intrapreneur landscapes. After going through certain unsuccessful seed funding rounds for my own IoT startup, I decided to pivot my entrepreneurial journey. Now as an intrapreneur in an emerging startup in the region I'm taking ownership of a product which will revolutionize the way service providers interact with the mobile operators. And I believe that will help the organization in achieving their ultimate vision of empowering the mobile operators.   

I believe that it's vital for any organizational leader to understand the following factors which differentiate the aforementioned terms early in their innovation strategy and this could substantially increase the chances of success.

Independence (autonomy)

Entrepreneurs have the complete autonomy of his product since the inception to certain extent in its life cycle. Intrapreneurs are not as autonomous as entrepreneurs, although they believe they are. But again it solely depends on the organization culture and the leaders. Because some organizations show lack of interest towards introducing changes to their well-established policies and procedures.

Risk

Entrepreneurs sign up for a spectrum of consequences, varying from huge wins to utter losses, whereas some intrapreneurs get the paycheck from the organization and have no intention of higher financial gains, largely to avoid risk.

Resources

Entrepreneurs must find all the necessary resources and it's so challenging to work in a resource constrained environment which will ultimately tests his skills in managing with limited resources. Intrapreneurs, however, have access to the core organizational resources, but again they should also be skillful in handling and negotiating for other scarce resources.

Those major psychographic differences, and also the motivational factors must be carefully aligned for both entrepreneurs and intrapreneurs. It must be understood that intrapreneurs are not driven by the same motivations, and they do not respond similarly to a wide spectrum of possible outcomes.

But they do share some common traits. Both entrepreneurs and intrapreneurs are:

  • Creative & Innovative
  • Persevering
  • Disciplined, Motivated & Self Starters
  • Inspiring, Passionate & Energetic 

Now you know the differences. Happy innovating.

Friday, August 25, 2017

Enable Websocket support in mosquitto MQTT broker



This is a long overdue blog post of my mqtt tutorial series. If you are interested in learning more on mqtt, mosquitto mqtt broker and its security you can go through my previous blog posts.



In order to continue, you will have to download the following github project and build it.


First lets create a folder named "mqtt" in home directory. Then clone the above projects to the folder we have created.

cd ~
mkdir mqtt
cd mqtt
git clone https://github.com/eclipse/mosquitto.git
First you need to build mosquitto broker from the source you have downloaded. In order to do that you will need to download the following packages.

cd mosquitto
sudo apt-get install build-essential
sudo apt-get install libc-ares-dev
sudo apt-get install uuid-dev 
sudo apt-get install libssl-dev
sudo apt-get install libwebsockets-dev
After that you need to enable websocket support in config.mk file before building the product.

# ==============================================================
# User configuration section.
#
# These options control compilation on all systems apart from Windows and Mac
# OS X. Use CMake to compile on Windows and Mac.
#
# Largely, these are options that are designed to make mosquitto run more
# easily in restrictive environments by removing features.
#
# Modify the variable below to enable/disable features.
#
# Can also be overriden at the command line, e.g.:
#
# make WITH_TLS=no
# ==============================================================
# Build with websockets support on the broker.

WITH_WEBSOCKETS:=yes

Now you can build the mosquitto mqtt broker with the following command

make binary
make install
You might get some errors when creating man pages. You can ignore them. Now you have successfully built mosquitto from the source.

Problems you can expect to encounter


You may encounter few issues with the libwebsockets dependency depending on the version you are using. 

loop.c: In function ‘loop_handle_reads_writes’:
loop.c:493:22: error: storage size of ‘wspoll’ isn’t known
    struct lws_pollfd wspoll;
                      ^
loop.c:497:4: warning: implicit declaration of function ‘lws_service_fd’ [-Wimplicit-function-declaration]
    lws_service_fd(lws_get_context(context->wsi), &wspoll);
    ^
loop.c:497:4: warning: implicit declaration of function ‘lws_get_context’ [-Wimplicit-function-declaration]
loop.c:493:22: warning: unused variable ‘wspoll’ [-Wunused-variable]
    struct lws_pollfd wspoll;
I was able to solve this issue after deleting the existing libwebsockets library and building the latest one from the source.

First you should remove the existing library using the following command

sudo apt-get remove libwebsockets3
After that clone the libwebsockets source from github

git clone https://github.com/warmcat/libwebsockets
cd libwebsockets
mkdir build
cd build
cmake ..
make && sudo make install
sudo ldconfig
Now go to the mosquitto source folder again and re-run the build command.

make binary
make install
And you may get some errors when creating man pages. You can ignore them. 

Then you need to add some configuration entries to mosquitto.conf file. Edit the Default listener section as follows.

# ==============================================================
# Default listener
# ==============================================================
# IP address/hostname to bind the default listener to. If not
# given, the default listener will not be bound to a specific
# address and so will be accessible to all network interfaces.
# bind_address ip-address/host name
#bind_address
# Port to use for the default listener.
port 1883
listener 1884
protocol websockets
Now you can go to the mosquitto source folder and start the service using the following command.

mosquitto -c /path/to/mosquitto.conf 
You may encounter an error saying Error: Websockets support not available

I had to chmod the file /var/log/mosquitto/mosquitto.log in order to make it work. Else it would fail to load the mosquitto.conf and hence websockets.conf

Also add the following configuration change to the mosquitto.conf file to point the log location.

log_dest file /var/log/mosquitto/mosquitto.log
If you face any difficulties while executing the above instructions please comment below. 


Friday, August 18, 2017

This too shall pass - Gam zeh ya’avor



Story behind the phrase "This too shall pass"

“One day Solomon decided to humble Benaiah Ben Yehoyada, his most trusted minister. He said to him, “Benaiah, there is a certain ring that I want you to bring to me. I wish to wear it for Sukkot which gives you six months to find it.”

“If it exists anywhere on earth, your majesty,” replied Benaiah,

“I will find it and bring it to you, but what makes the ring so special?” “It has magic powers,” answered the king. “If a happy man looks at it, he becomes sad, and if a sad man looks at it, he becomes happy.” Solomon knew that no such ring existed in the world, but he wished to give his minister a little taste of humility.

Spring passed and then summer, and still Benaiah had no idea where he could find the ring. On the night before Sukkot, he decided to take a walk in one of the poorest quarters of Jerusalem. He passed by a merchant who had begun to set out the day’s wares on a shabby carpet. “Have you by any chance heard of a magic ring that makes the happy wearer forget his joy and the broken-hearted wearer forget his sorrows?” asked Benaiah.

He watched the grandfather take a plain gold ring from his carpet and engrave something on it. When Benaiah read the words on the ring, his face broke out in a wide smile. That night the entire city welcomed in the holiday of Sukkot with great festivity.


“Well, my friend,” said Solomon, “have you found what I sent you after?” All the ministers laughed and Solomon himself smiled. To everyone’s surprise, Benaiah held up a small gold ring and declared, “Here it is, your majesty!” As soon as Solomon read the inscription, the smile vanished from his face. The jeweler had written three Hebrew letters on the gold band: gimel, zayin, yud, which began the words “Gam zeh ya’avor” — “This too shall pass.” At that moment Solomon realized that all his wisdom and fabulous wealth and tremendous power were but fleeting things, for one day he would be nothing but dust.”

Even This Shall Pass Away by Theodore Tilton

Once in Persia reigned a king,
Who upon his signet ring
Graved a maxim true and wise,
Which, if held before his eyes,
Gave him counsel at a glance
Fit for every change and chance.
Solemn words, and these are they;
“Even this shall pass away.”

Trains of camels through the sand
Brought him gems from Samarcand;
Fleets of galleys through the seas
Brought him pearls to match with these;
But he counted not his gain
Treasures of the mine or main;
“What is wealth?” the king would say;
“Even this shall pass away.”

‘Mid the revels of his court,
At the zenith of his sport,
When the palms of all his guests
Burned with clapping at his jests,
He, amid his figs and wine,
Cried, “O loving friends of mine;
Pleasures come, but do not stay;
‘Even this shall pass away.’”

Lady, fairest ever seen,
Was the bride he crowned the queen.
Pillowed on his marriage bed,
Softly to his soul he said:
“Though no bridegroom ever pressed
Fairer bossom to his breast,
Mortal flesh must come to clay –
Even this shall pass away.”

Fighting on a furious field,
Once a javelin pierced his shield;
Soldiers, with a loud lament,
Bore him bleeding to his tent.
Groaning from his tortured side,
“Pain is hard to bear,” he cried;
“But with patience, day by day,
Even this shall pass away.”

Towering in the public square,
Twenty cubits in the air,
Rose his statue, carved in stone.
Then the king, disguised, unknown,
Stood before his sculptured name,
Musing meekly: “What is fame?
Fame is but a slow decay;
Even this shall pass away.”

Struck with palsy, sore and old,
Waiting at the Gates of Gold,
Said he with his dying breath,
“Life is done, but what is Death?”
Then, in answer to the king,
Fell a sunbeam on his ring,
Showing by a heavenly ray,
“Even this shall pass away.”

–Theodore Tilton

Monday, January 2, 2017

Selenium Webdriver with Tor network


This is my first blog post for the year 2017.  It's been a while since I've wrote my last blog post. In this post I'll be teaching you connecting selenium web driver with tor network and how to do some cool stuff.


Knowledge Prerequisites

  • Basic understanding on Selenium - web browser automation framework
  • Basic understanding on Tor network - The Onion Router, an anonymity network
  • Java 8 & maven - Hmmm You should know what that means 😃 
  • Common Sense - A little bit

Disclaimer

I do not promote hacking, software cracking and/or piracy. All the information provided on this blog post are for educational purposes only. This blog or the author is not responsible for any misuse of the information.
You shall not misuse the information to gain unauthorized access and/or write malicious programs. These information shall only be used to expand knowledge and not for causing malicious or damaging attacks.
You may try all of these techniques on your own computer at your own risk.
Performing  any hack attempts/tests without written permission from the owner of the computer system is illegal. Breaking into computer systems is illegal. 

Connecting to Tor network using selenium


Open Tor Browser:

File torProfileDir = new File("/home/path/to/tor-browser_en-US/Browser/TorBrowser/Data/Browser/profile.default");
FirefoxBinary binary = new FirefoxBinary(new File( "/home/path/to/tor-browser_en-US/start-tor-browser.desktop"));
FirefoxProfile torProfile = new FirefoxProfile(torProfileDir);
torProfile.setPreference("webdriver.load.strategy", "unstable");

try {
    binary.startProfile(torProfile, torProfileDir, "");
} catch (IOException e) {
    e.printStackTrace();
}

Open Firefox with some configurations:

You will need to download gecko driver from here and keep it in a known location.

FirefoxProfile profile = new FirefoxProfile();
profile.setPreference("network.proxy.type", 1);
profile.setPreference("network.proxy.socks", "127.0.0.1");
profile.setPreference("network.proxy.socks_port", 9150);
System.setProperty("webdriver.gecko.driver", "/path/to/geckodriver");
FirefoxDriver driver = new FirefoxDriver(profile);

maven pom.xml :


<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>TorSelenium</groupId>
    <artifactId>com.tor.selenium</artifactId>
    <version>1.0</version>

    <dependencies>
        <dependency>
            <groupId>org.seleniumhq.selenium</groupId>
            <artifactId>selenium-firefox-driver</artifactId>
            <version>${selenium.version}</version>
        </dependency>
        <dependency>
            <groupId>org.seleniumhq.selenium</groupId>
            <artifactId>selenium-java</artifactId>
            <version>${selenium.version}</version>
        </dependency>
    </dependencies>
    <properties>
        <selenium.version>3.0.1</selenium.version>
    </properties>
</project>

Some Advanced Configurations & Hands on stuff

In this section I'll show you how you can use selenium, tor network and customized Firefox profile to automate a voting system in a web contest. 

Read the above disclaimer again before you continue. 

Some online voting systems on particular websites don't use logins to cast your vote. I'm taking those systems as example for the rest of this article. A properly designed website may track your IP address, User agent headers, Cookies and cache storage in order to allow a user to cast only one vote. But if they are not using human verification method such as captcha, you will be able to automate the user activity by using Selenium.


Access from a random IP: 

Connecting to Tor network will allow you to hide your online presence and acquire anonymity. 

You can do the following changes to the torrc file located in /path_to/tor-browser_en-US/Browser/TorBrowser/Data/Tor 
This will build a circuit for every given time and that will change your IP.

# disable adaptive circuit building timeout (we want to set it statically)
LearnCircuitBuildTimeout 0
# Try to build a circuit for 10 seconds
CircuitBuildTimeout 10
# Use also circuits that are 2 hours old because we need stable and long term lived ones
MaxCircuitDirtiness 10


Disabling Cache /  Enabling private browsing mode  


You can use the following configurations to disable different kinds of cache and enable private browsing on firefox using selenium web driver.

profile.setPreference("browser.privatebrowsing.autostart",true);
profile.setPreference("browser.cache.disk.enable", false);
profile.setPreference("browser.cache.memory.enable", false);
profile.setPreference("browser.cache.offline.enable", false);
profile.setPreference("network.http.use-cache", false);


Installing additional plugins

Some websites may track your user agent headers. You can use "Random Agent Spoofer" plugin in firefox in order to modify your user agent headers randomly.


File addonpath = new File("/path/to/random_agent_spoofer-0.9.5.6-fx.xpi");
profile.addExtension(addonpath);

Enabling user profiles on firefox

Additionally you can create user profiles on firefox, then you can customize your profile with the above preferences and open a particular profile using selenium.  You can type about:config on firefox address bar and add or edit the existing configuration.

ProfilesIni profileIni = new ProfilesIni();
FirefoxProfile profile = profileIni.getProfile("Default User");


Some basic Selenium stuff


  • Load a web page:
driver.get("https://www.google.com/");

  • Get text from an element:
WebElement voteElement = driver.findElement(By.id("TotalVotes"));
String votes = voteElement.getText();
System.out.println("current votes : " + votes);
initalVotes = Integer.parseInt(votes);

  • Wait till an element gets loaded:
(new WebDriverWait(driver, 10)).until(ExpectedConditions.elementToBeClickable(By.xpath("//a[contains(@id,'elementid')]")));

  • Wait for page to load
JavascriptExecutor js = (JavascriptExecutor)driver;
js.executeScript("return document.readyState").toString().equals("complete");

  • Click an element:
driver.findElement(By.xpath("//a[contains(@id,'elementid')]")).click();

  • Reload a webpage:
driver.navigate().refresh();

  • Respond to an alert on browser

driver.switchTo().alert().accept();

Conclusion

In this article I just wanted to give you some tricks and tips on how to use selenium with Tor browser in order to do some experimental stuff. Don't misunderstand this article as a hacking guide or something which guides you to do unethical stuff. There may be different tools to achieve the same target. But I think this article will help you to explore some unexplored areas of your knowledge.