From ce05dc180917f26329c844622d925e63058b8b0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Paulo=20Jorge=20de=20Oliveira?= Date: Thu, 19 Dec 2019 09:27:11 -0300 Subject: [PATCH 1/2] Updated code with methods 'get_all_messages' and 'get_last_message' --- .gitignore | 3 +++ whatsapp_api.py | 17 +++++++++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 8c54ada..936f045 100644 --- a/.gitignore +++ b/.gitignore @@ -143,3 +143,6 @@ dmypy.json # Pyre type checker .pyre/ + +# PyCharm files +.idea/ diff --git a/whatsapp_api.py b/whatsapp_api.py index f0b8acb..a582795 100644 --- a/whatsapp_api.py +++ b/whatsapp_api.py @@ -33,13 +33,13 @@ def _get_element(self, xpath, attempts=5, _count=0): '''Safe get_element method with multiple attempts''' try: element = self.driver.find_element_by_xpath(xpath) - print('Found element!') + # print('Found element!') return element except Exception as e: - if _count Date: Thu, 19 Dec 2019 10:13:27 -0300 Subject: [PATCH 2/2] Implemented 3 new methods --- .gitignore | 3 +- requirements.txt | 1 - whatsapp_api.py | 88 ++++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 88 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 936f045..d536845 100644 --- a/.gitignore +++ b/.gitignore @@ -144,5 +144,6 @@ dmypy.json # Pyre type checker .pyre/ -# PyCharm files +# IDE folders .idea/ +.vs/ diff --git a/requirements.txt b/requirements.txt index fafd699..7cb6656 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1 @@ selenium -phonenumbers diff --git a/whatsapp_api.py b/whatsapp_api.py index a582795..ec131bb 100644 --- a/whatsapp_api.py +++ b/whatsapp_api.py @@ -1,18 +1,28 @@ from selenium import webdriver from selenium.webdriver.chrome.options import Options +from selenium.webdriver import ActionChains from time import sleep +import sys # Parameters WP_LINK = 'https://web.whatsapp.com' -## XPATHS +# XPATHS CONTACTS = '//*[@id="main"]/header/div[2]/div[2]/span' SEND = '//*[@id="main"]/footer/div[1]/div[3]' MESSAGE_BOX = '//*[@id="main"]/footer/div[1]/div[2]/div/div[2]' NEW_CHAT = '//*[@id="side"]/header/div[2]/div/span/div[2]/div' SEARCH_CONTACT = '//*[@id="app"]/div/div/div[2]/div[1]/span/div/span/div/div[1]/div/label/input' FIRST_CONTACT = '//*[@id="app"]/div/div/div[2]/div[1]/span/div/span/div/div[2]/div/div/div/div[2]/div' +MoreContactsXPath = '//*[@id="app"]/div/div/div[2]/div[3]/span/div/span/div/div/div[1]/div[5]/div[5]/div[2]' +GroupInfoXPath = '// *[ @ id = "app"] / div / div / div[2] / div[3] / span / div / span / div / div / div[1]' +# Classes +nameClass = '_19RFN._1ovWX._F7Vk' +messageClass = '_12pGw' +messageMenuClass = '_2-qoA' +messageMenuButtonsClass = '_3zy-4.Sl-9e._3_4Kp' +eraseButtonsClass = '_2eK7W._23_1v' class WhatsApp: def __init__(self): @@ -65,10 +75,44 @@ def get_group_numbers(self): '''Get phone numbers from a whatsapp group''' try: el = self.driver.find_element_by_xpath(CONTACTS) - return el.text.split(',') + return el.text.split(', ') except Exception as e: print("Group header not found") + def get_group_members_long(self): + """Get complete members' names (or numbers, if person is not in contact list) from a WhatsApp group""" + try: + # Click on contacts: + el = self.driver.find_element_by_xpath(CONTACTS) + el.click() + sleep(3) + + # Trying to click in more contacts (it may not exist) + try: + el = self.driver.find_element_by_xpath(MoreContactsXPath) + el.click() + except Exception as e: + msg = 'Error in {}.{}. Message: {}'.format( + self.__class__.__name__, # Ref. for getting class name on 2019-06-26: https://stackoverflow.com/questions/510972/getting-the-class-name-of-an-instance + sys._getframe().f_code.co_name, # Ref. for getting method name on 2019-06-26: https://stackoverflow.com/questions/251464/how-to-get-a-function-name-as-a-string-in-python + e) + print(msg) + + el1 = self.driver.find_element_by_xpath(GroupInfoXPath) # Getting element for Group Info box panel. + el2 = el1.find_elements_by_class_name(nameClass) # Locating all elements of such class inside el1. + Members = [e.text for e in el2] # Getting only the texts, not the whole objects. + + return Members + + except Exception as e: + msg = 'Error in {}.{}. Message: {}'.format( + self.__class__.__name__, + # Ref. for getting class name on 2019-06-26: https://stackoverflow.com/questions/510972/getting-the-class-name-of-an-instance + sys._getframe().f_code.co_name, + # Ref. for getting method name on 2019-06-26: https://stackoverflow.com/questions/251464/how-to-get-a-function-name-as-a-string-in-python + e) + print(msg) + def search_contact(self, keyword): '''Write and send message''' self._click(NEW_CHAT) @@ -88,4 +132,44 @@ def get_last_message(self): all_messages = self.get_all_messages() return all_messages[-1] + def get_all_messages_elements(self): + """Gets all messages currently shown in screen.""" + all_messages_element = self.driver.find_elements_by_class_name(messageClass) + return all_messages_element + def delete_message_from_recent(self, text): + """From recent (visible) messages, deletes the one with text equals to 'text'.""" + try: + all_messages_element = self.get_all_messages_elements() # Getting all recent messages. + + for e in reversed(all_messages_element): # Looking at each element in reversed order. + if e.text == text: + # Moving mouse over message, so menu appear. Ref: http://allselenium.info/mouse-over-actions-using-python-selenium-webdriver/ + action = ActionChains(self.driver) + action.move_to_element(e).perform() + sleep(1) + + # Clicking on menu + msgMenu = self.driver.find_elements_by_class_name(messageMenuClass) + msgMenu[0].click() + sleep(1) + + # Clicking on delete button: + msgMenuButtons = self.driver.find_elements_by_class_name(messageMenuButtonsClass) # Getting buttons + msgMenuButtons[-1].click() # Clicking on last button. + sleep(1) + + # Clicking on 'Erase for me' button: + eraseButtons = self.driver.find_elements_by_class_name(eraseButtonsClass) # Getting buttons + eraseButtons[0].click() # Clicking on first button. + + break # After deleting last msg that corresponds to 'text', breaks for loop. + else: + print('Did not find recent message with text: ' + text) + + except Exception as e: + msg = 'Error in {}.{}. Message: {}'.format( + self.__class__.__name__, # Ref. for getting class name on 2019-06-26: https://stackoverflow.com/questions/510972/getting-the-class-name-of-an-instance + sys._getframe().f_code.co_name, # Ref. for getting method name on 2019-06-26: https://stackoverflow.com/questions/251464/how-to-get-a-function-name-as-a-string-in-python + e) + print(msg)