Buddy: My WhatsApp Personal Assistant

using Selenium with Python

Posted on December 29, 2017

This is a different kind of chat bot built with Selenium. Selenium is a tool for automating web browsers. Selenium is capable of doing anything that you would be able to do on a browser.

So this script simulates what you'd normally do to message your friend on WhatsApp.

  • Open Google Chrome
  • Navigate to Web WhatsApp
  • Look for your friend's name
  • Read his last message
  • Send a reply

Now let's get started with the code

Importing python dependencies

Make sure to have python3, selenium and beautifulsoup installed and import the following dependencies. Any missing libraries can be installed with pip install.

          from selenium import webdriver
          from selenium.webdriver.chrome.options import Options
          from selenium.webdriver.common.keys import Keys
          from selenium.webdriver.common.action_chains import ActionChains
          from requests import get
          from bs4 import BeautifulSoup as bs
          import keyboard
          import time
          import click
          import os
          import sys
          import csv
          import threading
Web Browser

All major browsers are supported in Selenium. I used Chrome for the chat bot.

To start chrome with Selenium

	  chrome_options = Options()
          chrome_options.add_argument("user-data-dir=" + os.path.dirname(sys.argv[0]))
          driver = webdriver.Chrome(chrome_options=chrome_options)
          driver.maximize_window()

For some reason navigating to "https://web.whatsapp.com/" did not work. So I converted the url with Google URL Shortener.

I added a delay of 25 seconds to allow WhatsApp to load completely.

	  driver.get("https://goo.gl/WCS3hC")
          time.sleep(25)
Locating friend's name with Beautiful Soup

Define your friend or the group name, matching the characters exactly. The below code scrolls through the chat list to find the tab matching your target and clicks on it.

          target = '"your_friend/group_name"'

          panel = driver.find_element_by_class_name('chatlist-panel-body')

          elem = None
          a = 0
          while elem is None:
              a += 300
              try:
                  driver.execute_script('arguments[0].scrollTop = %s' %a, panel)
                  elem = driver.find_element_by_xpath('//span[@title=' + target + ']')
              except:
                   pass

          ac = ActionChains(driver)
          ac.move_to_element(elem).click().perform()
          time.sleep(2)

          url = driver.page_source

driver.page_source loads the latest html source code after user inputs

Reading last sent text message

The below is a series of conditions to check, if the selected tab is an user or a group, if the last message was sent by the Bot or by a friend.

Once properly located, the script stores the last sent message. This code runs every 5 seconds and looks for any new message with the threading function.

The rest of the code should be placed inside this function.

          def repeatfun():
                threading.Timer(5.0, repeatfun).start()
                url = driver.page_source
                soup = bs(url, "lxml")

                try:
                    gotdiv = soup.find_all("div", { "class" : "msg msg-group" })[-1]
                except IndexError:
                    gotdiv = 'null'

                if gotdiv == 'null':
                    div = soup.find_all("div", { "class" : "bubble bubble-text copyable-text" })[-1]
                    #print(div)
                else:
                    div = soup.find_all("div", { "class" : "msg msg-group" })[-1]

                text = div.find_all('span')
                print(text)

                try:
                    gottext = text[4].find_all(text=True)[1]
                except IndexError:
                    gottext = 'null'

                if gottext == 'null':
                    div = soup.find_all("div", { "class" : "chat-title" })[-1]
                    name = div.find_all(text=True)[1]
                    try:
                        msg = text[-2].find_all(text=True)[1].lower()
                    except IndexError:
                        msg = "You replied last"
                    time = text[-1].find(text=True)

                else: #group
                    name = text[3].find_all(text=True)[1]
                    try:
                        msg = text[4].find_all(text=True)[1].lower()
                    except IndexError:
                        msg = "You replied last"
                    try:
                        time = text[-2].find(text=True)
                    except:
                        time = "None"


                print(name, msg, time)

Print out the stored details to verify if everything works.

To check if the message has already been stored
          try:
              prevmsg = prevmsg
          except:
              prevmsg = "first"

          try:
              prevtime = prevtime
          except:
              prevtime = "first"
Fetching reply from the csv file

When the conditions are satisfied, the code looks for the word "Buddy" in the stored text (the Bot only responds to texts with the word "Buddy"). This is to ignore other text messages that are not intended for the Bot.

           if "buddy" in msg:

               with open('dict.csv', "r") as f:
                  reader = csv.reader(f)
                  chat = {}

                  for row in reader:
                      key = row[0]
                      chat[key] = row[1:]
               try:
                  gotreply = chat[msg]
               except KeyError:
                  gotreply = 'null'

               print(gotreply)

The message is iterated through the csv file and a corresponding reply is returned.

Dict
Sending Reply

The message is either in the csv file or absent. A simple if-else statement to return the corresponding reply.

          if gotreply == 'null':
            string = "Sorry! I didn't understand. I'm still learning."
            input_box = driver.find_element_by_class_name('pluggable-input-body')
            input_box.send_keys(string)
            driver.find_element_by_xpath('//span[@data-icon="send"]').click()
          else:
            input_box = driver.find_element_by_class_name('pluggable-input-body')
            input_box.send_keys(gotreply)
            driver.find_element_by_xpath('//span[@data-icon="send"]').click()

Selenium is then used to locate the "enter" button and the reply is sent on WhatsApp

Integrating Facebook and Twitter API with the WhatsApp Chat Bot

Instead of looking up for replies from the csv file, the bot can be integrated with the Facebook and Twitter API (refer previous blog) to send the retrieved data from social media to WhatsApp.

Use Case:

A message: "Buddy, Give me the latest news from BBC facebook page" should reply you with the latest status from the FB page.

Complete code link

For full code, click here