10
\$\begingroup\$

There is no official WhatsApp API. Here is a simple python class which satisfies the need.

# SWAMI KARUPPASWAMI THUNNAI

#============================================================
# Simple yet Hackable! WhatsApp API [UNOFFICIAL] for Python3
# Note: The author gives permission to use it under Apache2.0
#============================================================

import time
import datetime as dt
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import WebDriverWait

class Emoji:
    """
    This class is used to send emoji's to whatsapp
    """
    emoji = {}
    # We will load our emojis in the constructor
    def __init__(self):
        self.emoji[':thumbsup:'] = "(y)"
        self.emoji[':-1:'] = "(n)"
        self.emoji[':simple_smile:'] = ":-)"
        self.emoji[':worried:'] = ":-("
        self.emoji['stuck_out_tongue:'] = ":-p"
        self.emoji[':laughing:'] = ":-d"
        self.emoji[':heart:'] = "<3"
        self.emoji[':neutral_face:'] = ":-|"
        self.emoji[':wink:'] = ";-)"

    # This method is used to emojify all the text emoji's present in the message
    def emojify(self,message):
        emojies = self.emoji.keys()
        for emoji in emojies:
            message = message.replace(emoji,self.emoji[emoji])
        return message


class WhatsApp:
    """
    This class is used to interact with your whatsapp [UNOFFICIAL API]
    """
    browser = webdriver.Chrome()
    def __init__(self,wait,screenshot=None):
        self.browser.get("https://web.whatsapp.com/")
        time.sleep(wait)
        if screenshot != None:
            self.browser.save_screenshot(screenshot) # This will save the screenshot to the specified file location

    # This method is used to send the message to the individual person or a group
    # will return true if the message has been sent, false else
    def send_message(self,name,message):
        emoji = Emoji() # create an emoji object
        message = emoji.emojify(message) # this will emojify all the emoji which is present as the text in string
        search = self.browser.find_element_by_class_name("input-search")
        search.send_keys(name+Keys.ENTER) # we will send the name to the input key box
        current_time = time.time()
        while True:
            try:
                send_msg = self.browser.find_element_by_class_name("input")
                send_msg.send_keys(message+Keys.ENTER) # send the message
                return True
            except Exception as e:
                print(e)
            new_time = time.time()
            elapsed_time = new_time - current_time
            if elapsed_time > 30:
                return False

    # This method will count the no of participants for the group name provided
    def participants_for_group(self,group_name):
        search = self.browser.find_element_by_class_name("input-search")
        search.send_keys(group_name+Keys.ENTER) # we will send the name to the input key box
        current_time = time.time()
        while True:
            try:
                click_menu = self.browser.find_element_by_css_selector("header.pane-header:nth-child(2) > div:nth-child(2) > div:nth-child(1) > div:nth-child(1) > span:nth-child(1)")
                click_menu.click()
                participants_css_selector = "div.animate-enter2:nth-child(4) > div:nth-child(1) > div:nth-child(1) > div:nth-child(2)"
                participants_count = self.browser.find_element_by_css_selector(participants_css_selector).text
                if "256" in participants_count:
                    return participants_count
                else:
                    time.sleep(1)
            except Exception as e:
                print(e)
            new_time = time.time()
            elapsed_time = new_time - current_time
            if elapsed_time > 100:
                return "NONE" 

    # get the status message of a person 
    # TimeOut is approximately set to 10 seconds
    def get_status(self,name,timeout=10):
        search = self.browser.find_element_by_class_name("input-search")
        search.send_keys(name+Keys.ENTER) # we will send the name to the input key box
        start_time = dt.datetime.now()
        while True:
            try:
                click_menu = self.browser.find_element_by_css_selector("header.pane-header:nth-child(2) > div:nth-child(2) > div:nth-child(1) > div:nth-child(1) > span:nth-child(1)")
                click_menu.click()
                # This is the css selector for status
                status_selector = "div.animate-enter2:nth-child(4) > div:nth-child(2) > div:nth-child(1) > div:nth-child(1) > div:nth-child(1) > span:nth-child(1) > span:nth-child(1)"
                status = self.browser.find_element_by_css_selector(status_selector).text
                # if the status if obtained the length must be greater than 0
                if len(status) > 0:
                    return status
            except Exception as e:
                pass
            end_time = dt.datetime.now()
            elapsed_time = (end_time - start_time).seconds
            if elapsed_time > timeout:
                raise TimeoutError("The response has been TimedOut. You may change the time-out specifying in your args.")

    # to get the last seen of the person
    def get_last_seen(self,name,timeout=10):
        search = self.browser.find_element_by_class_name("input-search")
        search.send_keys(name+Keys.ENTER) # we will send the name to the input key box
        start_time = dt.datetime.now()
        last_seen_css_selector = ".chat-subtitle-text"
        while True:
            try:
                last_seen = self.browser.find_element_by_css_selector(last_seen_css_selector).text
                if len(last_seen) > 0:
                    if "click here" in last_seen:
                        pass
                    else:
                        return last_seen
            except Exception as e:
                pass

            end_time = dt.datetime.now()
            elapsed_time = (end_time - start_time).seconds
            if elapsed_time > timeout:
                raise TimeoutError("The response has been TimedOut. You may change the time-out specifying in your args.")


    # This method is used to quit the browser
    def quit(self):
        self.browser.quit()

