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 diff --git a/deletefb/deletefb.py b/deletefb/deletefb.py index 7d260ce..b6f3715 100755 --- a/deletefb/deletefb.py +++ b/deletefb/deletefb.py @@ -1,16 +1,10 @@ #! /usr/bin/env python import argparse -import time import getpass -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) +from deletefb.tools.login import login +import deletefb.tools.wall as wall def run_delete(): parser = argparse.ArgumentParser() @@ -63,7 +57,7 @@ def run_delete(): args_user_password = args.password or getpass.getpass('Enter your password: ') - delete_posts( + driver = login( user_email_address=args.email, user_password=args_user_password, user_profile_url=args.profile_url, @@ -71,99 +65,7 @@ def run_delete(): two_factor_token=args.two_factor_token ) -def delete_posts(user_email_address, - user_password, - user_profile_url, - is_headless, - two_factor_token): - """ - 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" - approvals_code = "approvals_code" - - 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.lower(): - - if two_factor_token: - - twofactorelement = driver.find_element_by_name(approvals_code) - twofactorelement.send_keys(two_factor_token) - - #Submits after the code is passed into the form, does not validate 2FA code. - contelement = driver.find_element_by_id("checkpointSubmitButton") - contelement.click() - - #Defaults to saving this new browser, this occurs on each new automated login. - save_browser = driver.find_element_by_id("checkpointSubmitButton") - save_browser.click() - else: - # 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() - + wall.delete_posts(driver) 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/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 new file mode 100644 index 0000000..38aa5bf --- /dev/null +++ b/deletefb/tools/login.py @@ -0,0 +1,70 @@ +import time + +from selenium.webdriver.chrome.options import Options +from seleniumrequests import Chrome + +def login(user_email_address, + user_password, + user_profile_url, + is_headless, + two_factor_token): + """ + 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" + approvals_code = "approvals_code" + + 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.lower(): + if two_factor_token: + + twofactorelement = driver.find_element_by_name(approvals_code) + twofactorelement.send_keys(two_factor_token) + + # Submits after the code is passed into the form, does not validate 2FA code. + contelement = driver.find_element_by_id("checkpointSubmitButton") + contelement.click() + + # Defaults to saving this new browser, this occurs on each new automated login. + save_browser = driver.find_element_by_id("checkpointSubmitButton") + save_browser.click() + else: + # 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 new file mode 100644 index 0000000..3005dd1 --- /dev/null +++ b/deletefb/tools/wall.py @@ -0,0 +1,41 @@ +import time + +from selenium.webdriver.common.action_chains import ActionChains +from .common import SELENIUM_EXCEPTIONS + +MAX_POSTS = 5000 + +def delete_posts(driver): + """ + Deletes or hides all posts from the wall + """ + 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()