From 2c73a529b3b66dfc06e88305b479275d5d2c0c37 Mon Sep 17 00:00:00 2001 From: wes Date: Wed, 22 May 2019 19:05:48 -0400 Subject: [PATCH 1/6] Start refactoring things into separate modules --- deletefb/deletefb.py | 89 +++----------------------------------- deletefb/tools/__init__.py | 0 deletefb/tools/wall.py | 79 +++++++++++++++++++++++++++++++++ 3 files changed, 86 insertions(+), 82 deletions(-) create mode 100644 deletefb/tools/__init__.py create mode 100644 deletefb/tools/wall.py diff --git a/deletefb/deletefb.py b/deletefb/deletefb.py index 83b6434..4247b9f 100755 --- a/deletefb/deletefb.py +++ b/deletefb/deletefb.py @@ -4,6 +4,8 @@ import argparse import time import getpass +import tools.wall as wall + from seleniumrequests import Chrome from selenium.webdriver.common.action_chains import ActionChains from selenium.webdriver.chrome.options import Options @@ -54,89 +56,12 @@ def run_delete(): args_user_password = args.password or getpass.getpass('Enter your password: ') - delete_posts( - user_email_address=args.email, - user_password=args_user_password, - user_profile_url=args.profile_url, - is_headless=args.is_headless + wall.delete_posts( + user_email_address=args.email, + user_password=args_user_password, + user_profile_url=args.profile_url, + is_headless=args.is_headless ) -def delete_posts(user_email_address, - user_password, - user_profile_url, - is_headless): - """ - user_email_address: str Your Email - user_password: str Your password - user_profile_url: str Your profile URL - """ - # The Chrome driver is required because Gecko was having issues - chrome_options = Options() - prefs = {"profile.default_content_setting_values.notifications": 2, 'disk-cache-size': 4096} - chrome_options.add_experimental_option("prefs", prefs) - chrome_options.add_argument("start-maximized") - - if is_headless: - chrome_options.add_argument('--headless') - chrome_options.add_argument('--disable-gpu') - chrome_options.add_argument('log-level=2') - - driver = Chrome(options=chrome_options) - driver.implicitly_wait(10) - - driver.get("https://facebook.com") - - email = "email" - password = "pass" - login = "loginbutton" - - emailelement = driver.find_element_by_name(email) - passwordelement = driver.find_element_by_name(password) - - emailelement.send_keys(user_email_address) - passwordelement.send_keys(user_password) - - loginelement = driver.find_element_by_id(login) - loginelement.click() - - if "Two-factor authentication" in driver.page_source: - # Allow time to enter 2FA code - print("Pausing to enter 2FA code") - time.sleep(20) - print("Continuing execution") - driver.get(user_profile_url) - - for _ in range(MAX_POSTS): - post_button_sel = "_4xev" - - while True: - try: - timeline_element = driver.find_element_by_class_name(post_button_sel) - actions = ActionChains(driver) - actions.move_to_element(timeline_element).click().perform() - - menu = driver.find_element_by_css_selector("#globalContainer > div.uiContextualLayerPositioner.uiLayer > div") - actions.move_to_element(menu).perform() - - try: - delete_button = menu.find_element_by_xpath("//a[@data-feed-option-name=\"FeedDeleteOption\"]") - except SELENIUM_EXCEPTIONS: - delete_button = menu.find_element_by_xpath("//a[@data-feed-option-name=\"HIDE_FROM_TIMELINE\"]") - - actions.move_to_element(delete_button).click().perform() - confirmation_button = driver.find_element_by_class_name("layerConfirm") - - # Facebook would not let me get focus on this button without some custom JS - driver.execute_script("arguments[0].click();", confirmation_button) - except SELENIUM_EXCEPTIONS: - continue - else: - break - - # Required to sleep the thread for a bit after using JS to click this button - time.sleep(5) - driver.refresh() - - if __name__ == "__main__": run_delete() diff --git a/deletefb/tools/__init__.py b/deletefb/tools/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/deletefb/tools/wall.py b/deletefb/tools/wall.py new file mode 100644 index 0000000..50b5222 --- /dev/null +++ b/deletefb/tools/wall.py @@ -0,0 +1,79 @@ + +def delete_posts(user_email_address, + user_password, + user_profile_url, + is_headless): + """ + user_email_address: str Your Email + user_password: str Your password + user_profile_url: str Your profile URL + """ + # The Chrome driver is required because Gecko was having issues + chrome_options = Options() + prefs = {"profile.default_content_setting_values.notifications": 2, 'disk-cache-size': 4096} + chrome_options.add_experimental_option("prefs", prefs) + chrome_options.add_argument("start-maximized") + + if is_headless: + chrome_options.add_argument('--headless') + chrome_options.add_argument('--disable-gpu') + chrome_options.add_argument('log-level=2') + + driver = Chrome(options=chrome_options) + driver.implicitly_wait(10) + + driver.get("https://facebook.com") + + email = "email" + password = "pass" + login = "loginbutton" + + emailelement = driver.find_element_by_name(email) + passwordelement = driver.find_element_by_name(password) + + emailelement.send_keys(user_email_address) + passwordelement.send_keys(user_password) + + loginelement = driver.find_element_by_id(login) + loginelement.click() + + if "Two-factor authentication" in driver.page_source: + # Allow time to enter 2FA code + print("Pausing to enter 2FA code") + time.sleep(20) + print("Continuing execution") + driver.get(user_profile_url) + + for _ in range(MAX_POSTS): + post_button_sel = "_4xev" + + while True: + try: + timeline_element = driver.find_element_by_class_name(post_button_sel) + actions = ActionChains(driver) + actions.move_to_element(timeline_element).click().perform() + + menu = driver.find_element_by_css_selector("#globalContainer > div.uiContextualLayerPositioner.uiLayer > div") + actions.move_to_element(menu).perform() + + try: + delete_button = menu.find_element_by_xpath("//a[@data-feed-option-name=\"FeedDeleteOption\"]") + except SELENIUM_EXCEPTIONS: + delete_button = menu.find_element_by_xpath("//a[@data-feed-option-name=\"HIDE_FROM_TIMELINE\"]") + + actions.move_to_element(delete_button).click().perform() + confirmation_button = driver.find_element_by_class_name("layerConfirm") + + # Facebook would not let me get focus on this button without some custom JS + driver.execute_script("arguments[0].click();", confirmation_button) + except SELENIUM_EXCEPTIONS: + continue + else: + break + + # Required to sleep the thread for a bit after using JS to click this button + time.sleep(5) + driver.refresh() + + + From 8cc9074934daa67792f3109f25019ee7dc56d55d Mon Sep 17 00:00:00 2001 From: wes Date: Wed, 22 May 2019 19:11:21 -0400 Subject: [PATCH 2/6] Refactoring --- deletefb/deletefb.py | 11 +---------- deletefb/tools/wall.py | 7 +++++++ 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/deletefb/deletefb.py b/deletefb/deletefb.py index 4247b9f..a2a1aa0 100755 --- a/deletefb/deletefb.py +++ b/deletefb/deletefb.py @@ -3,16 +3,7 @@ import argparse import time import getpass - -import tools.wall as wall - -from seleniumrequests import Chrome -from selenium.webdriver.common.action_chains import ActionChains -from selenium.webdriver.chrome.options import Options -from selenium.common.exceptions import NoSuchElementException, StaleElementReferenceException - -MAX_POSTS = 5000 -SELENIUM_EXCEPTIONS = (NoSuchElementException, StaleElementReferenceException) +import deletefb.tools.wall as wall def run_delete(): parser = argparse.ArgumentParser() diff --git a/deletefb/tools/wall.py b/deletefb/tools/wall.py index 50b5222..bb23e16 100644 --- a/deletefb/tools/wall.py +++ b/deletefb/tools/wall.py @@ -1,3 +1,10 @@ +from selenium.webdriver.chrome.options import Options +from seleniumrequests import Chrome +from selenium.webdriver.common.action_chains import ActionChains +from selenium.common.exceptions import NoSuchElementException, StaleElementReferenceException + +MAX_POSTS = 5000 +SELENIUM_EXCEPTIONS = (NoSuchElementException, StaleElementReferenceException) def delete_posts(user_email_address, user_password, From 81fd9329f9b537df5cabf22cf548c6749af0f07b Mon Sep 17 00:00:00 2001 From: wes Date: Wed, 22 May 2019 19:21:17 -0400 Subject: [PATCH 3/6] More refactoring into modules --- deletefb/deletefb.py | 15 ++++++----- deletefb/tools/login.py | 57 +++++++++++++++++++++++++++++++++++++++++ deletefb/tools/wall.py | 50 +++--------------------------------- 3 files changed, 70 insertions(+), 52 deletions(-) create mode 100644 deletefb/tools/login.py diff --git a/deletefb/deletefb.py b/deletefb/deletefb.py index a2a1aa0..84a9c75 100755 --- a/deletefb/deletefb.py +++ b/deletefb/deletefb.py @@ -1,8 +1,9 @@ #! /usr/bin/env python import argparse -import time import getpass + +from deletefb.tools.login import login import deletefb.tools.wall as wall def run_delete(): @@ -47,12 +48,14 @@ def run_delete(): args_user_password = args.password or getpass.getpass('Enter your password: ') - wall.delete_posts( - user_email_address=args.email, - user_password=args_user_password, - user_profile_url=args.profile_url, - is_headless=args.is_headless + driver = login( + user_email_address=args.email, + user_password=args_user_password, + user_profile_url=args.profile_url, + is_headless=args.is_headless ) + wall.delete_posts(driver) + if __name__ == "__main__": run_delete() diff --git a/deletefb/tools/login.py b/deletefb/tools/login.py new file mode 100644 index 0000000..d1c81df --- /dev/null +++ b/deletefb/tools/login.py @@ -0,0 +1,57 @@ +import time + +from selenium.webdriver.chrome.options import Options +from seleniumrequests import Chrome +from selenium.webdriver.common.action_chains import ActionChains +from selenium.common.exceptions import NoSuchElementException, StaleElementReferenceException + +def login(user_email_address, + user_password, + user_profile_url, + is_headless): + """ + Attempts to log into Facebook + Returns a driver object + + user_email_address: str Your Email + user_password: str Your password + user_profile_url: str Your profile URL + + """ + # The Chrome driver is required because Gecko was having issues + chrome_options = Options() + prefs = {"profile.default_content_setting_values.notifications": 2, 'disk-cache-size': 4096} + chrome_options.add_experimental_option("prefs", prefs) + chrome_options.add_argument("start-maximized") + + if is_headless: + chrome_options.add_argument('--headless') + chrome_options.add_argument('--disable-gpu') + chrome_options.add_argument('log-level=2') + + driver = Chrome(options=chrome_options) + driver.implicitly_wait(10) + + driver.get("https://facebook.com") + + email = "email" + password = "pass" + login = "loginbutton" + + emailelement = driver.find_element_by_name(email) + passwordelement = driver.find_element_by_name(password) + + emailelement.send_keys(user_email_address) + passwordelement.send_keys(user_password) + + loginelement = driver.find_element_by_id(login) + loginelement.click() + + if "Two-factor authentication" in driver.page_source: + # Allow time to enter 2FA code + print("Pausing to enter 2FA code") + time.sleep(20) + print("Continuing execution") + driver.get(user_profile_url) + + return driver diff --git a/deletefb/tools/wall.py b/deletefb/tools/wall.py index bb23e16..0093fce 100644 --- a/deletefb/tools/wall.py +++ b/deletefb/tools/wall.py @@ -1,3 +1,5 @@ +import time + from selenium.webdriver.chrome.options import Options from seleniumrequests import Chrome from selenium.webdriver.common.action_chains import ActionChains @@ -6,51 +8,10 @@ from selenium.common.exceptions import NoSuchElementException, StaleElementRefer MAX_POSTS = 5000 SELENIUM_EXCEPTIONS = (NoSuchElementException, StaleElementReferenceException) -def delete_posts(user_email_address, - user_password, - user_profile_url, - is_headless): +def delete_posts(driver): """ - user_email_address: str Your Email - user_password: str Your password - user_profile_url: str Your profile URL + Deletes or hides all posts from the wall """ - # The Chrome driver is required because Gecko was having issues - chrome_options = Options() - prefs = {"profile.default_content_setting_values.notifications": 2, 'disk-cache-size': 4096} - chrome_options.add_experimental_option("prefs", prefs) - chrome_options.add_argument("start-maximized") - - if is_headless: - chrome_options.add_argument('--headless') - chrome_options.add_argument('--disable-gpu') - chrome_options.add_argument('log-level=2') - - driver = Chrome(options=chrome_options) - driver.implicitly_wait(10) - - driver.get("https://facebook.com") - - email = "email" - password = "pass" - login = "loginbutton" - - emailelement = driver.find_element_by_name(email) - passwordelement = driver.find_element_by_name(password) - - emailelement.send_keys(user_email_address) - passwordelement.send_keys(user_password) - - loginelement = driver.find_element_by_id(login) - loginelement.click() - - if "Two-factor authentication" in driver.page_source: - # Allow time to enter 2FA code - print("Pausing to enter 2FA code") - time.sleep(20) - print("Continuing execution") - driver.get(user_profile_url) - for _ in range(MAX_POSTS): post_button_sel = "_4xev" @@ -81,6 +42,3 @@ def delete_posts(user_email_address, # Required to sleep the thread for a bit after using JS to click this button time.sleep(5) driver.refresh() - - - From 9f9fcc1ba633955b660362d03e70c0c93446dcb5 Mon Sep 17 00:00:00 2001 From: wes Date: Wed, 22 May 2019 19:25:00 -0400 Subject: [PATCH 4/6] Remove unnecessary imports --- deletefb/tools/login.py | 2 -- deletefb/tools/wall.py | 2 -- 2 files changed, 4 deletions(-) diff --git a/deletefb/tools/login.py b/deletefb/tools/login.py index d1c81df..62ad2a3 100644 --- a/deletefb/tools/login.py +++ b/deletefb/tools/login.py @@ -2,8 +2,6 @@ import time from selenium.webdriver.chrome.options import Options from seleniumrequests import Chrome -from selenium.webdriver.common.action_chains import ActionChains -from selenium.common.exceptions import NoSuchElementException, StaleElementReferenceException def login(user_email_address, user_password, diff --git a/deletefb/tools/wall.py b/deletefb/tools/wall.py index 0093fce..d187bb6 100644 --- a/deletefb/tools/wall.py +++ b/deletefb/tools/wall.py @@ -1,7 +1,5 @@ import time -from selenium.webdriver.chrome.options import Options -from seleniumrequests import Chrome from selenium.webdriver.common.action_chains import ActionChains from selenium.common.exceptions import NoSuchElementException, StaleElementReferenceException From 1502feef4ba703bca98aa7b7a9d8e2803b3c25ca Mon Sep 17 00:00:00 2001 From: wes Date: Wed, 22 May 2019 21:52:04 -0400 Subject: [PATCH 5/6] More cleanup --- deletefb/tools/common.py | 3 +++ deletefb/tools/likes.py | 8 ++++++++ deletefb/tools/login.py | 12 ++++++------ deletefb/tools/wall.py | 3 +-- 4 files changed, 18 insertions(+), 8 deletions(-) create mode 100644 deletefb/tools/common.py create mode 100644 deletefb/tools/likes.py diff --git a/deletefb/tools/common.py b/deletefb/tools/common.py new file mode 100644 index 0000000..92405c4 --- /dev/null +++ b/deletefb/tools/common.py @@ -0,0 +1,3 @@ +from selenium.common.exceptions import NoSuchElementException, StaleElementReferenceException + +SELENIUM_EXCEPTIONS = (NoSuchElementException, StaleElementReferenceException) diff --git a/deletefb/tools/likes.py b/deletefb/tools/likes.py new file mode 100644 index 0000000..80c6b0a --- /dev/null +++ b/deletefb/tools/likes.py @@ -0,0 +1,8 @@ +from selenium.webdriver.common.action_chains import ActionChains +from .common import SELENIUM_EXCEPTIONS + +def unlike_pages(driver): + """ + Unlike all pages + """ + return diff --git a/deletefb/tools/login.py b/deletefb/tools/login.py index 62ad2a3..d3d7663 100644 --- a/deletefb/tools/login.py +++ b/deletefb/tools/login.py @@ -36,14 +36,14 @@ def login(user_email_address, password = "pass" login = "loginbutton" - emailelement = driver.find_element_by_name(email) - passwordelement = driver.find_element_by_name(password) + email_element = driver.find_element_by_name(email) + password_element = driver.find_element_by_name(password) - emailelement.send_keys(user_email_address) - passwordelement.send_keys(user_password) + email_element.send_keys(user_email_address) + password_element.send_keys(user_password) - loginelement = driver.find_element_by_id(login) - loginelement.click() + login_element = driver.find_element_by_id(login) + login_element.click() if "Two-factor authentication" in driver.page_source: # Allow time to enter 2FA code diff --git a/deletefb/tools/wall.py b/deletefb/tools/wall.py index d187bb6..3005dd1 100644 --- a/deletefb/tools/wall.py +++ b/deletefb/tools/wall.py @@ -1,10 +1,9 @@ import time from selenium.webdriver.common.action_chains import ActionChains -from selenium.common.exceptions import NoSuchElementException, StaleElementReferenceException +from .common import SELENIUM_EXCEPTIONS MAX_POSTS = 5000 -SELENIUM_EXCEPTIONS = (NoSuchElementException, StaleElementReferenceException) def delete_posts(driver): """ From 1770fa47aabd93774e96e4bf9c7309fa5b7391a4 Mon Sep 17 00:00:00 2001 From: Wesley Kerfoot Date: Fri, 24 May 2019 20:49:42 -0400 Subject: [PATCH 6/6] Clean up README --- README.md | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 41730e5..fca93c9 100644 --- a/README.md +++ b/README.md @@ -38,27 +38,19 @@ would if you had installed it from PyPI. everything. You may safely minimize the chrome window without breaking it. ## 2FA -* If you have 2-Factor Auth configured then the script will pause for 20 +* It is recommended that you disable Two-Factor Authentication tempoprarily + while you are running the script, in order to get the best experience. + +* If you do have 2-Factor Auth configured then the script will pause for 20 seconds to allow you to enter your code and log in. +* You may also pass in a code by using the `-F` argument, e.g. `-F 111111`. + ## Headless mode * The tool supports running Chrome in headless mode with the `--headless` option, which may be preferable if you plan on running it in the background. -## How To Install Python - -### MacOS -See [this link](https://docs.python-guide.org/starting/install3/osx/) for -instructions on installing with Brew. - -### Linux -Use your native package manager - -### Windows -See [this link](https://www.howtogeek.com/197947/how-to-install-python-on-windows/), but I make no guarantees that Selenium will actually work as I have not tested it. - - -### Bugs +## Bugs If it stops working or otherwise crashes, delete the latest post manually and start it again after waiting a minute. I make no guarantees that it will work