Examples: You can achieve many thing less than five lines of code

Sending a message with an emoji:

from whatsapp import WhatsApp
whatsapp = WhatsApp(10)
print(whatsapp.send_message("Name",":heart: Good!"))  

Result:

enter image description here

Getting the status message of a person:

from whatsapp import WhatsApp
whatsapp = WhatsApp(10)
print(whatsapp.get_status("Oracle"))

Result: Have a nice day!

Getting last seen of a person:

from whatsapp import WhatsApp
whatsapp = WhatsApp(10)
print(whatsapp.get_last_seen("Oracle"))

Result: last seen today 11:34 pm

Getting the no of participants in the group:

from whatsapp import WhatsApp
whatsapp = WhatsApp(10)
result = whatsapp.participants_for_group("group")

Result: 3 of 256

Advantages:

  1. Limited emoji support is provided.

  2. The library itself hackable, you can use python lists to send messages to various persons or you can loop for a person's last seen status and if the status comes "Online" you can greet him a message. You can monitor the participants in the group in a loop and greet if new participants are added.

\$\endgroup\$
7
\$\begingroup\$

Here is a collection of things to improve.

Code Style Notes

Overall, there is a number of PEP8 Code Style violations, like lack of spaces around operators, blank lines, spaces before the # comment character etc. Make sure to address them. You can also use tools like flake8 or pylint to statically analyze code for common PEP8 and other code quality issues.

Some other Code Style related notes:

  • WebDriverWait is unused - this time, don't remove it but rather use it instead of time.sleep() - more about it later below
  • be specific about what exceptions you are handling avoiding catching bare Exception errors - for instance, you should rather be catching either WebDriverException than Exception in your browser interaction methods.
  • use is when comparing to None; for instance, replacing:

    if screenshot != None:
    

    with:

    if screenshot is not None:
    
  • you can simplify the emptiness checks. For example, you can replace:

    if len(last_seen) > 0:
        if "click here" in last_seen:
            pass
        else:
            return last_seen
    

    with:

    if last_seen and "click here" not in last_seen:
        return last_seen
    
  • you don't need to call .keys() on the dictionary and then iterate over them - you can iterate over the keys by directly iterating over the dictionary:

    for emoji in self.emoji:
    
  • avoid hardcoding things inside your implementations - move "magic" numbers like these timeout values inside the browser interaction methods to proper constants (or use that timeout argument to compare with the elapsed time)

Selenium-specific Improvements

  • instead of hardcoding wait timeouts and using time.sleep(), use WebDriverWait which implements an Explicit Wait functionality. There is a number of built-in Expected Conditions to wait for (and it's easy to write your own one) - for instance, you may wait for an element to be present or visible, button or link to be clickable etc.

    time.sleep() is seriously unreliable - it tends to wait more than you usually need and fails randomly when the timeout was not enough.

    And, I don't think you should parameterize your class with a wait value.

  • you should tie the browse instance to your class instance and not the class itself - initialize browser inside the __init__() constructor of the WhatsApp class
  • your CSS selectors are too fragile because they depend on the HTML structure of the page too much. Try to improve the quality and reliability of your locators, some tips to get you started: What makes a good selenium locator?

Other Things

  • I don't really see the need for Emoji to be a class. For me, it would be perfectly fine to have it in a dictionary or even in a separate JSON or YAML config file. emojify() can simply be a utility function. More on the subject of when to use classes and when not:

  • or, you can take it a step further and use the emoji third-party library

note I don't use WhatsApp and may be missing some context-specific things here.

\$\endgroup\$

protected by Community Nov 9 '18 at 6:39

Thank you for your interest in this question. Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).

Would you like to answer one of these unanswered questions instead?

Not the answer you're looking for? Browse other questions tagged or ask your own question.