From 2c73a529b3b66dfc06e88305b479275d5d2c0c37 Mon Sep 17 00:00:00 2001 From: wes Date: Wed, 22 May 2019 19:05:48 -0400 Subject: [PATCH 01/93] 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() + + + -- 2.30.2 From 8cc9074934daa67792f3109f25019ee7dc56d55d Mon Sep 17 00:00:00 2001 From: wes Date: Wed, 22 May 2019 19:11:21 -0400 Subject: [PATCH 02/93] 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, -- 2.30.2 From 81fd9329f9b537df5cabf22cf548c6749af0f07b Mon Sep 17 00:00:00 2001 From: wes Date: Wed, 22 May 2019 19:21:17 -0400 Subject: [PATCH 03/93] 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() - - - -- 2.30.2 From 9f9fcc1ba633955b660362d03e70c0c93446dcb5 Mon Sep 17 00:00:00 2001 From: wes Date: Wed, 22 May 2019 19:25:00 -0400 Subject: [PATCH 04/93] 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 -- 2.30.2 From 1502feef4ba703bca98aa7b7a9d8e2803b3c25ca Mon Sep 17 00:00:00 2001 From: wes Date: Wed, 22 May 2019 21:52:04 -0400 Subject: [PATCH 05/93] 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): """ -- 2.30.2 From 6146103942e50b5b8b7eccbc9e69e7c56a5fdd32 Mon Sep 17 00:00:00 2001 From: "Joseph D. Marhee" Date: Fri, 24 May 2019 10:52:17 -0500 Subject: [PATCH 06/93] adds handling for 2fa code command-line argument --- deletefb/deletefb.py | 39 +++++++++++++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/deletefb/deletefb.py b/deletefb/deletefb.py index 01dd660..7d260ce 100755 --- a/deletefb/deletefb.py +++ b/deletefb/deletefb.py @@ -41,6 +41,15 @@ def run_delete(): help="The link to your Facebook profile, e.g. https://www.facebook.com/your.name" ) + parser.add_argument( + "-F", + "--two-factor", + required=False, + dest="two_factor_token", + type=str, + help="The code generated by your 2FA device for Facebook" + ) + parser.add_argument( "-H", "--headless", @@ -58,13 +67,15 @@ def run_delete(): user_email_address=args.email, user_password=args_user_password, user_profile_url=args.profile_url, - is_headless=args.is_headless + is_headless=args.is_headless, + two_factor_token=args.two_factor_token ) def delete_posts(user_email_address, user_password, user_profile_url, - is_headless): + is_headless, + two_factor_token): """ user_email_address: str Your Email user_password: str Your password @@ -89,6 +100,7 @@ def delete_posts(user_email_address, email = "email" password = "pass" login = "loginbutton" + approvals_code = "approvals_code" emailelement = driver.find_element_by_name(email) passwordelement = driver.find_element_by_name(password) @@ -100,10 +112,25 @@ def delete_posts(user_email_address, loginelement.click() if "two-factor authentication" in driver.page_source.lower(): - # Allow time to enter 2FA code - print("Pausing to enter 2FA code") - time.sleep(20) - print("Continuing execution") + + 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): -- 2.30.2 From 1770fa47aabd93774e96e4bf9c7309fa5b7391a4 Mon Sep 17 00:00:00 2001 From: Wesley Kerfoot Date: Fri, 24 May 2019 20:49:42 -0400 Subject: [PATCH 07/93] 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 -- 2.30.2 From 5c111631804929f3e020ec3248863f5033b424e5 Mon Sep 17 00:00:00 2001 From: Wesley Kerfoot Date: Fri, 24 May 2019 21:02:52 -0400 Subject: [PATCH 08/93] Integrate changes that allow selecting year --- deletefb/deletefb.py | 14 ++++++++++++-- deletefb/tools/login.py | 2 -- deletefb/tools/wall.py | 10 +++++++++- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/deletefb/deletefb.py b/deletefb/deletefb.py index b6f3715..d3d3ddb 100755 --- a/deletefb/deletefb.py +++ b/deletefb/deletefb.py @@ -53,6 +53,15 @@ def run_delete(): help="Run browser in headless mode (no gui)" ) + parser.add_argument( + "-Y", + "--year", + required=False, + dest="year", + type=str, + help="The year(s) you want posts deleted." + ) + args = parser.parse_args() args_user_password = args.password or getpass.getpass('Enter your password: ') @@ -60,12 +69,13 @@ def run_delete(): driver = login( user_email_address=args.email, user_password=args_user_password, - user_profile_url=args.profile_url, is_headless=args.is_headless, two_factor_token=args.two_factor_token ) - wall.delete_posts(driver) + wall.delete_posts(driver, + args.profile_url, + year=args.year) if __name__ == "__main__": run_delete() diff --git a/deletefb/tools/login.py b/deletefb/tools/login.py index 38aa5bf..012253d 100644 --- a/deletefb/tools/login.py +++ b/deletefb/tools/login.py @@ -5,7 +5,6 @@ from seleniumrequests import Chrome def login(user_email_address, user_password, - user_profile_url, is_headless, two_factor_token): """ @@ -66,5 +65,4 @@ def login(user_email_address, 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 3005dd1..0017a4c 100644 --- a/deletefb/tools/wall.py +++ b/deletefb/tools/wall.py @@ -5,10 +5,18 @@ from .common import SELENIUM_EXCEPTIONS MAX_POSTS = 5000 -def delete_posts(driver): +def delete_posts(driver, + user_profile_url, + year=None): """ Deletes or hides all posts from the wall """ + + if not year is None: + user_profile_url = "{0}/timeline?year={1}".format(user_profile_url, year) + + driver.get(user_profile_url) + for _ in range(MAX_POSTS): post_button_sel = "_4xev" -- 2.30.2 From d0be69bbbb6c0249d6124c5a27aad04e6d7476a3 Mon Sep 17 00:00:00 2001 From: Wesley Kerfoot Date: Fri, 24 May 2019 21:32:51 -0400 Subject: [PATCH 09/93] (Hopefully) less buggy 2FA --- deletefb/tools/login.py | 48 +++++++++++++++++++++++++---------------- 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/deletefb/tools/login.py b/deletefb/tools/login.py index 012253d..ce0f424 100644 --- a/deletefb/tools/login.py +++ b/deletefb/tools/login.py @@ -2,6 +2,7 @@ import time from selenium.webdriver.chrome.options import Options from seleniumrequests import Chrome +from selenium.common.exceptions import NoSuchElementException def login(user_email_address, user_password, @@ -46,23 +47,34 @@ def login(user_email_address, 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") + # Defaults to no 2fa + has_2fa = False + + try: + # If this element exists, we've reached a 2FA page + driver.find_element_by_xpath("//form[@class=\"checkpoint\"]") + driver.find_element_by_xpath("//input[@name=\"approvals_code\"]") + has_2fa = True + except NoSuchElementException: + has_2fa = "two-factor authentication" in driver.page_source.lower() or has_2fa + + if two_factor_token and has_2fa: + 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() + elif has_2fa: + # Allow time to enter 2FA code + print("Pausing to enter 2FA code") + time.sleep(35) + print("Continuing execution") + else: + pass return driver -- 2.30.2 From 266b5697731660336e6e8e01b2d6d7dc4593a73c Mon Sep 17 00:00:00 2001 From: Wesley Kerfoot Date: Fri, 24 May 2019 22:09:25 -0400 Subject: [PATCH 10/93] Add a warning when 2FA is enabled --- deletefb/tools/login.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/deletefb/tools/login.py b/deletefb/tools/login.py index ce0f424..2dea088 100644 --- a/deletefb/tools/login.py +++ b/deletefb/tools/login.py @@ -58,6 +58,12 @@ def login(user_email_address, except NoSuchElementException: has_2fa = "two-factor authentication" in driver.page_source.lower() or has_2fa + if has_2fa: + print(""" + Two-Factor Auth is enabled. + Please file an issue at https://github.com/weskerfoot/DeleteFB/issues if you run into any problems + """) + if two_factor_token and has_2fa: twofactorelement = driver.find_element_by_name(approvals_code) twofactorelement.send_keys(two_factor_token) -- 2.30.2 From db015cc28183545185c2e2ed48ab788e7d48d3b1 Mon Sep 17 00:00:00 2001 From: Wesley Kerfoot Date: Sat, 25 May 2019 14:07:12 -0400 Subject: [PATCH 11/93] Add new `mode` parameter, work on unliking pages --- deletefb/deletefb.py | 26 +++++++++++++++++++++++--- deletefb/tools/likes.py | 20 ++++++++++++++++++-- 2 files changed, 41 insertions(+), 5 deletions(-) diff --git a/deletefb/deletefb.py b/deletefb/deletefb.py index d3d3ddb..b51915f 100755 --- a/deletefb/deletefb.py +++ b/deletefb/deletefb.py @@ -3,11 +3,25 @@ import argparse import getpass +from sys import exit from deletefb.tools.login import login import deletefb.tools.wall as wall +import deletefb.tools.likes as likes def run_delete(): parser = argparse.ArgumentParser() + + parser.add_argument( + "-M", + "--mode", + required=False, + default="wall", + dest="mode", + type=str, + choices=["wall", "unlike_pages"], + help="The mode you want to run in. Default is `wall' which deletes wall posts" + ) + parser.add_argument( "-E", "--email", @@ -73,9 +87,15 @@ def run_delete(): two_factor_token=args.two_factor_token ) - wall.delete_posts(driver, - args.profile_url, - year=args.year) + if args.mode == "wall": + wall.delete_posts(driver, + args.profile_url, + year=args.year) + elif args.mode == "unlike_pages": + likes.unlike_pages(driver, args.profile_url) + else: + print("Please enter a valid mode") + exit(1) if __name__ == "__main__": run_delete() diff --git a/deletefb/tools/likes.py b/deletefb/tools/likes.py index 80c6b0a..edc1669 100644 --- a/deletefb/tools/likes.py +++ b/deletefb/tools/likes.py @@ -1,8 +1,24 @@ +from time import sleep from selenium.webdriver.common.action_chains import ActionChains from .common import SELENIUM_EXCEPTIONS -def unlike_pages(driver): +def unlike_pages(driver, + user_profile_url): """ Unlike all pages """ - return + + actions = ActionChains(driver) + + driver.get("https://www.facebook.com/pages/?category=liked") + + pages_list = driver.find_element_by_css_selector("#all_liked_pages") + + actions.move_to_element(pages_list) + + unlike_buttons = pages_list.find_elements_by_xpath("//button") + + for button in unlike_buttons: + print(button) + + sleep(1000) -- 2.30.2 From e10f57751dcf0e28c8d7cf83ac1339a444c20a24 Mon Sep 17 00:00:00 2001 From: Wesley Kerfoot Date: Sun, 26 May 2019 12:42:24 -0400 Subject: [PATCH 12/93] Updates to unlikes (not quite working yet) --- deletefb/tools/common.py | 29 +++++++++++++++++++++++++++++ deletefb/tools/likes.py | 28 ++++++++++++++++++++++++---- 2 files changed, 53 insertions(+), 4 deletions(-) diff --git a/deletefb/tools/common.py b/deletefb/tools/common.py index 92405c4..0bf3df1 100644 --- a/deletefb/tools/common.py +++ b/deletefb/tools/common.py @@ -1,3 +1,32 @@ from selenium.common.exceptions import NoSuchElementException, StaleElementReferenceException +from time import sleep +from json import dumps +from os.path import abspath, relpath, split SELENIUM_EXCEPTIONS = (NoSuchElementException, StaleElementReferenceException) + +def try_move(actions, el): + for _ in range(10): + try: + actions.move_to_element(el).perform() + except StaleElementReferenceException: + sleep(5) + continue + + +def archiver(category): + """ + category: the category of logs you want to log + return values: (log_file_handle, archiver) + + call archiver like archive("some content") + """ + log_path = abspath(relpath(split(category)[-1], ".")) + + log_file = open(log_path, mode="wt", buffering=1) + + def log(content): + structured_content = {"category" : category, "content" : content} + log_file.write("{0}\n".format(dumps(structured_content))) + + return (log_file, log) diff --git a/deletefb/tools/likes.py b/deletefb/tools/likes.py index edc1669..ed01fff 100644 --- a/deletefb/tools/likes.py +++ b/deletefb/tools/likes.py @@ -1,6 +1,9 @@ from time import sleep +from selenium.webdriver.common.by import By from selenium.webdriver.common.action_chains import ActionChains -from .common import SELENIUM_EXCEPTIONS +from selenium.webdriver.support.ui import WebDriverWait +from selenium.webdriver.support import expected_conditions as EC +from .common import SELENIUM_EXCEPTIONS, archiver def unlike_pages(driver, user_profile_url): @@ -8,17 +11,34 @@ def unlike_pages(driver, Unlike all pages """ + like_log, archive_likes = archiver("likes") + actions = ActionChains(driver) driver.get("https://www.facebook.com/pages/?category=liked") - pages_list = driver.find_element_by_css_selector("#all_liked_pages") + wait = WebDriverWait(driver, 20) - actions.move_to_element(pages_list) + wait.until( + EC.presence_of_element_located((By.XPATH, "//div[text()='Liked']")) + ) + sleep(10) + + pages_list = driver.find_element_by_css_selector("#all_liked_pages") + actions.move_to_element(pages_list).perform() unlike_buttons = pages_list.find_elements_by_xpath("//button") for button in unlike_buttons: + try: + actions.move_to_element(button).perform() + page_name = button.find_element_by_xpath("./../..").text.split("\n")[0] + archive_likes(page_name) + + except SELENIUM_EXCEPTIONS as e: + print(e) + continue print(button) - sleep(1000) + # Explicitly close the log file when we're done with it + like_log.close() -- 2.30.2 From cc6886112c0a5508fc8d47125e6e989c10239cff Mon Sep 17 00:00:00 2001 From: Wesley Kerfoot Date: Sun, 26 May 2019 12:47:24 -0400 Subject: [PATCH 13/93] Bump version number --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 745c8c5..eee8a36 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ with open("README.md", "r") as fh: setuptools.setup( name="delete-facebook-posts", - version="1.0.4", + version="1.0.5", author="Wesley Kerfoot", author_email="wes@wesk.tech", description="A Selenium Script to Delete Facebook Posts", -- 2.30.2 From fddd373d142a36f0161406b7975fb920632bf88f Mon Sep 17 00:00:00 2001 From: Wesley Kerfoot Date: Sun, 26 May 2019 15:08:32 -0400 Subject: [PATCH 14/93] Bug the user if they do not have chromedriver installed --- deletefb/tools/common.py | 6 ++++++ deletefb/tools/login.py | 12 +++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/deletefb/tools/common.py b/deletefb/tools/common.py index 0bf3df1..4b6cbea 100644 --- a/deletefb/tools/common.py +++ b/deletefb/tools/common.py @@ -30,3 +30,9 @@ def archiver(category): log_file.write("{0}\n".format(dumps(structured_content))) return (log_file, log) + + +no_chrome_driver = """ +You need to install the chromedriver for Selenium\n +Please see this link https://github.com/weskerfoot/DeleteFB#how-to-use-it\n +""" diff --git a/deletefb/tools/login.py b/deletefb/tools/login.py index 2dea088..0a1ddd7 100644 --- a/deletefb/tools/login.py +++ b/deletefb/tools/login.py @@ -1,8 +1,10 @@ import time +from sys import stderr, exit from selenium.webdriver.chrome.options import Options from seleniumrequests import Chrome from selenium.common.exceptions import NoSuchElementException +from .common import no_chrome_driver def login(user_email_address, user_password, @@ -28,7 +30,15 @@ def login(user_email_address, chrome_options.add_argument('--disable-gpu') chrome_options.add_argument('log-level=2') - driver = Chrome(options=chrome_options) + try: + driver = Chrome(options=chrome_options) + except Exception as e: + # The user does not have chromedriver installed + # Tell them to install it + stderr.write(str(e)) + stderr.write(no_chrome_driver) + exit(1) + driver.implicitly_wait(10) driver.get("https://facebook.com") -- 2.30.2 From a26fcf42f3e6a4407c864088848d33341b9e74d0 Mon Sep 17 00:00:00 2001 From: Wesley Kerfoot Date: Sun, 26 May 2019 16:41:31 -0400 Subject: [PATCH 15/93] Unliking pages working --- deletefb/tools/common.py | 8 +++-- deletefb/tools/likes.py | 66 ++++++++++++++++++++++++++++------------ 2 files changed, 53 insertions(+), 21 deletions(-) diff --git a/deletefb/tools/common.py b/deletefb/tools/common.py index 4b6cbea..29d9341 100644 --- a/deletefb/tools/common.py +++ b/deletefb/tools/common.py @@ -1,9 +1,13 @@ -from selenium.common.exceptions import NoSuchElementException, StaleElementReferenceException +from selenium.common.exceptions import (NoSuchElementException, + StaleElementReferenceException, + TimeoutException) from time import sleep from json import dumps from os.path import abspath, relpath, split -SELENIUM_EXCEPTIONS = (NoSuchElementException, StaleElementReferenceException) +SELENIUM_EXCEPTIONS = (NoSuchElementException, + StaleElementReferenceException, + TimeoutException) def try_move(actions, el): for _ in range(10): diff --git a/deletefb/tools/likes.py b/deletefb/tools/likes.py index ed01fff..e74ec7a 100644 --- a/deletefb/tools/likes.py +++ b/deletefb/tools/likes.py @@ -1,10 +1,28 @@ -from time import sleep from selenium.webdriver.common.by import By from selenium.webdriver.common.action_chains import ActionChains from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from .common import SELENIUM_EXCEPTIONS, archiver +def load_likes(driver): + """ + Loads the page that lists all pages you like + """ + driver.get("https://www.facebook.com/pages/?category=liked") + + wait = WebDriverWait(driver, 20) + + try: + wait.until( + EC.presence_of_element_located((By.XPATH, "//button/div/div[text()='Liked']")) + ) + + wait.until( + EC.presence_of_element_located((By.XPATH, "//button/div/i[@aria-hidden=\"true\"]")) + ) + except SELENIUM_EXCEPTIONS: + return + def unlike_pages(driver, user_profile_url): """ @@ -15,30 +33,40 @@ def unlike_pages(driver, actions = ActionChains(driver) - driver.get("https://www.facebook.com/pages/?category=liked") + load_likes(driver) - wait = WebDriverWait(driver, 20) + pages_list = driver.find_element_by_css_selector("#all_liked_pages") - wait.until( - EC.presence_of_element_located((By.XPATH, "//div[text()='Liked']")) - ) + actions.move_to_element(pages_list).perform() - sleep(10) + unlike_buttons = pages_list.find_elements_by_xpath("//button/div/div[text()='Liked']/../..") - pages_list = driver.find_element_by_css_selector("#all_liked_pages") - actions.move_to_element(pages_list).perform() - unlike_buttons = pages_list.find_elements_by_xpath("//button") + while unlike_buttons: + for button in unlike_buttons: + try: + if "Liked" in button.text: + page_name = button.find_element_by_xpath("./../..").text.split("\n")[0] + + print("Trying to unlike {0}".format(page_name)) + + driver.execute_script("arguments[0].click();", button) + + archive_likes(page_name) + + print("{0} was unliked".format(page_name)) + + except SELENIUM_EXCEPTIONS as e: + continue - for button in unlike_buttons: + load_likes(driver) try: - actions.move_to_element(button).perform() - page_name = button.find_element_by_xpath("./../..").text.split("\n")[0] - archive_likes(page_name) - - except SELENIUM_EXCEPTIONS as e: - print(e) - continue - print(button) + pages_list = driver.find_element_by_css_selector("#all_liked_pages") + actions.move_to_element(pages_list).perform() + unlike_buttons = pages_list.find_elements_by_xpath("//button") + if not unlike_buttons: + break + except SELENIUM_EXCEPTIONS: + break # Explicitly close the log file when we're done with it like_log.close() -- 2.30.2 From 6bfd72912539ef7298c55d807de40fe2e9127c98 Mon Sep 17 00:00:00 2001 From: Wesley Kerfoot Date: Sun, 26 May 2019 16:45:02 -0400 Subject: [PATCH 16/93] Remove unnecessary parameter --- deletefb/deletefb.py | 2 +- deletefb/tools/likes.py | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/deletefb/deletefb.py b/deletefb/deletefb.py index b51915f..4507c68 100755 --- a/deletefb/deletefb.py +++ b/deletefb/deletefb.py @@ -92,7 +92,7 @@ def run_delete(): args.profile_url, year=args.year) elif args.mode == "unlike_pages": - likes.unlike_pages(driver, args.profile_url) + likes.unlike_pages(driver) else: print("Please enter a valid mode") exit(1) diff --git a/deletefb/tools/likes.py b/deletefb/tools/likes.py index e74ec7a..6914145 100644 --- a/deletefb/tools/likes.py +++ b/deletefb/tools/likes.py @@ -23,8 +23,7 @@ def load_likes(driver): except SELENIUM_EXCEPTIONS: return -def unlike_pages(driver, - user_profile_url): +def unlike_pages(driver): """ Unlike all pages """ @@ -47,8 +46,6 @@ def unlike_pages(driver, if "Liked" in button.text: page_name = button.find_element_by_xpath("./../..").text.split("\n")[0] - print("Trying to unlike {0}".format(page_name)) - driver.execute_script("arguments[0].click();", button) archive_likes(page_name) -- 2.30.2 From 9bb624eaa5d89c614b6a79753a2abbe4fab0a0a3 Mon Sep 17 00:00:00 2001 From: Wesley Kerfoot Date: Sun, 26 May 2019 16:51:26 -0400 Subject: [PATCH 17/93] Guard against invalid combos of options --- deletefb/deletefb.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/deletefb/deletefb.py b/deletefb/deletefb.py index 4507c68..da96208 100755 --- a/deletefb/deletefb.py +++ b/deletefb/deletefb.py @@ -78,6 +78,9 @@ def run_delete(): args = parser.parse_args() + if args.year and args.mode != "wall": + parser.error("The --year option is only supported in wall mode") + args_user_password = args.password or getpass.getpass('Enter your password: ') driver = login( -- 2.30.2 From 89a6c2de1deb2109fb5e844d3082fe5af3cc7fc3 Mon Sep 17 00:00:00 2001 From: Wesley Kerfoot Date: Mon, 27 May 2019 01:28:47 -0400 Subject: [PATCH 18/93] Use relative imports to allow direct execution --- deletefb/deletefb.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/deletefb/deletefb.py b/deletefb/deletefb.py index b6f3715..b52f956 100755 --- a/deletefb/deletefb.py +++ b/deletefb/deletefb.py @@ -3,8 +3,8 @@ import argparse import getpass -from deletefb.tools.login import login -import deletefb.tools.wall as wall +from .tools.login import login +from .tools.wall import delete_posts def run_delete(): parser = argparse.ArgumentParser() @@ -65,7 +65,7 @@ def run_delete(): two_factor_token=args.two_factor_token ) - wall.delete_posts(driver) + delete_posts(driver) if __name__ == "__main__": run_delete() -- 2.30.2 From 06d7c5a45da666e109fd7ee1c2465ca613dc5f1c Mon Sep 17 00:00:00 2001 From: Wesley Kerfoot Date: Mon, 27 May 2019 01:29:35 -0400 Subject: [PATCH 19/93] Change how it is executed directly in a venv --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index fca93c9..9db2e87 100644 --- a/README.md +++ b/README.md @@ -23,8 +23,7 @@ You have several options to run it. 2) Clone this repo and run `pip install --user .` or do `pip install --user git+https://github.com/weskerfoot/DeleteFB.git` 3) Set up a Python virtualenv, activate it, and run `pip install -r -requirements.txt`, then you can just run `python deletefb/deletefb.py` as you -would if you had installed it from PyPI. +requirements.txt`, then you can just run `python -m deletefb.deletefb.py` in the project directory, as you would if you had installed it from PyPI. ## How To Use It -- 2.30.2 From f20c4c6283cda14637b8d2119f91fd2bab6adc30 Mon Sep 17 00:00:00 2001 From: Wesley Kerfoot Date: Mon, 27 May 2019 01:44:03 -0400 Subject: [PATCH 20/93] Clarify chromedriver instructions --- README.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 9db2e87..54f2497 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,16 @@ requirements.txt`, then you can just run `python -m deletefb.deletefb.py` in the ## How To Use It * Make sure that you have Google Chrome installed and that it is up to date -* Also install the chromedriver for Selenium. See [here](https://sites.google.com/a/chromium.org/chromedriver/downloads). On Arch Linux you can find this in the `chromium` package, and on Ubuntu it is `chromium-chromedriver`. +* Also install the chromedriver for Selenium. See [here](https://sites.google.com/a/chromium.org/chromedriver/downloads). + * On Linux, it will be called something like `chromium-chromedriver` or just + `chromium`. + * On MacOS, it will be available via brew, with the following commands: + + ``` + brew tap homebrew/cask; + brew cask install chromedriver + ``` + * Run `deletefb -E "youremail@example.org" -P "yourfacebookpassword" -U "https://www.facebook.com/your.profile.url"` * The script will log into your Facebook account, go to your profile page, and start deleting posts. If it cannot delete something, then it will "hide" it -- 2.30.2 From 6fcb0adb0bf9914af77e14a142d2891eb5f3e83a Mon Sep 17 00:00:00 2001 From: Wesley Kerfoot Date: Mon, 27 May 2019 01:44:53 -0400 Subject: [PATCH 21/93] Clarify chromedriver more --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 54f2497..79cc5ea 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ requirements.txt`, then you can just run `python -m deletefb.deletefb.py` in the ## How To Use It * Make sure that you have Google Chrome installed and that it is up to date -* Also install the chromedriver for Selenium. See [here](https://sites.google.com/a/chromium.org/chromedriver/downloads). +* Also install the chromedriver for Selenium. See [here](https://sites.google.com/a/chromium.org/chromedriver/downloads) for an explanation of what the chromedriver does. * On Linux, it will be called something like `chromium-chromedriver` or just `chromium`. * On MacOS, it will be available via brew, with the following commands: -- 2.30.2 From 565e01f6d0499f2b405ef50c006d772068e253fe Mon Sep 17 00:00:00 2001 From: Wesley Kerfoot Date: Mon, 27 May 2019 01:46:08 -0400 Subject: [PATCH 22/93] Update link to chromedriver to point to about page --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 79cc5ea..aa9e64a 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ requirements.txt`, then you can just run `python -m deletefb.deletefb.py` in the ## How To Use It * Make sure that you have Google Chrome installed and that it is up to date -* Also install the chromedriver for Selenium. See [here](https://sites.google.com/a/chromium.org/chromedriver/downloads) for an explanation of what the chromedriver does. +* Also install the chromedriver for Selenium. See [here](https://sites.google.com/a/chromium.org/chromedriver/home) for an explanation of what the chromedriver does. * On Linux, it will be called something like `chromium-chromedriver` or just `chromium`. * On MacOS, it will be available via brew, with the following commands: -- 2.30.2 From 99a30df3de327ec44672b704c0e992c0f614db11 Mon Sep 17 00:00:00 2001 From: Wesley Kerfoot Date: Mon, 27 May 2019 01:52:25 -0400 Subject: [PATCH 23/93] Remove stray import --- deletefb/deletefb.py | 1 - 1 file changed, 1 deletion(-) diff --git a/deletefb/deletefb.py b/deletefb/deletefb.py index 72d223a..0e91240 100755 --- a/deletefb/deletefb.py +++ b/deletefb/deletefb.py @@ -4,7 +4,6 @@ import argparse import getpass from sys import exit -from deletefb.tools.login import login from .tools.login import login from .tools.wall import delete_posts from .tools.likes import unlike_pages -- 2.30.2 From 643929d3dd96048ef3e15d89d124f1f4293aa3a0 Mon Sep 17 00:00:00 2001 From: Wesley Kerfoot Date: Tue, 28 May 2019 01:21:51 -0400 Subject: [PATCH 24/93] Archive wall posts, include timestamp --- deletefb/tools/common.py | 11 ++++++++--- deletefb/tools/wall.py | 16 ++++++++++++++-- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/deletefb/tools/common.py b/deletefb/tools/common.py index 29d9341..4b95ea1 100644 --- a/deletefb/tools/common.py +++ b/deletefb/tools/common.py @@ -25,12 +25,17 @@ def archiver(category): call archiver like archive("some content") """ - log_path = abspath(relpath(split(category)[-1], ".")) + log_path = "{0}.log".format(abspath(relpath(split(category)[-1], "."))) log_file = open(log_path, mode="wt", buffering=1) - def log(content): - structured_content = {"category" : category, "content" : content} + def log(content, timestamp=False): + structured_content = { + "category" : category, + "content" : content, + "timestamp" : timestamp + } + log_file.write("{0}\n".format(dumps(structured_content))) return (log_file, log) diff --git a/deletefb/tools/wall.py b/deletefb/tools/wall.py index 0017a4c..a318c54 100644 --- a/deletefb/tools/wall.py +++ b/deletefb/tools/wall.py @@ -1,9 +1,10 @@ import time from selenium.webdriver.common.action_chains import ActionChains -from .common import SELENIUM_EXCEPTIONS +from .common import SELENIUM_EXCEPTIONS, archiver -MAX_POSTS = 5000 +# Used as a threshold to avoid running forever +MAX_POSTS = 15000 def delete_posts(driver, user_profile_url, @@ -17,12 +18,23 @@ def delete_posts(driver, driver.get(user_profile_url) + wall_log, archive_wall_post = archiver("wall") + for _ in range(MAX_POSTS): post_button_sel = "_4xev" + post_content_sel = "_5_jv" + + post_timestamp_sel = "timestamp" while True: try: timeline_element = driver.find_element_by_class_name(post_button_sel) + + post_content_element = driver.find_element_by_class_name(post_content_sel) + post_content_ts = driver.find_element_by_class_name(post_timestamp_sel) + + archive_wall_post(post_content_element.text, timestamp=post_content_ts.text) + actions = ActionChains(driver) actions.move_to_element(timeline_element).click().perform() -- 2.30.2 From 056fd75aae9983285d27bfd3d5449c311d566ff7 Mon Sep 17 00:00:00 2001 From: Wesley Kerfoot Date: Tue, 28 May 2019 01:46:01 -0400 Subject: [PATCH 25/93] Add feature to disable logging --- README.md | 13 +++++++++++++ deletefb/deletefb.py | 12 ++++++++++++ deletefb/tools/common.py | 3 +++ 3 files changed, 28 insertions(+) diff --git a/README.md b/README.md index aa9e64a..b6eaa6b 100644 --- a/README.md +++ b/README.md @@ -49,11 +49,24 @@ requirements.txt`, then you can just run `python -m deletefb.deletefb.py` in the * 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 run into issues with Facebook complaining about your browser, + currently the only workaround is to manually click through them. + * 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`. +## Delete by year +* The tool supports passing the `--year` flag in order to delete wall posts by + year. It is incompatible with any mode other than `wall`. + +## Archival +* The tool will archive everything being deleted by default in `.log` files. + Currently they are simply stored as JSON objects for each line in the log. It + will archive the content, and a timestamp if it is available. You may disable + this feature by using `--no-archive`. + ## 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. diff --git a/deletefb/deletefb.py b/deletefb/deletefb.py index 0e91240..23e275d 100755 --- a/deletefb/deletefb.py +++ b/deletefb/deletefb.py @@ -4,6 +4,7 @@ import argparse import getpass from sys import exit +from os import environ from .tools.login import login from .tools.wall import delete_posts from .tools.likes import unlike_pages @@ -67,6 +68,14 @@ def run_delete(): help="Run browser in headless mode (no gui)" ) + parser.add_argument( + "--no-archive", + action="store_true", + dest="archive_off", + default=True, + help="Turn off archiving (on by default)" + ) + parser.add_argument( "-Y", "--year", @@ -78,6 +87,9 @@ def run_delete(): args = parser.parse_args() + if args.archive_off: + environ["DELETEFB_ARCHIVE"] = "false" if args.archive_off else "true" + if args.year and args.mode != "wall": parser.error("The --year option is only supported in wall mode") diff --git a/deletefb/tools/common.py b/deletefb/tools/common.py index 4b95ea1..9dc9e38 100644 --- a/deletefb/tools/common.py +++ b/deletefb/tools/common.py @@ -4,6 +4,7 @@ from selenium.common.exceptions import (NoSuchElementException, from time import sleep from json import dumps from os.path import abspath, relpath, split +from os import environ SELENIUM_EXCEPTIONS = (NoSuchElementException, StaleElementReferenceException, @@ -30,6 +31,8 @@ def archiver(category): log_file = open(log_path, mode="wt", buffering=1) def log(content, timestamp=False): + if environ.get("DELETEFB_ARCHIVE", "true") == "false": + return structured_content = { "category" : category, "content" : content, -- 2.30.2 From 39ddca80c55fd3f8b4ae2c71f3f00b2d8f796f77 Mon Sep 17 00:00:00 2001 From: Wesley Kerfoot Date: Tue, 28 May 2019 01:48:35 -0400 Subject: [PATCH 26/93] Update readme with info about unliking pages --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index b6eaa6b..d210890 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,11 @@ requirements.txt`, then you can just run `python -m deletefb.deletefb.py` in the * The tool supports passing the `--year` flag in order to delete wall posts by year. It is incompatible with any mode other than `wall`. +## Unlike Pages +* You may use `-M unlike_pages` to unlike all of your pages. The names of the + pages will be archived (unless archival is turned off), and this option + conflicts with the year option. + ## Archival * The tool will archive everything being deleted by default in `.log` files. Currently they are simply stored as JSON objects for each line in the log. It -- 2.30.2 From 75dbf645db5825de3f010b2a50d9332f03e7502b Mon Sep 17 00:00:00 2001 From: Wesley Kerfoot Date: Tue, 28 May 2019 01:50:03 -0400 Subject: [PATCH 27/93] Clean up README some more --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d210890..da85a81 100644 --- a/README.md +++ b/README.md @@ -57,9 +57,9 @@ requirements.txt`, then you can just run `python -m deletefb.deletefb.py` in the * You may also pass in a code by using the `-F` argument, e.g. `-F 111111`. -## Delete by year +## Delete By Year * The tool supports passing the `--year` flag in order to delete wall posts by - year. It is incompatible with any mode other than `wall`. + year. E.g. `-Y 2010` would delete posts from the year 2010. It is incompatible with any mode other than `wall`. ## Unlike Pages * You may use `-M unlike_pages` to unlike all of your pages. The names of the -- 2.30.2 From 24adaa47a9312f1bd08704de44273bb0f91c5d29 Mon Sep 17 00:00:00 2001 From: Wesley Kerfoot Date: Tue, 28 May 2019 01:50:22 -0400 Subject: [PATCH 28/93] New minor release --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index eee8a36..9ff96c8 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ with open("README.md", "r") as fh: setuptools.setup( name="delete-facebook-posts", - version="1.0.5", + version="1.1.0", author="Wesley Kerfoot", author_email="wes@wesk.tech", description="A Selenium Script to Delete Facebook Posts", -- 2.30.2 From a469b8f5f7dc94ad2b189900efbfae80e047238d Mon Sep 17 00:00:00 2001 From: Wesley Kerfoot Date: Tue, 28 May 2019 02:06:22 -0400 Subject: [PATCH 29/93] correct 2fa timeout --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index da85a81..4b2b4c3 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ requirements.txt`, then you can just run `python -m deletefb.deletefb.py` in the * If you run into issues with Facebook complaining about your browser, currently the only workaround is to manually click through them. -* If you do have 2-Factor Auth configured then the script will pause for 20 +* If you do have 2-Factor Auth configured then the script will pause for 35 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`. -- 2.30.2 From 1241d6e7e89863d95f037c350722dd52a6aa44f8 Mon Sep 17 00:00:00 2001 From: Wesley Kerfoot Date: Wed, 29 May 2019 00:08:31 -0400 Subject: [PATCH 30/93] Fix bugs with logging and bump version --- deletefb/deletefb.py | 2 +- deletefb/tools/common.py | 2 +- deletefb/tools/wall.py | 9 +++++---- setup.py | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/deletefb/deletefb.py b/deletefb/deletefb.py index 23e275d..e7cc186 100755 --- a/deletefb/deletefb.py +++ b/deletefb/deletefb.py @@ -72,7 +72,7 @@ def run_delete(): "--no-archive", action="store_true", dest="archive_off", - default=True, + default=False, help="Turn off archiving (on by default)" ) diff --git a/deletefb/tools/common.py b/deletefb/tools/common.py index 9dc9e38..50fb71d 100644 --- a/deletefb/tools/common.py +++ b/deletefb/tools/common.py @@ -28,7 +28,7 @@ def archiver(category): """ log_path = "{0}.log".format(abspath(relpath(split(category)[-1], "."))) - log_file = open(log_path, mode="wt", buffering=1) + log_file = open(log_path, mode="ta", buffering=1) def log(content, timestamp=False): if environ.get("DELETEFB_ARCHIVE", "true") == "false": diff --git a/deletefb/tools/wall.py b/deletefb/tools/wall.py index a318c54..0264841 100644 --- a/deletefb/tools/wall.py +++ b/deletefb/tools/wall.py @@ -18,13 +18,13 @@ def delete_posts(driver, driver.get(user_profile_url) - wall_log, archive_wall_post = archiver("wall") - for _ in range(MAX_POSTS): post_button_sel = "_4xev" - post_content_sel = "_5_jv" - post_timestamp_sel = "timestamp" + post_content_sel = "userContent" + post_timestamp_sel = "timestampContent" + + wall_log, archive_wall_post = archiver("wall") while True: try: @@ -55,6 +55,7 @@ def delete_posts(driver, continue else: break + wall_log.close() # Required to sleep the thread for a bit after using JS to click this button time.sleep(5) diff --git a/setup.py b/setup.py index 9ff96c8..7e3f445 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ with open("README.md", "r") as fh: setuptools.setup( name="delete-facebook-posts", - version="1.1.0", + version="1.1.1", author="Wesley Kerfoot", author_email="wes@wesk.tech", description="A Selenium Script to Delete Facebook Posts", -- 2.30.2 From 4401318b2a5b2463c3f787c4ad9d4a0e476f1060 Mon Sep 17 00:00:00 2001 From: Wesley Kerfoot Date: Wed, 29 May 2019 00:14:32 -0400 Subject: [PATCH 31/93] Clarify archiving logic in setup --- deletefb/deletefb.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/deletefb/deletefb.py b/deletefb/deletefb.py index e7cc186..6f670ca 100755 --- a/deletefb/deletefb.py +++ b/deletefb/deletefb.py @@ -88,7 +88,10 @@ def run_delete(): args = parser.parse_args() if args.archive_off: - environ["DELETEFB_ARCHIVE"] = "false" if args.archive_off else "true" + environ["DELETEFB_ARCHIVE"] = "false" + else: + environ["DELETEFB_ARCHIVE"] = "true" + if args.year and args.mode != "wall": parser.error("The --year option is only supported in wall mode") -- 2.30.2 From e76266f966eb4ddbdcc5768552603fc56b358d6e Mon Sep 17 00:00:00 2001 From: Wesley Kerfoot Date: Thu, 30 May 2019 11:06:06 -0400 Subject: [PATCH 32/93] Try to use untag button if delete or hide is not an option --- deletefb/tools/wall.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/deletefb/tools/wall.py b/deletefb/tools/wall.py index 0264841..4af01d0 100644 --- a/deletefb/tools/wall.py +++ b/deletefb/tools/wall.py @@ -44,7 +44,10 @@ def delete_posts(driver, 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\"]") + try: + delete_button = menu.find_element_by_xpath("//a[@data-feed-option-name=\"HIDE_FROM_TIMELINE\"]") + except SELENIUM_EXCEPTIONS: + delete_button = menu.find_element_by_xpath("//a[@data-feed-option-name=\"UNTAG\"]") actions.move_to_element(delete_button).click().perform() confirmation_button = driver.find_element_by_class_name("layerConfirm") -- 2.30.2 From 8bf8617c15f4d6a0b4cff7edc1fcb93bae115111 Mon Sep 17 00:00:00 2001 From: ConnorSkees <39542938+ConnorSkees@users.noreply.github.com> Date: Fri, 31 May 2019 19:57:55 -0400 Subject: [PATCH 33/93] Refactor --- deletefb/deletefb.py | 12 +++++----- deletefb/tools/common.py | 49 ++++++++++++++++++++++++---------------- deletefb/tools/likes.py | 16 ++++++++++++- deletefb/tools/login.py | 28 ++++++++++++++--------- deletefb/tools/wall.py | 9 +++++++- 5 files changed, 75 insertions(+), 39 deletions(-) diff --git a/deletefb/deletefb.py b/deletefb/deletefb.py index 6f670ca..b6cc483 100755 --- a/deletefb/deletefb.py +++ b/deletefb/deletefb.py @@ -1,10 +1,10 @@ -#! /usr/bin/env python +#!/usr/bin/env python import argparse import getpass +import sys +import os -from sys import exit -from os import environ from .tools.login import login from .tools.wall import delete_posts from .tools.likes import unlike_pages @@ -88,9 +88,9 @@ def run_delete(): args = parser.parse_args() if args.archive_off: - environ["DELETEFB_ARCHIVE"] = "false" + os.environ["DELETEFB_ARCHIVE"] = "false" else: - environ["DELETEFB_ARCHIVE"] = "true" + os.environ["DELETEFB_ARCHIVE"] = "true" if args.year and args.mode != "wall": @@ -113,7 +113,7 @@ def run_delete(): unlike_pages(driver) else: print("Please enter a valid mode") - exit(1) + sys.exit(1) if __name__ == "__main__": run_delete() diff --git a/deletefb/tools/common.py b/deletefb/tools/common.py index 50fb71d..e086df6 100644 --- a/deletefb/tools/common.py +++ b/deletefb/tools/common.py @@ -1,50 +1,59 @@ -from selenium.common.exceptions import (NoSuchElementException, - StaleElementReferenceException, - TimeoutException) -from time import sleep -from json import dumps +import json +import os from os.path import abspath, relpath, split -from os import environ +import time -SELENIUM_EXCEPTIONS = (NoSuchElementException, - StaleElementReferenceException, - TimeoutException) +from selenium.common.exceptions import ( + NoSuchElementException, + StaleElementReferenceException, + TimeoutException +) + + +SELENIUM_EXCEPTIONS = ( + NoSuchElementException, + StaleElementReferenceException, + TimeoutException +) def try_move(actions, el): for _ in range(10): try: actions.move_to_element(el).perform() except StaleElementReferenceException: - sleep(5) + time.sleep(5) continue def archiver(category): """ - category: the category of logs you want to log - return values: (log_file_handle, archiver) + Log content to file. Call using `archive("some content")` + + Args: + category: str The category of logs you want to log - call archiver like archive("some content") + Returns: + (log_file_handle, archiver) """ log_path = "{0}.log".format(abspath(relpath(split(category)[-1], "."))) log_file = open(log_path, mode="ta", buffering=1) def log(content, timestamp=False): - if environ.get("DELETEFB_ARCHIVE", "true") == "false": + if os.environ.get("DELETEFB_ARCHIVE", "true") == "false": return structured_content = { - "category" : category, - "content" : content, - "timestamp" : timestamp - } + "category" : category, + "content" : content, + "timestamp" : timestamp + } - log_file.write("{0}\n".format(dumps(structured_content))) + log_file.write("{0}\n".format(json.dumps(structured_content))) return (log_file, log) -no_chrome_driver = """ +NO_CHROME_DRIVER = """ You need to install the chromedriver for Selenium\n Please see this link https://github.com/weskerfoot/DeleteFB#how-to-use-it\n """ diff --git a/deletefb/tools/likes.py b/deletefb/tools/likes.py index 6914145..abef381 100644 --- a/deletefb/tools/likes.py +++ b/deletefb/tools/likes.py @@ -2,11 +2,19 @@ from selenium.webdriver.common.by import By from selenium.webdriver.common.action_chains import ActionChains from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC + from .common import SELENIUM_EXCEPTIONS, archiver + def load_likes(driver): """ Loads the page that lists all pages you like + + Args: + driver: seleniumrequests.Chrome Driver instance + + Returns: + None """ driver.get("https://www.facebook.com/pages/?category=liked") @@ -26,6 +34,12 @@ def load_likes(driver): def unlike_pages(driver): """ Unlike all pages + + Args: + driver: seleniumrequests.Chrome Driver instance + + Returns: + None """ like_log, archive_likes = archiver("likes") @@ -52,7 +66,7 @@ def unlike_pages(driver): print("{0} was unliked".format(page_name)) - except SELENIUM_EXCEPTIONS as e: + except SELENIUM_EXCEPTIONS: continue load_likes(driver) diff --git a/deletefb/tools/login.py b/deletefb/tools/login.py index 0a1ddd7..847649d 100644 --- a/deletefb/tools/login.py +++ b/deletefb/tools/login.py @@ -1,10 +1,12 @@ import time +import sys -from sys import stderr, exit +from selenium.common.exceptions import NoSuchElementException from selenium.webdriver.chrome.options import Options from seleniumrequests import Chrome -from selenium.common.exceptions import NoSuchElementException -from .common import no_chrome_driver + +from .common import NO_CHROME_DRIVER + def login(user_email_address, user_password, @@ -14,9 +16,13 @@ def login(user_email_address, 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 + Args: + user_email_address: str Your email + user_password: str Your password + user_profile_url: str Your profile URL + + Returns: + seleniumrequests.Chrome instance """ # The Chrome driver is required because Gecko was having issues @@ -35,9 +41,9 @@ def login(user_email_address, except Exception as e: # The user does not have chromedriver installed # Tell them to install it - stderr.write(str(e)) - stderr.write(no_chrome_driver) - exit(1) + sys.stderr.write(str(e)) + sys.stderr.write(NO_CHROME_DRIVER) + sys.exit(1) driver.implicitly_wait(10) @@ -45,7 +51,7 @@ def login(user_email_address, email = "email" password = "pass" - login = "loginbutton" + login_button = "loginbutton" approvals_code = "approvals_code" emailelement = driver.find_element_by_name(email) @@ -54,7 +60,7 @@ def login(user_email_address, emailelement.send_keys(user_email_address) passwordelement.send_keys(user_password) - loginelement = driver.find_element_by_id(login) + loginelement = driver.find_element_by_id(login_button) loginelement.click() # Defaults to no 2fa diff --git a/deletefb/tools/wall.py b/deletefb/tools/wall.py index 0264841..26da422 100644 --- a/deletefb/tools/wall.py +++ b/deletefb/tools/wall.py @@ -1,19 +1,26 @@ import time from selenium.webdriver.common.action_chains import ActionChains + from .common import SELENIUM_EXCEPTIONS, archiver # Used as a threshold to avoid running forever MAX_POSTS = 15000 + def delete_posts(driver, user_profile_url, year=None): """ Deletes or hides all posts from the wall + + Args: + driver: seleniumrequests.Chrome Driver instance + user_profile_url: str + year: optional int YYYY year """ - if not year is None: + if year is not None: user_profile_url = "{0}/timeline?year={1}".format(user_profile_url, year) driver.get(user_profile_url) -- 2.30.2 From c89f7a1d6cfd1fa9264483321f55bdedc905e28c Mon Sep 17 00:00:00 2001 From: ConnorSkees <39542938+ConnorSkees@users.noreply.github.com> Date: Sat, 1 Jun 2019 10:05:39 -0400 Subject: [PATCH 34/93] Add basic logging --- deletefb/deletefb.log | 0 deletefb/deletefb.py | 14 ++++++--- deletefb/logging_conf.json | 62 ++++++++++++++++++++++++++++++++++++++ deletefb/tools/common.py | 13 +++++++- 4 files changed, 84 insertions(+), 5 deletions(-) create mode 100644 deletefb/deletefb.log create mode 100644 deletefb/logging_conf.json diff --git a/deletefb/deletefb.log b/deletefb/deletefb.log new file mode 100644 index 0000000..e69de29 diff --git a/deletefb/deletefb.py b/deletefb/deletefb.py index b6cc483..832280a 100755 --- a/deletefb/deletefb.py +++ b/deletefb/deletefb.py @@ -2,12 +2,18 @@ import argparse import getpass -import sys +import json import os +import sys + +from tools.common import logger +from tools.login import login +from tools.wall import delete_posts +from tools.likes import unlike_pages + + +LOG = logger(__name__) -from .tools.login import login -from .tools.wall import delete_posts -from .tools.likes import unlike_pages def run_delete(): parser = argparse.ArgumentParser() diff --git a/deletefb/logging_conf.json b/deletefb/logging_conf.json new file mode 100644 index 0000000..2078e80 --- /dev/null +++ b/deletefb/logging_conf.json @@ -0,0 +1,62 @@ +{ + "logging": { + "version": 1, + "disable_existing_loggers": true, + "formatters": { + "brief": { + "class": "logging.Formatter", + "style": "{", + "datefmt": "%H:%M:%S", + "format": "{name:s}-{levelname:s}-{asctime:s}-{message:s}" + }, + "verbose": { + "class": "logging.Formatter", + "style": "{", + "datefmt": "%Y-%m-%dT%H:%M:%S", + "format": "{name:s}:{levelname:s}:L{lineno:d} {asctime:s} {message:s}" + } + }, + "handlers": { + "console": { + "level": "DEBUG", + "class": "logging.StreamHandler", + "formatter": "brief", + "stream": "ext://sys.stdout" + }, + "file_handler": { + "level": "INFO", + "class": "logging.handlers.WatchedFileHandler", + "formatter": "verbose", + "filename": "deletefb/deletefb.log", + "mode": "a", + "encoding": "utf-8" + } + }, + "loggers": { + "root": { + "level": "DEBUG", + "handlers": ["console", "file_handler"] + }, + "deletefb": { + "level": "DEBUG", + "handlers": ["console"], + "propagate": false + }, + "login": { + "level": "DEBUG", + "handlers": ["file_handler"], + "propagate": false + }, + "likes": { + "level": "DEBUG", + "handlers": ["file_handler"], + "propagate": false + }, + "wall": { + "level": "DEBUG", + "handlers": ["file_handler"], + "propagate": false + } + } + } +} diff --git a/deletefb/tools/common.py b/deletefb/tools/common.py index e086df6..b50dc90 100644 --- a/deletefb/tools/common.py +++ b/deletefb/tools/common.py @@ -1,6 +1,8 @@ import json +import logging +import logging.config import os -from os.path import abspath, relpath, split +from os.path import abspath, relpath, split, isfile import time from selenium.common.exceptions import ( @@ -24,6 +26,15 @@ def try_move(actions, el): time.sleep(5) continue +def logger(name: str): + # called from directory (__main__.py) + config_path = "deletefb/logging_conf.json" + if not isfile(config_path): # called from file (deletefb.py) + os.chdir("..") + with open(config_path, "r", encoding="utf-8") as config_file: + config = json.load(config_file) + logging.config.dictConfig(config["logging"]) + return logging.getLogger("deletefb") def archiver(category): """ -- 2.30.2 From 5a42296b306576f7a94eaaab2599ae6bf89ff80d Mon Sep 17 00:00:00 2001 From: ConnorSkees <39542938+ConnorSkees@users.noreply.github.com> Date: Sat, 1 Jun 2019 10:06:09 -0400 Subject: [PATCH 35/93] Create __main__.py Allow for it to be run from outer directory with `python deletefb` --- deletefb/__main__.py | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 deletefb/__main__.py diff --git a/deletefb/__main__.py b/deletefb/__main__.py new file mode 100644 index 0000000..c735177 --- /dev/null +++ b/deletefb/__main__.py @@ -0,0 +1,5 @@ +#!/usr/bin/env python +import deletefb + +if __name__ == "__main__": + deletefb.run_delete() -- 2.30.2 From 6884c12c404b5225f112336f708cb08a8c192d52 Mon Sep 17 00:00:00 2001 From: ConnorSkees <39542938+ConnorSkees@users.noreply.github.com> Date: Sat, 1 Jun 2019 11:01:22 -0400 Subject: [PATCH 36/93] Add docstring and fix hardcoded strings --- deletefb/deletefb.py | 3 +-- deletefb/tools/common.py | 12 +++++++++--- deletefb/tools/likes.py | 5 ++++- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/deletefb/deletefb.py b/deletefb/deletefb.py index 832280a..54d0c66 100755 --- a/deletefb/deletefb.py +++ b/deletefb/deletefb.py @@ -11,8 +11,7 @@ from tools.login import login from tools.wall import delete_posts from tools.likes import unlike_pages - -LOG = logger(__name__) +LOG = logger("deletefb") def run_delete(): diff --git a/deletefb/tools/common.py b/deletefb/tools/common.py index b50dc90..fe97881 100644 --- a/deletefb/tools/common.py +++ b/deletefb/tools/common.py @@ -26,15 +26,21 @@ def try_move(actions, el): time.sleep(5) continue -def logger(name: str): - # called from directory (__main__.py) +def logger(name): + """ + Args: + name (str): Logger name + + Returns: + logging.Logger + """ config_path = "deletefb/logging_conf.json" if not isfile(config_path): # called from file (deletefb.py) os.chdir("..") with open(config_path, "r", encoding="utf-8") as config_file: config = json.load(config_file) logging.config.dictConfig(config["logging"]) - return logging.getLogger("deletefb") + return logging.getLogger(name) def archiver(category): """ diff --git a/deletefb/tools/likes.py b/deletefb/tools/likes.py index abef381..cbb8886 100644 --- a/deletefb/tools/likes.py +++ b/deletefb/tools/likes.py @@ -3,7 +3,9 @@ from selenium.webdriver.common.action_chains import ActionChains from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC -from .common import SELENIUM_EXCEPTIONS, archiver +from .common import SELENIUM_EXCEPTIONS, archiver, logger + +LOG = logger(__name__) def load_likes(driver): @@ -29,6 +31,7 @@ def load_likes(driver): EC.presence_of_element_located((By.XPATH, "//button/div/i[@aria-hidden=\"true\"]")) ) except SELENIUM_EXCEPTIONS: + LOG.exception("Traceback of load_likes") return def unlike_pages(driver): -- 2.30.2 From b7287634de00770d03aed4e4c9d38572d73993d3 Mon Sep 17 00:00:00 2001 From: ConnorSkees <39542938+ConnorSkees@users.noreply.github.com> Date: Sat, 1 Jun 2019 12:06:24 -0400 Subject: [PATCH 37/93] Update README.md --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 4b2b4c3..c882e17 100644 --- a/README.md +++ b/README.md @@ -22,8 +22,7 @@ You have several options to run it. 1) Install from PyPI with `pip install --user delete-facebook-posts` 2) Clone this repo and run `pip install --user .` or do `pip install --user git+https://github.com/weskerfoot/DeleteFB.git` -3) Set up a Python virtualenv, activate it, and run `pip install -r -requirements.txt`, then you can just run `python -m deletefb.deletefb.py` in the project directory, as you would if you had installed it from PyPI. +3) Set up a Python virtualenv, activate it, and run `pip install -r requirements.txt`, then you can just run `python deletefb` in the DeleteFB directory. ## How To Use It -- 2.30.2 From 69313de81152ee5f20d0a1078cc6b2b6d83a31bb Mon Sep 17 00:00:00 2001 From: Wesley Kerfoot Date: Sat, 1 Jun 2019 17:12:01 -0400 Subject: [PATCH 38/93] Refactor settings --- deletefb/config.py | 3 +++ deletefb/deletefb.py | 17 ++++++++--------- deletefb/tools/common.py | 7 +++++-- 3 files changed, 16 insertions(+), 11 deletions(-) create mode 100644 deletefb/config.py diff --git a/deletefb/config.py b/deletefb/config.py new file mode 100644 index 0000000..16fb42a --- /dev/null +++ b/deletefb/config.py @@ -0,0 +1,3 @@ +settings = { + "ARCHIVE" : False +} diff --git a/deletefb/deletefb.py b/deletefb/deletefb.py index 54d0c66..c69d42b 100755 --- a/deletefb/deletefb.py +++ b/deletefb/deletefb.py @@ -6,6 +6,7 @@ import json import os import sys +from config import settings from tools.common import logger from tools.login import login from tools.wall import delete_posts @@ -13,7 +14,6 @@ from tools.likes import unlike_pages LOG = logger("deletefb") - def run_delete(): parser = argparse.ArgumentParser() @@ -92,11 +92,7 @@ def run_delete(): args = parser.parse_args() - if args.archive_off: - os.environ["DELETEFB_ARCHIVE"] = "false" - else: - os.environ["DELETEFB_ARCHIVE"] = "true" - + settings["ARCHIVE"] = not args.archive_off if args.year and args.mode != "wall": parser.error("The --year option is only supported in wall mode") @@ -111,9 +107,12 @@ def run_delete(): ) if args.mode == "wall": - delete_posts(driver, - args.profile_url, - year=args.year) + delete_posts( + driver, + args.profile_url, + year=args.year + ) + elif args.mode == "unlike_pages": unlike_pages(driver) else: diff --git a/deletefb/tools/common.py b/deletefb/tools/common.py index fe97881..e9b99af 100644 --- a/deletefb/tools/common.py +++ b/deletefb/tools/common.py @@ -2,9 +2,11 @@ import json import logging import logging.config import os -from os.path import abspath, relpath, split, isfile import time +from config import settings + +from os.path import abspath, relpath, split, isfile from selenium.common.exceptions import ( NoSuchElementException, StaleElementReferenceException, @@ -57,8 +59,9 @@ def archiver(category): log_file = open(log_path, mode="ta", buffering=1) def log(content, timestamp=False): - if os.environ.get("DELETEFB_ARCHIVE", "true") == "false": + if not settings["ARCHIVE"]: return + structured_content = { "category" : category, "content" : content, -- 2.30.2 From e52dc89e07a5f0e1e03a209b5cac64f41d937ce6 Mon Sep 17 00:00:00 2001 From: Wesley Kerfoot Date: Sat, 1 Jun 2019 17:20:17 -0400 Subject: [PATCH 39/93] Default should be 'True' for archive --- deletefb/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deletefb/config.py b/deletefb/config.py index 16fb42a..8ca2556 100644 --- a/deletefb/config.py +++ b/deletefb/config.py @@ -1,3 +1,3 @@ settings = { - "ARCHIVE" : False + "ARCHIVE" : True } -- 2.30.2 From e8e9a3434f54fbcc2ee6b8bb4f4963c12374729f Mon Sep 17 00:00:00 2001 From: Wesley Kerfoot Date: Sat, 1 Jun 2019 17:41:35 -0400 Subject: [PATCH 40/93] See if a driver.refresh() helps with unliking pages --- deletefb/tools/common.py | 1 - deletefb/tools/likes.py | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/deletefb/tools/common.py b/deletefb/tools/common.py index e9b99af..79815ec 100644 --- a/deletefb/tools/common.py +++ b/deletefb/tools/common.py @@ -13,7 +13,6 @@ from selenium.common.exceptions import ( TimeoutException ) - SELENIUM_EXCEPTIONS = ( NoSuchElementException, StaleElementReferenceException, diff --git a/deletefb/tools/likes.py b/deletefb/tools/likes.py index cbb8886..c332ba0 100644 --- a/deletefb/tools/likes.py +++ b/deletefb/tools/likes.py @@ -18,6 +18,8 @@ def load_likes(driver): Returns: None """ + + driver.refresh() driver.get("https://www.facebook.com/pages/?category=liked") wait = WebDriverWait(driver, 20) -- 2.30.2 From 88e4703889c7b273f459fa1c1c9c6a183863ea52 Mon Sep 17 00:00:00 2001 From: Wesley Kerfoot Date: Sat, 1 Jun 2019 23:21:24 -0400 Subject: [PATCH 41/93] Fix imports --- deletefb/deletefb.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/deletefb/deletefb.py b/deletefb/deletefb.py index 54d0c66..db7dc25 100755 --- a/deletefb/deletefb.py +++ b/deletefb/deletefb.py @@ -6,10 +6,10 @@ import json import os import sys -from tools.common import logger -from tools.login import login -from tools.wall import delete_posts -from tools.likes import unlike_pages +from .tools.common import logger +from .tools.login import login +from .tools.wall import delete_posts +from .tools.likes import unlike_pages LOG = logger("deletefb") -- 2.30.2 From ab81a7f9d67e30014576176ab57053e5eed2ace4 Mon Sep 17 00:00:00 2001 From: Wesley Kerfoot Date: Sat, 1 Jun 2019 23:30:31 -0400 Subject: [PATCH 42/93] Fix docs on how to call package manually --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c882e17..eea8211 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ You have several options to run it. 1) Install from PyPI with `pip install --user delete-facebook-posts` 2) Clone this repo and run `pip install --user .` or do `pip install --user git+https://github.com/weskerfoot/DeleteFB.git` -3) Set up a Python virtualenv, activate it, and run `pip install -r requirements.txt`, then you can just run `python deletefb` in the DeleteFB directory. +3) Set up a Python virtualenv, activate it, and run `pip install -r requirements.txt`, then you can just run `python -m deletefb.deletefb` in the DeleteFB directory. ## How To Use It -- 2.30.2 From 169321e9cc4c2931a8a427e0b8ca293d70ad2fb9 Mon Sep 17 00:00:00 2001 From: Wesley Kerfoot Date: Sat, 1 Jun 2019 23:39:15 -0400 Subject: [PATCH 43/93] Refactor settings --- deletefb/deletefb.py | 1 + deletefb/tools/common.py | 2 +- deletefb/{ => tools}/config.py | 0 3 files changed, 2 insertions(+), 1 deletion(-) rename deletefb/{ => tools}/config.py (100%) diff --git a/deletefb/deletefb.py b/deletefb/deletefb.py index 0e4f546..484b5f9 100755 --- a/deletefb/deletefb.py +++ b/deletefb/deletefb.py @@ -6,6 +6,7 @@ import json import os import sys +from .tools.config import settings from .tools.common import logger from .tools.login import login from .tools.wall import delete_posts diff --git a/deletefb/tools/common.py b/deletefb/tools/common.py index 79815ec..06df691 100644 --- a/deletefb/tools/common.py +++ b/deletefb/tools/common.py @@ -4,7 +4,7 @@ import logging.config import os import time -from config import settings +from .config import settings from os.path import abspath, relpath, split, isfile from selenium.common.exceptions import ( diff --git a/deletefb/config.py b/deletefb/tools/config.py similarity index 100% rename from deletefb/config.py rename to deletefb/tools/config.py -- 2.30.2 From 23ba502b657ef51b7f3c9de217f6a99096ac34ef Mon Sep 17 00:00:00 2001 From: Wesley Kerfoot Date: Sun, 2 Jun 2019 01:59:41 -0400 Subject: [PATCH 44/93] Grab all URLs from page like page --- deletefb/deletefb.py | 2 +- deletefb/tools/likes.py | 48 +++++++++++------------------------------ 2 files changed, 13 insertions(+), 37 deletions(-) diff --git a/deletefb/deletefb.py b/deletefb/deletefb.py index 484b5f9..20db6e3 100755 --- a/deletefb/deletefb.py +++ b/deletefb/deletefb.py @@ -114,7 +114,7 @@ def run_delete(): ) elif args.mode == "unlike_pages": - unlike_pages(driver) + unlike_pages(driver, args.profile_url) else: print("Please enter a valid mode") sys.exit(1) diff --git a/deletefb/tools/likes.py b/deletefb/tools/likes.py index c332ba0..be86926 100644 --- a/deletefb/tools/likes.py +++ b/deletefb/tools/likes.py @@ -2,13 +2,14 @@ from selenium.webdriver.common.by import By from selenium.webdriver.common.action_chains import ActionChains from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC +from time import sleep from .common import SELENIUM_EXCEPTIONS, archiver, logger LOG = logger(__name__) -def load_likes(driver): +def load_likes(driver, profile_url): """ Loads the page that lists all pages you like @@ -19,24 +20,19 @@ def load_likes(driver): None """ - driver.refresh() - driver.get("https://www.facebook.com/pages/?category=liked") + driver.get("{0}/likes_all".format(profile_url)) wait = WebDriverWait(driver, 20) try: wait.until( - EC.presence_of_element_located((By.XPATH, "//button/div/div[text()='Liked']")) - ) - - wait.until( - EC.presence_of_element_located((By.XPATH, "//button/div/i[@aria-hidden=\"true\"]")) + EC.presence_of_element_located((By.CSS_SELECTOR, ".PageLikeButton")) ) except SELENIUM_EXCEPTIONS: LOG.exception("Traceback of load_likes") return -def unlike_pages(driver): +def unlike_pages(driver, profile_url): """ Unlike all pages @@ -49,40 +45,20 @@ def unlike_pages(driver): like_log, archive_likes = archiver("likes") + wait = WebDriverWait(driver, 20) actions = ActionChains(driver) - load_likes(driver) - - pages_list = driver.find_element_by_css_selector("#all_liked_pages") + load_likes(driver, profile_url) - actions.move_to_element(pages_list).perform() + pages = driver.find_elements_by_xpath("//li//div/div/a[contains(@class, 'lfloat')]") - unlike_buttons = pages_list.find_elements_by_xpath("//button/div/div[text()='Liked']/../..") - - while unlike_buttons: - for button in unlike_buttons: - try: - if "Liked" in button.text: - page_name = button.find_element_by_xpath("./../..").text.split("\n")[0] - - driver.execute_script("arguments[0].click();", button) - - archive_likes(page_name) + actions = ActionChains(driver) - print("{0} was unliked".format(page_name)) + page_urls = [page.get_attribute("href").replace("www", "mobile") for page in pages] - except SELENIUM_EXCEPTIONS: - continue + for url in page_urls: + driver.get(url) - load_likes(driver) - try: - pages_list = driver.find_element_by_css_selector("#all_liked_pages") - actions.move_to_element(pages_list).perform() - unlike_buttons = pages_list.find_elements_by_xpath("//button") - if not unlike_buttons: - break - except SELENIUM_EXCEPTIONS: - break # Explicitly close the log file when we're done with it like_log.close() -- 2.30.2 From 2b1346720d5d32c5de50ee7c59cf56f2cc2b5b10 Mon Sep 17 00:00:00 2001 From: Wesley Kerfoot Date: Sun, 2 Jun 2019 02:24:45 -0400 Subject: [PATCH 45/93] Refactor page links --- deletefb/tools/likes.py | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/deletefb/tools/likes.py b/deletefb/tools/likes.py index be86926..c1cb508 100644 --- a/deletefb/tools/likes.py +++ b/deletefb/tools/likes.py @@ -32,6 +32,24 @@ def load_likes(driver, profile_url): LOG.exception("Traceback of load_likes") return + +def get_page_links(driver): + pages = driver.find_elements_by_xpath("//li//div/div/a[contains(@class, 'lfloat')]") + + actions = ActionChains(driver) + + return [page.get_attribute("href").replace("www", "mobile") for page in pages] + + +def unlike_page(driver, url): + driver.get(url) + + actions = ActionChains(driver) + + button_element = find_element_by_xpath("//span[text()='Unlike']/..") + actions.move_to_element(button_element).click().perform() + + def unlike_pages(driver, profile_url): """ Unlike all pages @@ -45,20 +63,14 @@ def unlike_pages(driver, profile_url): like_log, archive_likes = archiver("likes") - wait = WebDriverWait(driver, 20) - actions = ActionChains(driver) - load_likes(driver, profile_url) - pages = driver.find_elements_by_xpath("//li//div/div/a[contains(@class, 'lfloat')]") - - actions = ActionChains(driver) - - page_urls = [page.get_attribute("href").replace("www", "mobile") for page in pages] - - for url in page_urls: - driver.get(url) + urls = get_page_links(driver) + while urls: + for url in urls: + print(unlike_page(driver, url)) + urls = get_page_links(driver) # Explicitly close the log file when we're done with it like_log.close() -- 2.30.2 From 0d1e047aedd0f969a2ffe44bfc1994932f10d5c6 Mon Sep 17 00:00:00 2001 From: Wesley Kerfoot Date: Sun, 2 Jun 2019 15:21:26 -0400 Subject: [PATCH 46/93] Page unliking working using mobile pages --- deletefb/tools/likes.py | 48 +++++++++++++++++++++++++++++++++++------ 1 file changed, 42 insertions(+), 6 deletions(-) diff --git a/deletefb/tools/likes.py b/deletefb/tools/likes.py index c1cb508..1677979 100644 --- a/deletefb/tools/likes.py +++ b/deletefb/tools/likes.py @@ -8,7 +8,6 @@ from .common import SELENIUM_EXCEPTIONS, archiver, logger LOG = logger(__name__) - def load_likes(driver, profile_url): """ Loads the page that lists all pages you like @@ -22,7 +21,7 @@ def load_likes(driver, profile_url): driver.get("{0}/likes_all".format(profile_url)) - wait = WebDriverWait(driver, 20) + wait = WebDriverWait(driver, 30) try: wait.until( @@ -34,6 +33,14 @@ def load_likes(driver, profile_url): def get_page_links(driver): + """ + Gets all of the links to the pages you like + + Args: + driver: seleniumrequests.Chrome Driver instance + Returns: + List of URLs to pages + """ pages = driver.find_elements_by_xpath("//li//div/div/a[contains(@class, 'lfloat')]") actions = ActionChains(driver) @@ -42,12 +49,36 @@ def get_page_links(driver): def unlike_page(driver, url): + """ + Unlikes a page given the URL to it + Args: + driver: seleniumrequests.Chrome Driver instance + url: url string pointing to a page + + Returns: + None + + """ driver.get(url) + wait = WebDriverWait(driver, 30) actions = ActionChains(driver) - button_element = find_element_by_xpath("//span[text()='Unlike']/..") - actions.move_to_element(button_element).click().perform() + wait.until( + EC.presence_of_element_located((By.XPATH, "//*[text()='Liked']")) + ) + + button = driver.find_element_by_xpath("//*[text()='Liked']") + + driver.execute_script("arguments[0].click();", button) + + wait.until( + EC.presence_of_element_located((By.XPATH, "//a/span[text()='Unlike']")) + ) + + unlike_button = driver.find_element_by_xpath("//a/span[text()='Unlike']/..") + + driver.execute_script("arguments[0].click();", unlike_button) def unlike_pages(driver, profile_url): @@ -69,8 +100,13 @@ def unlike_pages(driver, profile_url): while urls: for url in urls: - print(unlike_page(driver, url)) - urls = get_page_links(driver) + unlike_page(driver, url) + load_likes(driver, profile_url) + try: + urls = get_page_links(driver) + except SELENIUM_EXCEPTIONS: + # We're done + break # Explicitly close the log file when we're done with it like_log.close() -- 2.30.2 From 940a94afed1e7ae2de97bb8b06db551b521b0fe9 Mon Sep 17 00:00:00 2001 From: Wesley Kerfoot Date: Sun, 2 Jun 2019 15:29:07 -0400 Subject: [PATCH 47/93] Clean up --- deletefb/tools/common.py | 16 +++++++++------- deletefb/tools/likes.py | 9 +++++---- deletefb/tools/wall.py | 8 +++----- 3 files changed, 17 insertions(+), 16 deletions(-) diff --git a/deletefb/tools/common.py b/deletefb/tools/common.py index 06df691..cc9e95f 100644 --- a/deletefb/tools/common.py +++ b/deletefb/tools/common.py @@ -19,13 +19,15 @@ SELENIUM_EXCEPTIONS = ( TimeoutException ) -def try_move(actions, el): - for _ in range(10): - try: - actions.move_to_element(el).perform() - except StaleElementReferenceException: - time.sleep(5) - continue +def click_button(driver, el): + """ + Click a button using Javascript + Args: + driver: seleniumrequests.Chrome Driver instance + Returns: + None + """ + driver.execute_script("arguments[0].click();", el) def logger(name): """ diff --git a/deletefb/tools/likes.py b/deletefb/tools/likes.py index 1677979..9498f2e 100644 --- a/deletefb/tools/likes.py +++ b/deletefb/tools/likes.py @@ -2,9 +2,8 @@ from selenium.webdriver.common.by import By from selenium.webdriver.common.action_chains import ActionChains from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC -from time import sleep -from .common import SELENIUM_EXCEPTIONS, archiver, logger +from .common import SELENIUM_EXCEPTIONS, archiver, logger, click_button LOG = logger(__name__) @@ -70,15 +69,17 @@ def unlike_page(driver, url): button = driver.find_element_by_xpath("//*[text()='Liked']") - driver.execute_script("arguments[0].click();", button) + # Click the "Liked" button to open up "Unlike" + click_button(driver, button) wait.until( EC.presence_of_element_located((By.XPATH, "//a/span[text()='Unlike']")) ) + # There should be an "Unlike" button now, click it unlike_button = driver.find_element_by_xpath("//a/span[text()='Unlike']/..") - driver.execute_script("arguments[0].click();", unlike_button) + click_button(driver, unlike_button) def unlike_pages(driver, profile_url): diff --git a/deletefb/tools/wall.py b/deletefb/tools/wall.py index 9983e41..f164305 100644 --- a/deletefb/tools/wall.py +++ b/deletefb/tools/wall.py @@ -1,13 +1,11 @@ import time - from selenium.webdriver.common.action_chains import ActionChains -from .common import SELENIUM_EXCEPTIONS, archiver +from .common import SELENIUM_EXCEPTIONS, archiver, click_button # Used as a threshold to avoid running forever MAX_POSTS = 15000 - def delete_posts(driver, user_profile_url, year=None): @@ -59,8 +57,8 @@ def delete_posts(driver, 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) + click_button(driver, confirmation_button) + except SELENIUM_EXCEPTIONS: continue else: -- 2.30.2 From bf6ce1d615deef413975f801212b68730e54a945 Mon Sep 17 00:00:00 2001 From: Wesley Kerfoot Date: Sun, 2 Jun 2019 15:38:40 -0400 Subject: [PATCH 48/93] Match anything with "Unlike" for pages because FB keeps changing the elements --- deletefb/tools/likes.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/deletefb/tools/likes.py b/deletefb/tools/likes.py index 9498f2e..a245961 100644 --- a/deletefb/tools/likes.py +++ b/deletefb/tools/likes.py @@ -59,6 +59,7 @@ def unlike_page(driver, url): """ driver.get(url) + print(url) wait = WebDriverWait(driver, 30) actions = ActionChains(driver) @@ -73,11 +74,11 @@ def unlike_page(driver, url): click_button(driver, button) wait.until( - EC.presence_of_element_located((By.XPATH, "//a/span[text()='Unlike']")) + EC.presence_of_element_located((By.XPATH, "//*[text()='Unlike']")) ) # There should be an "Unlike" button now, click it - unlike_button = driver.find_element_by_xpath("//a/span[text()='Unlike']/..") + unlike_button = driver.find_element_by_xpath("//*[text()='Unlike']/..") click_button(driver, unlike_button) -- 2.30.2 From 139801c37cf0dfc20b5a7c67d1554d84c4d99876 Mon Sep 17 00:00:00 2001 From: Wesley Kerfoot Date: Sun, 2 Jun 2019 15:50:44 -0400 Subject: [PATCH 49/93] Update README --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index eea8211..ddc4fc5 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,8 @@ git+https://github.com/weskerfoot/DeleteFB.git` ## Unlike Pages * You may use `-M unlike_pages` to unlike all of your pages. The names of the pages will be archived (unless archival is turned off), and this option - conflicts with the year option. + conflicts with the year option. This will only unlike your *pages* that you + have liked. It will *not* unlike anything else (like books or movies). ## Archival * The tool will archive everything being deleted by default in `.log` files. -- 2.30.2 From 2580f4594e57b1e3eff7488537a7418f1e310198 Mon Sep 17 00:00:00 2001 From: Wesley Kerfoot Date: Sun, 2 Jun 2019 16:26:13 -0400 Subject: [PATCH 50/93] Bump version and refactor to use bloom filters for avoiding dups --- deletefb/tools/common.py | 14 ++++++++++++++ deletefb/tools/config.py | 3 ++- deletefb/tools/likes.py | 14 ++++++++++---- deletefb/tools/wall.py | 3 ++- setup.py | 5 +++-- 5 files changed, 31 insertions(+), 8 deletions(-) diff --git a/deletefb/tools/common.py b/deletefb/tools/common.py index cc9e95f..c107abf 100644 --- a/deletefb/tools/common.py +++ b/deletefb/tools/common.py @@ -6,6 +6,9 @@ import time from .config import settings +# Used to avoid duplicates in the log +from pybloom_live import BloomFilter + from os.path import abspath, relpath, split, isfile from selenium.common.exceptions import ( NoSuchElementException, @@ -59,10 +62,19 @@ def archiver(category): log_file = open(log_path, mode="ta", buffering=1) + bfilter = BloomFilter( + capacity=settings["MAX_POSTS"], + error_rate=0.001 + ) + def log(content, timestamp=False): if not settings["ARCHIVE"]: return + if content in bfilter: + # This was already archived + return + structured_content = { "category" : category, "content" : content, @@ -71,6 +83,8 @@ def archiver(category): log_file.write("{0}\n".format(json.dumps(structured_content))) + bfilter.add(content) + return (log_file, log) diff --git a/deletefb/tools/config.py b/deletefb/tools/config.py index 8ca2556..078efca 100644 --- a/deletefb/tools/config.py +++ b/deletefb/tools/config.py @@ -1,3 +1,4 @@ settings = { - "ARCHIVE" : True + "ARCHIVE" : True, + "MAX_POSTS" : 5000 } diff --git a/deletefb/tools/likes.py b/deletefb/tools/likes.py index a245961..3adb151 100644 --- a/deletefb/tools/likes.py +++ b/deletefb/tools/likes.py @@ -47,19 +47,23 @@ def get_page_links(driver): return [page.get_attribute("href").replace("www", "mobile") for page in pages] -def unlike_page(driver, url): +def unlike_page(driver, url, archive=None): """ Unlikes a page given the URL to it Args: driver: seleniumrequests.Chrome Driver instance url: url string pointing to a page + archive: archiver instance Returns: None """ + driver.get(url) - print(url) + + print("Unliking {0}".format(url)) + wait = WebDriverWait(driver, 30) actions = ActionChains(driver) @@ -82,6 +86,8 @@ def unlike_page(driver, url): click_button(driver, unlike_button) + if archive: + archive(url) def unlike_pages(driver, profile_url): """ @@ -102,9 +108,9 @@ def unlike_pages(driver, profile_url): while urls: for url in urls: - unlike_page(driver, url) - load_likes(driver, profile_url) + unlike_page(driver, url, archive=archive_likes) try: + load_likes(driver, profile_url) urls = get_page_links(driver) except SELENIUM_EXCEPTIONS: # We're done diff --git a/deletefb/tools/wall.py b/deletefb/tools/wall.py index f164305..5dcd6ec 100644 --- a/deletefb/tools/wall.py +++ b/deletefb/tools/wall.py @@ -1,10 +1,11 @@ import time from selenium.webdriver.common.action_chains import ActionChains +from .config import settings from .common import SELENIUM_EXCEPTIONS, archiver, click_button # Used as a threshold to avoid running forever -MAX_POSTS = 15000 +MAX_POSTS = settings["MAX_POSTS"] def delete_posts(driver, user_profile_url, diff --git a/setup.py b/setup.py index 7e3f445..ce75ca6 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ with open("README.md", "r") as fh: setuptools.setup( name="delete-facebook-posts", - version="1.1.1", + version="1.1.2", author="Wesley Kerfoot", author_email="wes@wesk.tech", description="A Selenium Script to Delete Facebook Posts", @@ -16,7 +16,8 @@ setuptools.setup( install_requires = [ "selenium", "selenium-requests", - "requests" + "requests", + "pybloom-live" ], classifiers= [ "Programming Language :: Python :: 3", -- 2.30.2 From 5cc653b2d5ebded2e4ac4d2f818eb39020def776 Mon Sep 17 00:00:00 2001 From: Wesley Kerfoot Date: Sun, 2 Jun 2019 16:30:18 -0400 Subject: [PATCH 51/93] Use single quotes in the README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ddc4fc5..5263b14 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ git+https://github.com/weskerfoot/DeleteFB.git` brew cask install chromedriver ``` -* Run `deletefb -E "youremail@example.org" -P "yourfacebookpassword" -U "https://www.facebook.com/your.profile.url"` +* Run `deletefb -E 'youremail@example.org' -P 'yourfacebookpassword' -U 'https://www.facebook.com/your.profile.url'` * The script will log into your Facebook account, go to your profile page, and start deleting posts. If it cannot delete something, then it will "hide" it from your timeline instead. -- 2.30.2 From f4b015b95f0fb1bf55b6451dae0087fdc4aece23 Mon Sep 17 00:00:00 2001 From: Wesley Kerfoot Date: Sun, 2 Jun 2019 16:42:20 -0400 Subject: [PATCH 52/93] Try to move elegantly handle broken pages --- deletefb/tools/likes.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/deletefb/tools/likes.py b/deletefb/tools/likes.py index 3adb151..9533eaa 100644 --- a/deletefb/tools/likes.py +++ b/deletefb/tools/likes.py @@ -64,13 +64,17 @@ def unlike_page(driver, url, archive=None): print("Unliking {0}".format(url)) - wait = WebDriverWait(driver, 30) + wait = WebDriverWait(driver, 60) actions = ActionChains(driver) - wait.until( - EC.presence_of_element_located((By.XPATH, "//*[text()='Liked']")) - ) + try: + wait.until( + EC.presence_of_element_located((By.XPATH, "//*[text()='Liked']")) + ) + except SELENIUM_EXCEPTIONS: + # Something went wrong with this page, so skip it + return button = driver.find_element_by_xpath("//*[text()='Liked']") -- 2.30.2 From b9db5d0eb2eae6d5fb2aa155a5f056efd1c8d714 Mon Sep 17 00:00:00 2001 From: Wesley Kerfoot Date: Sun, 2 Jun 2019 16:55:22 -0400 Subject: [PATCH 53/93] Cleanup --- deletefb/tools/likes.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/deletefb/tools/likes.py b/deletefb/tools/likes.py index 9533eaa..daa8789 100644 --- a/deletefb/tools/likes.py +++ b/deletefb/tools/likes.py @@ -20,7 +20,7 @@ def load_likes(driver, profile_url): driver.get("{0}/likes_all".format(profile_url)) - wait = WebDriverWait(driver, 30) + wait = WebDriverWait(driver, 20) try: wait.until( @@ -30,7 +30,6 @@ def load_likes(driver, profile_url): LOG.exception("Traceback of load_likes") return - def get_page_links(driver): """ Gets all of the links to the pages you like @@ -46,7 +45,6 @@ def get_page_links(driver): return [page.get_attribute("href").replace("www", "mobile") for page in pages] - def unlike_page(driver, url, archive=None): """ Unlikes a page given the URL to it @@ -64,7 +62,7 @@ def unlike_page(driver, url, archive=None): print("Unliking {0}".format(url)) - wait = WebDriverWait(driver, 60) + wait = WebDriverWait(driver, 20) actions = ActionChains(driver) -- 2.30.2 From e1ec0a89a6da35dbd0469bae36a00745048e27dc Mon Sep 17 00:00:00 2001 From: Wesley Kerfoot Date: Sun, 2 Jun 2019 17:09:40 -0400 Subject: [PATCH 54/93] Try to refactor delete options --- deletefb/tools/wall.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/deletefb/tools/wall.py b/deletefb/tools/wall.py index 5dcd6ec..0b854ea 100644 --- a/deletefb/tools/wall.py +++ b/deletefb/tools/wall.py @@ -30,6 +30,8 @@ def delete_posts(driver, post_content_sel = "userContent" post_timestamp_sel = "timestampContent" + button_types = ["FeedDeleteOption", "HIDE_FROM_TIMELINE", "UNTAG"] + wall_log, archive_wall_post = archiver("wall") while True: @@ -47,13 +49,18 @@ def delete_posts(driver, 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 = None + + for button_type in button_types: try: - delete_button = menu.find_element_by_xpath("//a[@data-feed-option-name=\"HIDE_FROM_TIMELINE\"]") + delete_button = menu.find_element_by_xpath("//a[@data-feed-option-name=\"{0}\"]".format(button_type)) + break except SELENIUM_EXCEPTIONS: - delete_button = menu.find_element_by_xpath("//a[@data-feed-option-name=\"UNTAG\"]") + continue + + if not delete_button: + print("Could not find anything to delete") + break actions.move_to_element(delete_button).click().perform() confirmation_button = driver.find_element_by_class_name("layerConfirm") -- 2.30.2 From 63beee5154abb5bb5f6f07cecb0d279ac8667550 Mon Sep 17 00:00:00 2001 From: Arshad Ahmad Date: Mon, 3 Jun 2019 13:03:22 +0800 Subject: [PATCH 55/93] Corrected typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5263b14..5a6c430 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ git+https://github.com/weskerfoot/DeleteFB.git` everything. You may safely minimize the chrome window without breaking it. ## 2FA -* It is recommended that you disable Two-Factor Authentication tempoprarily +* It is recommended that you disable Two-Factor Authentication temporarily while you are running the script, in order to get the best experience. * If you run into issues with Facebook complaining about your browser, -- 2.30.2 From f8804931838e9fadbb74cd7e86c9683e77595de2 Mon Sep 17 00:00:00 2001 From: wes Date: Mon, 3 Jun 2019 10:30:12 -0400 Subject: [PATCH 56/93] Fix error with missing data file --- deletefb/tools/common.py | 2 +- setup.py | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/deletefb/tools/common.py b/deletefb/tools/common.py index c107abf..a33f32b 100644 --- a/deletefb/tools/common.py +++ b/deletefb/tools/common.py @@ -40,7 +40,7 @@ def logger(name): Returns: logging.Logger """ - config_path = "deletefb/logging_conf.json" + config_path = "../logging_conf.json" if not isfile(config_path): # called from file (deletefb.py) os.chdir("..") with open(config_path, "r", encoding="utf-8") as config_file: diff --git a/setup.py b/setup.py index ce75ca6..1a4eedf 100644 --- a/setup.py +++ b/setup.py @@ -13,6 +13,11 @@ setuptools.setup( long_description_content_type="text/markdown", url="https://github.com/weskerfoot/DeleteFB", packages=setuptools.find_packages(), + include_package_data=True, + package_data={ + # If any package contains *.txt or *.rst files, include them: + '': ['*.json'], + }, install_requires = [ "selenium", "selenium-requests", -- 2.30.2 From 73e98905c0d8263fe404c8976f3d25e6605b2729 Mon Sep 17 00:00:00 2001 From: Wesley Kerfoot Date: Mon, 3 Jun 2019 10:40:20 -0400 Subject: [PATCH 57/93] Construct path to logging conf using __file__ variable --- deletefb/tools/common.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/deletefb/tools/common.py b/deletefb/tools/common.py index a33f32b..8cc4385 100644 --- a/deletefb/tools/common.py +++ b/deletefb/tools/common.py @@ -40,7 +40,11 @@ def logger(name): Returns: logging.Logger """ - config_path = "../logging_conf.json" + + # Make sure the path always points to the correct directory + config_path = os.path.dirname( + os.path.realpath(__file__)) + "/../logging_conf.json" + if not isfile(config_path): # called from file (deletefb.py) os.chdir("..") with open(config_path, "r", encoding="utf-8") as config_file: -- 2.30.2 From 9bc86fa644735d06dd346b3b6fa3b522e3fc6942 Mon Sep 17 00:00:00 2001 From: Wesley Kerfoot Date: Mon, 3 Jun 2019 10:40:48 -0400 Subject: [PATCH 58/93] Bump version --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 1a4eedf..19c796c 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ with open("README.md", "r") as fh: setuptools.setup( name="delete-facebook-posts", - version="1.1.2", + version="1.1.3", author="Wesley Kerfoot", author_email="wes@wesk.tech", description="A Selenium Script to Delete Facebook Posts", -- 2.30.2 From 2ac334c70f59c236b37ea1dd32b6c780e3c61e82 Mon Sep 17 00:00:00 2001 From: Wesley Kerfoot Date: Mon, 3 Jun 2019 10:48:27 -0400 Subject: [PATCH 59/93] Change logging confg path --- deletefb/logging_conf.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deletefb/logging_conf.json b/deletefb/logging_conf.json index 2078e80..e8037ec 100644 --- a/deletefb/logging_conf.json +++ b/deletefb/logging_conf.json @@ -27,7 +27,7 @@ "level": "INFO", "class": "logging.handlers.WatchedFileHandler", "formatter": "verbose", - "filename": "deletefb/deletefb.log", + "filename": "./deletefb.log", "mode": "a", "encoding": "utf-8" } -- 2.30.2 From 238e6ef69aba5d8dfba5e2bfbf336c43e0571ca6 Mon Sep 17 00:00:00 2001 From: Wesley Kerfoot Date: Mon, 3 Jun 2019 10:48:54 -0400 Subject: [PATCH 60/93] Bump minor version --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 19c796c..87e82db 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ with open("README.md", "r") as fh: setuptools.setup( name="delete-facebook-posts", - version="1.1.3", + version="1.1.4", author="Wesley Kerfoot", author_email="wes@wesk.tech", description="A Selenium Script to Delete Facebook Posts", -- 2.30.2 From 04ea30917bd76d14958af05932d028c0b71d55df Mon Sep 17 00:00:00 2001 From: Wesley Kerfoot <378351+weskerfoot@users.noreply.github.com> Date: Mon, 3 Jun 2019 11:09:14 -0400 Subject: [PATCH 61/93] Fix comment in setup.py --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 87e82db..22f6959 100644 --- a/setup.py +++ b/setup.py @@ -15,7 +15,7 @@ setuptools.setup( packages=setuptools.find_packages(), include_package_data=True, package_data={ - # If any package contains *.txt or *.rst files, include them: + # Include *json files in the package: '': ['*.json'], }, install_requires = [ -- 2.30.2 From dbf3649583d39f2c5993dc08528ab30373c2fe9f Mon Sep 17 00:00:00 2001 From: Wesley Kerfoot Date: Fri, 7 Jun 2019 01:07:05 -0400 Subject: [PATCH 62/93] Refactor in progress --- deletefb/tools/archive.py | 57 +++++++++++++++++++++++++++++++++++++++ deletefb/tools/common.py | 42 ----------------------------- 2 files changed, 57 insertions(+), 42 deletions(-) create mode 100644 deletefb/tools/archive.py diff --git a/deletefb/tools/archive.py b/deletefb/tools/archive.py new file mode 100644 index 0000000..107ee38 --- /dev/null +++ b/deletefb/tools/archive.py @@ -0,0 +1,57 @@ +import attr +import datetime +import uuid +import json + +from contextlib import contextmanager +from pathlib import Path + + +# Used to avoid duplicates in the log +from pybloom_live import BloomFilter + +def acquire_path(): + + log_file = open(log_path, mode="ta", buffering=1) + + bfilter = BloomFilter( + capacity=settings["MAX_POSTS"], + error_rate=0.001 + ) + + return + +@attr.s +class Post: + content = attr.ib() + comments = attr.ib(default=[]) + date = attr.ib(factory=datetime.datetime.now) + name = attr.ib(factory=uuid.uuid4) + +@attr.s +class Comment: + commenter = attr.ib() + content = attr.ib() + date = attr.ib(factory=datetime.datetime.now) + name = attr.ib(factory=uuid.uuid4) + +@attr.s +class Archive: + archive_type = attr.ib() + + # We give the Archive class a file handle + # This is better because the archive function + # should not know about anything related to filesystem paths + archive_file = attr.ib() + + def archive(self, content): + # do something + print("Archiving type {0} with content {1} to directory {2}".format(self.archive_type, content, self.archive_dir)) + self.archive_file.write(json.dumps(content.asdict())) + +wall_archive = Archive(archive_type="wall") + +comments = [Comment(commenter="Bob", content="Nice post!"), + Comment(commenter="Alice", content="I hate this")] + +wall_archive.archive(Post(content="A post!", comments=comments)) diff --git a/deletefb/tools/common.py b/deletefb/tools/common.py index 8cc4385..a831fc3 100644 --- a/deletefb/tools/common.py +++ b/deletefb/tools/common.py @@ -6,9 +6,6 @@ import time from .config import settings -# Used to avoid duplicates in the log -from pybloom_live import BloomFilter - from os.path import abspath, relpath, split, isfile from selenium.common.exceptions import ( NoSuchElementException, @@ -52,45 +49,6 @@ def logger(name): logging.config.dictConfig(config["logging"]) return logging.getLogger(name) -def archiver(category): - """ - Log content to file. Call using `archive("some content")` - - Args: - category: str The category of logs you want to log - - Returns: - (log_file_handle, archiver) - """ - log_path = "{0}.log".format(abspath(relpath(split(category)[-1], "."))) - - log_file = open(log_path, mode="ta", buffering=1) - - bfilter = BloomFilter( - capacity=settings["MAX_POSTS"], - error_rate=0.001 - ) - - def log(content, timestamp=False): - if not settings["ARCHIVE"]: - return - - if content in bfilter: - # This was already archived - return - - structured_content = { - "category" : category, - "content" : content, - "timestamp" : timestamp - } - - log_file.write("{0}\n".format(json.dumps(structured_content))) - - bfilter.add(content) - - return (log_file, log) - NO_CHROME_DRIVER = """ You need to install the chromedriver for Selenium\n -- 2.30.2 From 8bc51b88d7598755979a644b53f0eae47b929d5d Mon Sep 17 00:00:00 2001 From: Wesley Kerfoot Date: Sun, 9 Jun 2019 16:47:21 -0400 Subject: [PATCH 63/93] New archiver semi-working --- deletefb/tools/archive.py | 29 +++++++++++----- deletefb/tools/likes.py | 30 ++++++++-------- deletefb/tools/wall.py | 72 +++++++++++++++++++++------------------ 3 files changed, 73 insertions(+), 58 deletions(-) diff --git a/deletefb/tools/archive.py b/deletefb/tools/archive.py index 107ee38..c8f3e4c 100644 --- a/deletefb/tools/archive.py +++ b/deletefb/tools/archive.py @@ -11,7 +11,6 @@ from pathlib import Path from pybloom_live import BloomFilter def acquire_path(): - log_file = open(log_path, mode="ta", buffering=1) bfilter = BloomFilter( @@ -26,14 +25,15 @@ class Post: content = attr.ib() comments = attr.ib(default=[]) date = attr.ib(factory=datetime.datetime.now) - name = attr.ib(factory=uuid.uuid4) + name = attr.ib(factory=lambda: uuid.uuid4().hex) + @attr.s class Comment: commenter = attr.ib() content = attr.ib() date = attr.ib(factory=datetime.datetime.now) - name = attr.ib(factory=uuid.uuid4) + name = attr.ib(factory=lambda: uuid.uuid4().hex) @attr.s class Archive: @@ -46,12 +46,23 @@ class Archive: def archive(self, content): # do something - print("Archiving type {0} with content {1} to directory {2}".format(self.archive_type, content, self.archive_dir)) - self.archive_file.write(json.dumps(content.asdict())) + print("Archiving type {0} with content {1} to file".format(self.archive_type, content)) + self.archive_file.write(json.dumps(attr.asdict(content))) -wall_archive = Archive(archive_type="wall") + def close(self): + self.archive_file.close() -comments = [Comment(commenter="Bob", content="Nice post!"), - Comment(commenter="Alice", content="I hate this")] +@contextmanager +def archiver(archive_type): + archiver_instance = Archive(archive_type=archive_type, + archive_file=open( + "./%s.log" % archive_type, + mode="ta", + buffering=1 + ) + ) -wall_archive.archive(Post(content="A post!", comments=comments)) + try: + yield archiver_instance + finally: + archiver_instance.close() diff --git a/deletefb/tools/likes.py b/deletefb/tools/likes.py index daa8789..3246d85 100644 --- a/deletefb/tools/likes.py +++ b/deletefb/tools/likes.py @@ -3,7 +3,8 @@ from selenium.webdriver.common.action_chains import ActionChains from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC -from .common import SELENIUM_EXCEPTIONS, archiver, logger, click_button +from .common import SELENIUM_EXCEPTIONS, logger, click_button +from .archive import archiver LOG = logger(__name__) @@ -102,21 +103,18 @@ def unlike_pages(driver, profile_url): None """ - like_log, archive_likes = archiver("likes") + with archiver("likes") as archive_likes: + load_likes(driver, profile_url) - load_likes(driver, profile_url) + urls = get_page_links(driver) - urls = get_page_links(driver) + while urls: + for url in urls: + unlike_page(driver, url, archive=archive_likes.archive) + try: + load_likes(driver, profile_url) + urls = get_page_links(driver) + except SELENIUM_EXCEPTIONS: + # We're done + break - while urls: - for url in urls: - unlike_page(driver, url, archive=archive_likes) - try: - load_likes(driver, profile_url) - urls = get_page_links(driver) - except SELENIUM_EXCEPTIONS: - # We're done - break - - # Explicitly close the log file when we're done with it - like_log.close() diff --git a/deletefb/tools/wall.py b/deletefb/tools/wall.py index 0b854ea..f95d17e 100644 --- a/deletefb/tools/wall.py +++ b/deletefb/tools/wall.py @@ -2,7 +2,8 @@ import time from selenium.webdriver.common.action_chains import ActionChains from .config import settings -from .common import SELENIUM_EXCEPTIONS, archiver, click_button +from .common import SELENIUM_EXCEPTIONS, click_button +from .archive import archiver, Post # Used as a threshold to avoid running forever MAX_POSTS = settings["MAX_POSTS"] @@ -32,47 +33,52 @@ def delete_posts(driver, button_types = ["FeedDeleteOption", "HIDE_FROM_TIMELINE", "UNTAG"] - wall_log, archive_wall_post = archiver("wall") + with archiver("wall") as archive_wall_post: + while True: + try: + timeline_element = driver.find_element_by_class_name(post_button_sel) - while True: - try: - timeline_element = driver.find_element_by_class_name(post_button_sel) + post_content_element = driver.find_element_by_class_name(post_content_sel) + post_content_ts = driver.find_element_by_class_name(post_timestamp_sel) - post_content_element = driver.find_element_by_class_name(post_content_sel) - post_content_ts = driver.find_element_by_class_name(post_timestamp_sel) - archive_wall_post(post_content_element.text, timestamp=post_content_ts.text) + # Archive the post + archive_wall_post.archive( + Post( + content=post_content_element.text, + date=post_content_ts.text + ) + ) - actions = ActionChains(driver) - actions.move_to_element(timeline_element).click().perform() + 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() + menu = driver.find_element_by_css_selector("#globalContainer > div.uiContextualLayerPositioner.uiLayer > div") + actions.move_to_element(menu).perform() - delete_button = None + delete_button = None - for button_type in button_types: - try: - delete_button = menu.find_element_by_xpath("//a[@data-feed-option-name=\"{0}\"]".format(button_type)) - break - except SELENIUM_EXCEPTIONS: - continue + for button_type in button_types: + try: + delete_button = menu.find_element_by_xpath("//a[@data-feed-option-name=\"{0}\"]".format(button_type)) + break + except SELENIUM_EXCEPTIONS: + continue - if not delete_button: - print("Could not find anything to delete") - break + if not delete_button: + print("Could not find anything to delete") + break - actions.move_to_element(delete_button).click().perform() - confirmation_button = driver.find_element_by_class_name("layerConfirm") + actions.move_to_element(delete_button).click().perform() + confirmation_button = driver.find_element_by_class_name("layerConfirm") - click_button(driver, confirmation_button) + click_button(driver, confirmation_button) - except SELENIUM_EXCEPTIONS: - continue - else: - break - wall_log.close() + 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() + # Required to sleep the thread for a bit after using JS to click this button + time.sleep(5) + driver.refresh() -- 2.30.2 From 8ac0351ab279815f1ffd4c2025fd6526dd3a8a9d Mon Sep 17 00:00:00 2001 From: Wesley Kerfoot Date: Sun, 9 Jun 2019 17:02:54 -0400 Subject: [PATCH 64/93] Cleanup & refactoring --- deletefb/deletefb.py | 12 ++++---- deletefb/tools/archive.py | 62 ++++++++++++++------------------------- deletefb/tools/common.py | 13 ++++---- deletefb/tools/likes.py | 18 ++++++++---- deletefb/tools/login.py | 8 ++--- deletefb/tools/wall.py | 26 +++++++++++++--- 6 files changed, 71 insertions(+), 68 deletions(-) diff --git a/deletefb/deletefb.py b/deletefb/deletefb.py index 20db6e3..a69dfea 100755 --- a/deletefb/deletefb.py +++ b/deletefb/deletefb.py @@ -1,17 +1,17 @@ #!/usr/bin/env python +from .tools.common import logger +from .tools.config import settings +from .tools.likes import unlike_pages +from .tools.login import login +from .tools.wall import delete_posts + import argparse import getpass import json import os import sys -from .tools.config import settings -from .tools.common import logger -from .tools.login import login -from .tools.wall import delete_posts -from .tools.likes import unlike_pages - LOG = logger("deletefb") def run_delete(): diff --git a/deletefb/tools/archive.py b/deletefb/tools/archive.py index c8f3e4c..c2e0e04 100644 --- a/deletefb/tools/archive.py +++ b/deletefb/tools/archive.py @@ -1,39 +1,17 @@ -import attr -import datetime -import uuid -import json - from contextlib import contextmanager from pathlib import Path +import attr +import json # Used to avoid duplicates in the log from pybloom_live import BloomFilter -def acquire_path(): - log_file = open(log_path, mode="ta", buffering=1) - - bfilter = BloomFilter( +def make_filter(): + return BloomFilter( capacity=settings["MAX_POSTS"], error_rate=0.001 - ) - - return - -@attr.s -class Post: - content = attr.ib() - comments = attr.ib(default=[]) - date = attr.ib(factory=datetime.datetime.now) - name = attr.ib(factory=lambda: uuid.uuid4().hex) - - -@attr.s -class Comment: - commenter = attr.ib() - content = attr.ib() - date = attr.ib(factory=datetime.datetime.now) - name = attr.ib(factory=lambda: uuid.uuid4().hex) + ) @attr.s class Archive: @@ -44,25 +22,29 @@ class Archive: # should not know about anything related to filesystem paths archive_file = attr.ib() + _bloom_filter = attr.ib(factory=make_filter) + def archive(self, content): - # do something - print("Archiving type {0} with content {1} to file".format(self.archive_type, content)) - self.archive_file.write(json.dumps(attr.asdict(content))) + """ + Archive an object + """ + print("Archiving {0}".format(content)) - def close(self): - self.archive_file.close() + if content.name not in self._bloom_filter: + self.archive_file.write(json.dumps(attr.asdict(content))) + self._bloom_filter.add(content.name) + return @contextmanager def archiver(archive_type): - archiver_instance = Archive(archive_type=archive_type, - archive_file=open( - "./%s.log" % archive_type, - mode="ta", - buffering=1 - ) - ) + archive_file = open("./%s.log" % archive_type, mode="ta", buffering=1) + + archiver_instance = Archive( + archive_type=archive_type, + archive_file=archive_file + ) try: yield archiver_instance finally: - archiver_instance.close() + archive_file.close() diff --git a/deletefb/tools/common.py b/deletefb/tools/common.py index a831fc3..b508afc 100644 --- a/deletefb/tools/common.py +++ b/deletefb/tools/common.py @@ -1,11 +1,4 @@ -import json -import logging -import logging.config -import os -import time - from .config import settings - from os.path import abspath, relpath, split, isfile from selenium.common.exceptions import ( NoSuchElementException, @@ -13,6 +6,12 @@ from selenium.common.exceptions import ( TimeoutException ) +import json +import logging +import logging.config +import os +import time + SELENIUM_EXCEPTIONS = ( NoSuchElementException, StaleElementReferenceException, diff --git a/deletefb/tools/likes.py b/deletefb/tools/likes.py index 3246d85..9f8ef05 100644 --- a/deletefb/tools/likes.py +++ b/deletefb/tools/likes.py @@ -1,13 +1,19 @@ -from selenium.webdriver.common.by import By +from .archive import archiver +from .common import SELENIUM_EXCEPTIONS, logger, click_button from selenium.webdriver.common.action_chains import ActionChains -from selenium.webdriver.support.ui import WebDriverWait +from selenium.webdriver.common.by import By from selenium.webdriver.support import expected_conditions as EC - -from .common import SELENIUM_EXCEPTIONS, logger, click_button -from .archive import archiver +from selenium.webdriver.support.ui import WebDriverWait +import attr LOG = logger(__name__) +# Data type definitions of posts and comments +@attr.s +class Page: + date = attr.ib(factory=datetime.datetime.now) + name = attr.ib() + def load_likes(driver, profile_url): """ Loads the page that lists all pages you like @@ -90,7 +96,7 @@ def unlike_page(driver, url, archive=None): click_button(driver, unlike_button) if archive: - archive(url) + archive(Page(name=url)) def unlike_pages(driver, profile_url): """ diff --git a/deletefb/tools/login.py b/deletefb/tools/login.py index 847649d..2bca993 100644 --- a/deletefb/tools/login.py +++ b/deletefb/tools/login.py @@ -1,12 +1,10 @@ -import time -import sys - +from .common import NO_CHROME_DRIVER from selenium.common.exceptions import NoSuchElementException from selenium.webdriver.chrome.options import Options from seleniumrequests import Chrome -from .common import NO_CHROME_DRIVER - +import sys +import time def login(user_email_address, user_password, diff --git a/deletefb/tools/wall.py b/deletefb/tools/wall.py index f95d17e..0fc94f2 100644 --- a/deletefb/tools/wall.py +++ b/deletefb/tools/wall.py @@ -1,9 +1,27 @@ -import time +from .archive import archiver, Post +from .common import SELENIUM_EXCEPTIONS, click_button +from .config import settings from selenium.webdriver.common.action_chains import ActionChains -from .config import settings -from .common import SELENIUM_EXCEPTIONS, click_button -from .archive import archiver, Post +import attr +import datetime +import time +import uuid + +# Data type definitions of posts and comments +@attr.s +class Post: + content = attr.ib() + comments = attr.ib(default=[]) + date = attr.ib(factory=datetime.datetime.now) + name = attr.ib(factory=lambda: uuid.uuid4().hex) + +@attr.s +class Comment: + commenter = attr.ib() + content = attr.ib() + date = attr.ib(factory=datetime.datetime.now) + name = attr.ib(factory=lambda: uuid.uuid4().hex) # Used as a threshold to avoid running forever MAX_POSTS = settings["MAX_POSTS"] -- 2.30.2 From f4ba552fc4a07b985beb73485ff7a9b86369599f Mon Sep 17 00:00:00 2001 From: Wesley Kerfoot Date: Sun, 9 Jun 2019 17:06:21 -0400 Subject: [PATCH 65/93] Fix missing imports --- deletefb/deletefb.py | 1 - deletefb/tools/archive.py | 1 + deletefb/tools/likes.py | 4 +++- deletefb/tools/wall.py | 2 +- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/deletefb/deletefb.py b/deletefb/deletefb.py index a69dfea..befd51e 100755 --- a/deletefb/deletefb.py +++ b/deletefb/deletefb.py @@ -1,5 +1,4 @@ #!/usr/bin/env python - from .tools.common import logger from .tools.config import settings from .tools.likes import unlike_pages diff --git a/deletefb/tools/archive.py b/deletefb/tools/archive.py index c2e0e04..43a8f2b 100644 --- a/deletefb/tools/archive.py +++ b/deletefb/tools/archive.py @@ -1,3 +1,4 @@ +from .config import settings from contextlib import contextmanager from pathlib import Path diff --git a/deletefb/tools/likes.py b/deletefb/tools/likes.py index 9f8ef05..afeed8e 100644 --- a/deletefb/tools/likes.py +++ b/deletefb/tools/likes.py @@ -4,15 +4,17 @@ from selenium.webdriver.common.action_chains import ActionChains from selenium.webdriver.common.by import By from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.support.ui import WebDriverWait + import attr +import datetime LOG = logger(__name__) # Data type definitions of posts and comments @attr.s class Page: - date = attr.ib(factory=datetime.datetime.now) name = attr.ib() + date = attr.ib(factory=datetime.datetime.now) def load_likes(driver, profile_url): """ diff --git a/deletefb/tools/wall.py b/deletefb/tools/wall.py index 0fc94f2..46a92c8 100644 --- a/deletefb/tools/wall.py +++ b/deletefb/tools/wall.py @@ -1,4 +1,4 @@ -from .archive import archiver, Post +from .archive import archiver from .common import SELENIUM_EXCEPTIONS, click_button from .config import settings from selenium.webdriver.common.action_chains import ActionChains -- 2.30.2 From f159937e1deeaee8614ebf32395687f64528b6eb Mon Sep 17 00:00:00 2001 From: Wesley Kerfoot Date: Sun, 9 Jun 2019 17:16:34 -0400 Subject: [PATCH 66/93] Fix bugs with archiver --- deletefb/tools/archive.py | 2 +- deletefb/tools/common.py | 7 +++++++ deletefb/tools/likes.py | 5 ++--- deletefb/tools/wall.py | 7 +++---- 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/deletefb/tools/archive.py b/deletefb/tools/archive.py index 43a8f2b..831bf6e 100644 --- a/deletefb/tools/archive.py +++ b/deletefb/tools/archive.py @@ -32,7 +32,7 @@ class Archive: print("Archiving {0}".format(content)) if content.name not in self._bloom_filter: - self.archive_file.write(json.dumps(attr.asdict(content))) + self.archive_file.write(json.dumps(attr.asdict(content)) + "\n") self._bloom_filter.add(content.name) return diff --git a/deletefb/tools/common.py b/deletefb/tools/common.py index b508afc..fb08daf 100644 --- a/deletefb/tools/common.py +++ b/deletefb/tools/common.py @@ -6,6 +6,7 @@ from selenium.common.exceptions import ( TimeoutException ) +import datetime import json import logging import logging.config @@ -18,6 +19,12 @@ SELENIUM_EXCEPTIONS = ( TimeoutException ) +def timestamp_now(): + """ + Returns: a timestamp for this instant, in ISO 8601 format + """ + return datetime.datetime.isoformat(datetime.datetime.now()) + def click_button(driver, el): """ Click a button using Javascript diff --git a/deletefb/tools/likes.py b/deletefb/tools/likes.py index afeed8e..0ebce33 100644 --- a/deletefb/tools/likes.py +++ b/deletefb/tools/likes.py @@ -1,12 +1,11 @@ from .archive import archiver -from .common import SELENIUM_EXCEPTIONS, logger, click_button +from .common import SELENIUM_EXCEPTIONS, logger, click_button, timestamp_now from selenium.webdriver.common.action_chains import ActionChains from selenium.webdriver.common.by import By from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.support.ui import WebDriverWait import attr -import datetime LOG = logger(__name__) @@ -14,7 +13,7 @@ LOG = logger(__name__) @attr.s class Page: name = attr.ib() - date = attr.ib(factory=datetime.datetime.now) + date = attr.ib(factory=timestamp_now) def load_likes(driver, profile_url): """ diff --git a/deletefb/tools/wall.py b/deletefb/tools/wall.py index 46a92c8..2b01816 100644 --- a/deletefb/tools/wall.py +++ b/deletefb/tools/wall.py @@ -1,10 +1,9 @@ from .archive import archiver -from .common import SELENIUM_EXCEPTIONS, click_button +from .common import SELENIUM_EXCEPTIONS, click_button, timestamp_now from .config import settings from selenium.webdriver.common.action_chains import ActionChains import attr -import datetime import time import uuid @@ -13,14 +12,14 @@ import uuid class Post: content = attr.ib() comments = attr.ib(default=[]) - date = attr.ib(factory=datetime.datetime.now) + date = attr.ib(factory=timestamp_now) name = attr.ib(factory=lambda: uuid.uuid4().hex) @attr.s class Comment: commenter = attr.ib() content = attr.ib() - date = attr.ib(factory=datetime.datetime.now) + date = attr.ib(factory=timestamp_now) name = attr.ib(factory=lambda: uuid.uuid4().hex) # Used as a threshold to avoid running forever -- 2.30.2 From 91594a6cd47c36e6128302eb0abe1daf7262ee98 Mon Sep 17 00:00:00 2001 From: Wesley Kerfoot Date: Sun, 9 Jun 2019 18:57:41 -0400 Subject: [PATCH 67/93] Refactor types into separate module --- deletefb/tools/common.py | 7 ------- deletefb/tools/likes.py | 11 ++--------- deletefb/tools/wall.py | 20 ++------------------ deletefb/types.py | 30 ++++++++++++++++++++++++++++++ 4 files changed, 34 insertions(+), 34 deletions(-) create mode 100644 deletefb/types.py diff --git a/deletefb/tools/common.py b/deletefb/tools/common.py index fb08daf..b508afc 100644 --- a/deletefb/tools/common.py +++ b/deletefb/tools/common.py @@ -6,7 +6,6 @@ from selenium.common.exceptions import ( TimeoutException ) -import datetime import json import logging import logging.config @@ -19,12 +18,6 @@ SELENIUM_EXCEPTIONS = ( TimeoutException ) -def timestamp_now(): - """ - Returns: a timestamp for this instant, in ISO 8601 format - """ - return datetime.datetime.isoformat(datetime.datetime.now()) - def click_button(driver, el): """ Click a button using Javascript diff --git a/deletefb/tools/likes.py b/deletefb/tools/likes.py index 0ebce33..63ade1e 100644 --- a/deletefb/tools/likes.py +++ b/deletefb/tools/likes.py @@ -1,20 +1,13 @@ from .archive import archiver -from .common import SELENIUM_EXCEPTIONS, logger, click_button, timestamp_now +from ..types import Page +from .common import SELENIUM_EXCEPTIONS, logger, click_button from selenium.webdriver.common.action_chains import ActionChains from selenium.webdriver.common.by import By from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.support.ui import WebDriverWait -import attr - LOG = logger(__name__) -# Data type definitions of posts and comments -@attr.s -class Page: - name = attr.ib() - date = attr.ib(factory=timestamp_now) - def load_likes(driver, profile_url): """ Loads the page that lists all pages you like diff --git a/deletefb/tools/wall.py b/deletefb/tools/wall.py index 2b01816..890bae0 100644 --- a/deletefb/tools/wall.py +++ b/deletefb/tools/wall.py @@ -1,26 +1,10 @@ +from ..types import Post from .archive import archiver -from .common import SELENIUM_EXCEPTIONS, click_button, timestamp_now +from .common import SELENIUM_EXCEPTIONS, click_button from .config import settings from selenium.webdriver.common.action_chains import ActionChains -import attr import time -import uuid - -# Data type definitions of posts and comments -@attr.s -class Post: - content = attr.ib() - comments = attr.ib(default=[]) - date = attr.ib(factory=timestamp_now) - name = attr.ib(factory=lambda: uuid.uuid4().hex) - -@attr.s -class Comment: - commenter = attr.ib() - content = attr.ib() - date = attr.ib(factory=timestamp_now) - name = attr.ib(factory=lambda: uuid.uuid4().hex) # Used as a threshold to avoid running forever MAX_POSTS = settings["MAX_POSTS"] diff --git a/deletefb/types.py b/deletefb/types.py new file mode 100644 index 0000000..f171a9c --- /dev/null +++ b/deletefb/types.py @@ -0,0 +1,30 @@ +import attr +import time +import uuid +import datetime + +def timestamp_now(): + """ + Returns: a timestamp for this instant, in ISO 8601 format + """ + return datetime.datetime.isoformat(datetime.datetime.now()) + +# Data type definitions of posts and comments +@attr.s +class Post: + content = attr.ib() + comments = attr.ib(default=[]) + date = attr.ib(factory=timestamp_now) + name = attr.ib(factory=lambda: uuid.uuid4().hex) + +@attr.s +class Comment: + commenter = attr.ib() + content = attr.ib() + date = attr.ib(factory=timestamp_now) + name = attr.ib(factory=lambda: uuid.uuid4().hex) + +@attr.s +class Page: + name = attr.ib() + date = attr.ib(factory=timestamp_now) -- 2.30.2 From 0cebd82af8172d5690d714785e96ed47ccb3bdc4 Mon Sep 17 00:00:00 2001 From: Wesley Kerfoot Date: Sun, 9 Jun 2019 19:03:08 -0400 Subject: [PATCH 68/93] Remove unused imports --- deletefb/deletefb.py | 2 -- deletefb/tools/archive.py | 1 - deletefb/tools/common.py | 4 +--- deletefb/tools/likes.py | 5 ----- deletefb/types.py | 1 - 5 files changed, 1 insertion(+), 12 deletions(-) diff --git a/deletefb/deletefb.py b/deletefb/deletefb.py index befd51e..6688f4c 100755 --- a/deletefb/deletefb.py +++ b/deletefb/deletefb.py @@ -7,8 +7,6 @@ from .tools.wall import delete_posts import argparse import getpass -import json -import os import sys LOG = logger("deletefb") diff --git a/deletefb/tools/archive.py b/deletefb/tools/archive.py index 831bf6e..36692d8 100644 --- a/deletefb/tools/archive.py +++ b/deletefb/tools/archive.py @@ -1,6 +1,5 @@ from .config import settings from contextlib import contextmanager -from pathlib import Path import attr import json diff --git a/deletefb/tools/common.py b/deletefb/tools/common.py index b508afc..6b6c74e 100644 --- a/deletefb/tools/common.py +++ b/deletefb/tools/common.py @@ -1,5 +1,4 @@ -from .config import settings -from os.path import abspath, relpath, split, isfile +from os.path import isfile from selenium.common.exceptions import ( NoSuchElementException, StaleElementReferenceException, @@ -10,7 +9,6 @@ import json import logging import logging.config import os -import time SELENIUM_EXCEPTIONS = ( NoSuchElementException, diff --git a/deletefb/tools/likes.py b/deletefb/tools/likes.py index 63ade1e..5a8497f 100644 --- a/deletefb/tools/likes.py +++ b/deletefb/tools/likes.py @@ -1,7 +1,6 @@ from .archive import archiver from ..types import Page from .common import SELENIUM_EXCEPTIONS, logger, click_button -from selenium.webdriver.common.action_chains import ActionChains from selenium.webdriver.common.by import By from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.support.ui import WebDriverWait @@ -42,8 +41,6 @@ def get_page_links(driver): """ pages = driver.find_elements_by_xpath("//li//div/div/a[contains(@class, 'lfloat')]") - actions = ActionChains(driver) - return [page.get_attribute("href").replace("www", "mobile") for page in pages] def unlike_page(driver, url, archive=None): @@ -65,8 +62,6 @@ def unlike_page(driver, url, archive=None): wait = WebDriverWait(driver, 20) - actions = ActionChains(driver) - try: wait.until( EC.presence_of_element_located((By.XPATH, "//*[text()='Liked']")) diff --git a/deletefb/types.py b/deletefb/types.py index f171a9c..a771c67 100644 --- a/deletefb/types.py +++ b/deletefb/types.py @@ -1,5 +1,4 @@ import attr -import time import uuid import datetime -- 2.30.2 From 07cc35ce50e17cc57f4aef9bc7e28dba5674592c Mon Sep 17 00:00:00 2001 From: Wesley Kerfoot Date: Sun, 9 Jun 2019 19:07:04 -0400 Subject: [PATCH 69/93] Cleanup --- deletefb/tools/archive.py | 18 +++++++++++------- deletefb/tools/common.py | 4 +--- deletefb/tools/likes.py | 1 - 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/deletefb/tools/archive.py b/deletefb/tools/archive.py index 36692d8..cb4c41f 100644 --- a/deletefb/tools/archive.py +++ b/deletefb/tools/archive.py @@ -9,9 +9,9 @@ from pybloom_live import BloomFilter def make_filter(): return BloomFilter( - capacity=settings["MAX_POSTS"], - error_rate=0.001 - ) + capacity=settings["MAX_POSTS"], + error_rate=0.001 + ) @attr.s class Archive: @@ -33,15 +33,19 @@ class Archive: if content.name not in self._bloom_filter: self.archive_file.write(json.dumps(attr.asdict(content)) + "\n") self._bloom_filter.add(content.name) - return + return @contextmanager def archiver(archive_type): - archive_file = open("./%s.log" % archive_type, mode="ta", buffering=1) + archive_file = open( + "./{0}.log".format(archive_type), + mode="ta", + buffering=1 + ) archiver_instance = Archive( - archive_type=archive_type, - archive_file=archive_file + archive_type=archive_type, + archive_file=archive_file ) try: diff --git a/deletefb/tools/common.py b/deletefb/tools/common.py index 6b6c74e..7f6364a 100644 --- a/deletefb/tools/common.py +++ b/deletefb/tools/common.py @@ -36,8 +36,7 @@ def logger(name): """ # Make sure the path always points to the correct directory - config_path = os.path.dirname( - os.path.realpath(__file__)) + "/../logging_conf.json" + config_path = os.path.dirname(os.path.realpath(__file__)) + "/../logging_conf.json" if not isfile(config_path): # called from file (deletefb.py) os.chdir("..") @@ -46,7 +45,6 @@ def logger(name): logging.config.dictConfig(config["logging"]) return logging.getLogger(name) - NO_CHROME_DRIVER = """ You need to install the chromedriver for Selenium\n Please see this link https://github.com/weskerfoot/DeleteFB#how-to-use-it\n diff --git a/deletefb/tools/likes.py b/deletefb/tools/likes.py index 5a8497f..091a144 100644 --- a/deletefb/tools/likes.py +++ b/deletefb/tools/likes.py @@ -112,4 +112,3 @@ def unlike_pages(driver, profile_url): except SELENIUM_EXCEPTIONS: # We're done break - -- 2.30.2 From 17f00abff779d071c29158a3def2bf883bfc6232 Mon Sep 17 00:00:00 2001 From: Wesley Kerfoot Date: Mon, 10 Jun 2019 18:46:41 -0400 Subject: [PATCH 70/93] More robust handling of paths in archiver --- deletefb/tools/archive.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/deletefb/tools/archive.py b/deletefb/tools/archive.py index cb4c41f..1bc7364 100644 --- a/deletefb/tools/archive.py +++ b/deletefb/tools/archive.py @@ -1,5 +1,6 @@ from .config import settings from contextlib import contextmanager +from pathlib import Path import attr import json @@ -18,8 +19,6 @@ class Archive: archive_type = attr.ib() # We give the Archive class a file handle - # This is better because the archive function - # should not know about anything related to filesystem paths archive_file = attr.ib() _bloom_filter = attr.ib(factory=make_filter) @@ -37,8 +36,9 @@ class Archive: @contextmanager def archiver(archive_type): + archive_file = open( - "./{0}.log".format(archive_type), + (Path(".") / Path(archive_type).name).with_suffix(".log"), mode="ta", buffering=1 ) -- 2.30.2 From fc96e60c3a87516c7cea59bfbfc4a4fdec1447f0 Mon Sep 17 00:00:00 2001 From: wikijm Date: Thu, 13 Jun 2019 11:47:22 +0200 Subject: [PATCH 71/93] Add 'attr' as requirement Add 'attr' as requirement to avoid "ModuleNotFoundError: No module named 'attr'" error message. --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index 0511c3e..fe3c239 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,3 +8,4 @@ selenium-requests==1.3 six==1.12.0 tldextract==2.2.0 urllib3==1.25.2 +attr -- 2.30.2 From 008f0c57ac08e84e849f1db54bba6e313d2a122c Mon Sep 17 00:00:00 2001 From: Wesley Kerfoot Date: Thu, 13 Jun 2019 10:55:51 -0400 Subject: [PATCH 72/93] Bump version --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 22f6959..28d81fd 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ with open("README.md", "r") as fh: setuptools.setup( name="delete-facebook-posts", - version="1.1.4", + version="1.1.5", author="Wesley Kerfoot", author_email="wes@wesk.tech", description="A Selenium Script to Delete Facebook Posts", -- 2.30.2 From ad683182a3d41ab6d824a6b51054ada4f6c8d9be Mon Sep 17 00:00:00 2001 From: Wesley Kerfoot Date: Thu, 13 Jun 2019 11:01:01 -0400 Subject: [PATCH 73/93] Update requirements.txt --- requirements.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index fe3c239..0780145 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,9 @@ +attrs==19.1.0 +bitarray==0.9.3 certifi==2018.11.29 chardet==3.0.4 idna==2.8 +pybloom-live==3.0.0 requests==2.22.0 requests-file==1.4.3 selenium==3.141.0 @@ -8,4 +11,3 @@ selenium-requests==1.3 six==1.12.0 tldextract==2.2.0 urllib3==1.25.2 -attr -- 2.30.2 From cd95b0fe75b8937fcfc9ecd1ce496948cd03d680 Mon Sep 17 00:00:00 2001 From: Wesley Kerfoot Date: Thu, 13 Jun 2019 11:01:46 -0400 Subject: [PATCH 74/93] Update setup install requires --- setup.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 28d81fd..896271a 100644 --- a/setup.py +++ b/setup.py @@ -22,7 +22,8 @@ setuptools.setup( "selenium", "selenium-requests", "requests", - "pybloom-live" + "pybloom-live", + "attrs" ], classifiers= [ "Programming Language :: Python :: 3", -- 2.30.2 From e3314f0d914411aa2959386b6337f8821c35b90c Mon Sep 17 00:00:00 2001 From: Wesley Kerfoot Date: Sat, 15 Jun 2019 11:54:43 -0400 Subject: [PATCH 75/93] Update README --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 5a6c430..1ba5ba4 100644 --- a/README.md +++ b/README.md @@ -19,13 +19,15 @@ Personally, I did this so I would feel less attached to my Facebook profile ## Installation You have several options to run it. -1) Install from PyPI with `pip install --user delete-facebook-posts` -2) Clone this repo and run `pip install --user .` or do `pip install --user +1) Install from PyPI with `pip3 install --user delete-facebook-posts` +2) Clone this repo and run `pip3 install --user .` or do `pip3 install --user git+https://github.com/weskerfoot/DeleteFB.git` -3) Set up a Python virtualenv, activate it, and run `pip install -r requirements.txt`, then you can just run `python -m deletefb.deletefb` in the DeleteFB directory. +3) Set up a Python virtualenv, activate it, and run `pip3 install -r requirements.txt`, then you can just run `python -m deletefb.deletefb` in the DeleteFB directory. ## How To Use It +* Make sure that you have a recent version of Python 3.x installed (preferably + 3.6 or greater) * Make sure that you have Google Chrome installed and that it is up to date * Also install the chromedriver for Selenium. See [here](https://sites.google.com/a/chromium.org/chromedriver/home) for an explanation of what the chromedriver does. * On Linux, it will be called something like `chromium-chromedriver` or just -- 2.30.2 From 469f9e1f5ce5f201434ecfb07cf05a9ef026bb5e Mon Sep 17 00:00:00 2001 From: Wesley Kerfoot Date: Sat, 15 Jun 2019 12:12:10 -0400 Subject: [PATCH 76/93] Force python 3.6 or greater in setuptools --- setup.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 896271a..76df64f 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ with open("README.md", "r") as fh: setuptools.setup( name="delete-facebook-posts", - version="1.1.5", + version="1.1.6", author="Wesley Kerfoot", author_email="wes@wesk.tech", description="A Selenium Script to Delete Facebook Posts", @@ -14,6 +14,7 @@ setuptools.setup( url="https://github.com/weskerfoot/DeleteFB", packages=setuptools.find_packages(), include_package_data=True, + requires_python=">=3.6", package_data={ # Include *json files in the package: '': ['*.json'], -- 2.30.2 From 41ab642f326add38b5ea763032107f6155ed09e9 Mon Sep 17 00:00:00 2001 From: Wesley Kerfoot Date: Sun, 16 Jun 2019 01:00:16 -0400 Subject: [PATCH 77/93] More robust login procedure --- deletefb/tools/login.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/deletefb/tools/login.py b/deletefb/tools/login.py index 2bca993..772b53e 100644 --- a/deletefb/tools/login.py +++ b/deletefb/tools/login.py @@ -45,7 +45,7 @@ def login(user_email_address, driver.implicitly_wait(10) - driver.get("https://facebook.com") + driver.get("https://www.facebook.com/login/device-based/regular/login/?login_attempt=1&lwv=110") email = "email" password = "pass" @@ -97,4 +97,10 @@ def login(user_email_address, else: pass + # block until we have reached the main page + # print a message warning the user + while driver.current_url != "https://www.facebook.com/": + print("Execution blocked: Please navigate to https://www.facebook.com to continue") + time.sleep(5) + return driver -- 2.30.2 From ceb29e0344512aa5ff1d1f115ab6374c8051fde2 Mon Sep 17 00:00:00 2001 From: Wesley Kerfoot Date: Sun, 16 Jun 2019 01:02:28 -0400 Subject: [PATCH 78/93] Update readme with info about login --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index 1ba5ba4..1f059fe 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,14 @@ git+https://github.com/weskerfoot/DeleteFB.git` * Be patient as it will take a very long time, but it will eventually clear everything. You may safely minimize the chrome window without breaking it. +## Login +* The tool will log in using the credentials passed to it. It will wait until + the page "https://www.facebook.com/" is loaded in order to avoid any issues + with logging in. If you pass a 2FA token explicitly with the `-F` option, + then it will try to enter that for you. If there are any issues, it simply + pauses indefinitely to allow the user to resolve the problems, and then + continues execution. + ## 2FA * It is recommended that you disable Two-Factor Authentication temporarily while you are running the script, in order to get the best experience. -- 2.30.2 From 46f8324eb276e51067792cecf410bbbf90b4fe0f Mon Sep 17 00:00:00 2001 From: Wesley Kerfoot Date: Sun, 16 Jun 2019 01:07:28 -0400 Subject: [PATCH 79/93] New minor release 1.6.7 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 76df64f..a9b692d 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ with open("README.md", "r") as fh: setuptools.setup( name="delete-facebook-posts", - version="1.1.6", + version="1.1.7", author="Wesley Kerfoot", author_email="wes@wesk.tech", description="A Selenium Script to Delete Facebook Posts", -- 2.30.2 From 0f06567c7f91c3dd810b257a11ab056a311227e3 Mon Sep 17 00:00:00 2001 From: Wesley Kerfoot Date: Sun, 16 Jun 2019 01:14:08 -0400 Subject: [PATCH 80/93] Clean up README --- README.md | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/README.md b/README.md index 1f059fe..856e1f3 100644 --- a/README.md +++ b/README.md @@ -47,12 +47,7 @@ git+https://github.com/weskerfoot/DeleteFB.git` everything. You may safely minimize the chrome window without breaking it. ## Login -* The tool will log in using the credentials passed to it. It will wait until - the page "https://www.facebook.com/" is loaded in order to avoid any issues - with logging in. If you pass a 2FA token explicitly with the `-F` option, - then it will try to enter that for you. If there are any issues, it simply - pauses indefinitely to allow the user to resolve the problems, and then - continues execution. +* The tool will log in using the credentials passed to it. It will wait until the page `https://www.facebook.com/` is loaded in order to avoid any issues with logging in. If you pass a 2FA token explicitly with the `-F` option, then it will try to enter that for you. If there are any issues, it simply pauses indefinitely to allow the user to resolve the problems, and then continues execution. ## 2FA * It is recommended that you disable Two-Factor Authentication temporarily -- 2.30.2 From 4fd7a5b32b67b260e8e7aa92378a69c970fc8665 Mon Sep 17 00:00:00 2001 From: Gregory Gundersen Date: Wed, 19 Jun 2019 15:40:39 +0100 Subject: [PATCH 81/93] Don't version control log output files. --- deletefb/deletefb.log | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 deletefb/deletefb.log diff --git a/deletefb/deletefb.log b/deletefb/deletefb.log deleted file mode 100644 index e69de29..0000000 -- 2.30.2 From bee0c958a2d21681505186e9363abcd4e3c60481 Mon Sep 17 00:00:00 2001 From: Gregory Gundersen Date: Wed, 19 Jun 2019 15:41:46 +0100 Subject: [PATCH 82/93] Don't version control log output files. --- deletefb/logging_conf.json | 62 -------------------------------------- 1 file changed, 62 deletions(-) delete mode 100644 deletefb/logging_conf.json diff --git a/deletefb/logging_conf.json b/deletefb/logging_conf.json deleted file mode 100644 index 2078e80..0000000 --- a/deletefb/logging_conf.json +++ /dev/null @@ -1,62 +0,0 @@ -{ - "logging": { - "version": 1, - "disable_existing_loggers": true, - "formatters": { - "brief": { - "class": "logging.Formatter", - "style": "{", - "datefmt": "%H:%M:%S", - "format": "{name:s}-{levelname:s}-{asctime:s}-{message:s}" - }, - "verbose": { - "class": "logging.Formatter", - "style": "{", - "datefmt": "%Y-%m-%dT%H:%M:%S", - "format": "{name:s}:{levelname:s}:L{lineno:d} {asctime:s} {message:s}" - } - }, - "handlers": { - "console": { - "level": "DEBUG", - "class": "logging.StreamHandler", - "formatter": "brief", - "stream": "ext://sys.stdout" - }, - "file_handler": { - "level": "INFO", - "class": "logging.handlers.WatchedFileHandler", - "formatter": "verbose", - "filename": "deletefb/deletefb.log", - "mode": "a", - "encoding": "utf-8" - } - }, - "loggers": { - "root": { - "level": "DEBUG", - "handlers": ["console", "file_handler"] - }, - "deletefb": { - "level": "DEBUG", - "handlers": ["console"], - "propagate": false - }, - "login": { - "level": "DEBUG", - "handlers": ["file_handler"], - "propagate": false - }, - "likes": { - "level": "DEBUG", - "handlers": ["file_handler"], - "propagate": false - }, - "wall": { - "level": "DEBUG", - "handlers": ["file_handler"], - "propagate": false - } - } - } -} -- 2.30.2 From d4d9420fc3f498444e9a00ecc240666d6b48b5c5 Mon Sep 17 00:00:00 2001 From: Marco Matos Date: Mon, 8 Jul 2019 08:54:29 +0000 Subject: [PATCH 83/93] saving status --- Dockerfile | 41 +++++++++++++++++++++++++++++++++++++++++ sources.list | 13 +++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 Dockerfile create mode 100644 sources.list diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..2fc8cab --- /dev/null +++ b/Dockerfile @@ -0,0 +1,41 @@ +# docker build -t DeleteFB . && \ +# docker run -ti --rm \ +# -e DISPLAY=$DISPLAY \ +# -v /tmp/.X11-unix:/tmp/.X11-unix \ +# DeleteFB + + +FROM ubuntu:bionic + +# Update and apt install + # add your own sources.list file here in order to speed up the build + ADD sources.list /etc/apt/sources.list + + RUN apt-get update && \ + apt-get install -y firefox \ + git \ + python3 \ + python3-pip \ + libcanberra-gtk-module \ + curl \ + sudo \ + vim + +# creating user + ENV user username + RUN export uid=1000 gid=1000 && \ + mkdir -p /home/${user} && \ + echo "${user}:x:${uid}:${gid}:${user},,,:/home/${user}:/bin/bash" >> /etc/passwd && \ + echo "${user}:x:${uid}:" >> /etc/group && \ + echo "${user} ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/${user} && \ + chmod 0440 /etc/sudoers.d/${user} && \ + chown ${uid}:${gid} -R /home/${user} && \ + usermod -aG sudo ${user} + +# delete FB repo + RUN pip3 install --user delete-facebook-posts + RUN pip3 install --user git+https://github.com/weskerfoot/DeleteFB.git + RUN git clone https://github.com/weskerfoot/DeleteFB.git + WORKDIR ./DeleteFB + RUN pip3 install -r requirements.txt + CMD python3 -m ./deletefb/deletefb.py \ No newline at end of file diff --git a/sources.list b/sources.list new file mode 100644 index 0000000..e81c8f6 --- /dev/null +++ b/sources.list @@ -0,0 +1,13 @@ +###### Ubuntu Main Repos +deb http://pl.archive.ubuntu.com/ubuntu/ bionic main restricted universe +deb-src http://pl.archive.ubuntu.com/ubuntu/ bionic main restricted universe + +###### Ubuntu Update Repos +deb http://pl.archive.ubuntu.com/ubuntu/ bionic-security main restricted universe +deb http://pl.archive.ubuntu.com/ubuntu/ bionic-updates main restricted universe +deb http://pl.archive.ubuntu.com/ubuntu/ bionic-proposed main restricted universe +deb http://pl.archive.ubuntu.com/ubuntu/ bionic-backports main restricted universe +deb-src http://pl.archive.ubuntu.com/ubuntu/ bionic-security main restricted universe +deb-src http://pl.archive.ubuntu.com/ubuntu/ bionic-updates main restricted universe +deb-src http://pl.archive.ubuntu.com/ubuntu/ bionic-proposed main restricted universe +deb-src http://pl.archive.ubuntu.com/ubuntu/ bionic-backports main restricted universe -- 2.30.2 From 9d28d097646a889bdfb986264d73d741a7f236c4 Mon Sep 17 00:00:00 2001 From: Marco Matos Date: Mon, 8 Jul 2019 12:59:46 +0000 Subject: [PATCH 84/93] fixing current status --- Dockerfile | 37 +++++++++++++++++++++++++++++++++---- local.conf | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 4 deletions(-) create mode 100644 local.conf diff --git a/Dockerfile b/Dockerfile index 2fc8cab..d930022 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,8 +1,8 @@ -# docker build -t DeleteFB . && \ +# docker build -t deletefb . && \ # docker run -ti --rm \ # -e DISPLAY=$DISPLAY \ # -v /tmp/.X11-unix:/tmp/.X11-unix \ -# DeleteFB +# deletefb FROM ubuntu:bionic @@ -32,10 +32,39 @@ FROM ubuntu:bionic chown ${uid}:${gid} -R /home/${user} && \ usermod -aG sudo ${user} -# delete FB repo +# Install Chrome +RUN apt-get update && apt-get install -y \ + apt-transport-https \ + ca-certificates \ + curl \ + gnupg \ + hicolor-icon-theme \ + libcanberra-gtk* \ + libgl1-mesa-dri \ + libgl1-mesa-glx \ + libpango1.0-0 \ + libpulse0 \ + libv4l-0 \ + fonts-symbola \ + --no-install-recommends \ + && curl -sSL https://dl.google.com/linux/linux_signing_key.pub | apt-key add - \ + && echo "deb [arch=amd64] https://dl.google.com/linux/chrome/deb/ stable main" > /etc/apt/sources.list.d/google.list \ + && apt-get update && apt-get install -y \ + google-chrome-stable \ + --no-install-recommends && \ + rm -rf /var/lib/apt/lists/* + +COPY local.conf /etc/fonts/local.conf + + +# delete FB repo install + + RUN pip3 install --user delete-facebook-posts RUN pip3 install --user git+https://github.com/weskerfoot/DeleteFB.git RUN git clone https://github.com/weskerfoot/DeleteFB.git WORKDIR ./DeleteFB RUN pip3 install -r requirements.txt - CMD python3 -m ./deletefb/deletefb.py \ No newline at end of file + CMD python3 -m ./deletefb/deletefb.py + + USER ${user} diff --git a/local.conf b/local.conf new file mode 100644 index 0000000..f6a5f5c --- /dev/null +++ b/local.conf @@ -0,0 +1,34 @@ + + + + + +rgb + + + + +true + + + + +hintslight + + + + +true + + + + +lcddefault + + + + +false + + + -- 2.30.2 From 1cccf4a2c5eb2371fcba88966c81235d726715b0 Mon Sep 17 00:00:00 2001 From: Marco Matos Date: Mon, 8 Jul 2019 15:01:18 +0000 Subject: [PATCH 85/93] updating latest changes --- Dockerfile | 11 ++++++++++- run.sh | 14 ++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) create mode 100755 run.sh diff --git a/Dockerfile b/Dockerfile index d930022..077184b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,6 +2,15 @@ # docker run -ti --rm \ # -e DISPLAY=$DISPLAY \ # -v /tmp/.X11-unix:/tmp/.X11-unix \ + # --cap-add=SYS_ADMIN \ + # --cap-add=NET_ADMIN \ + # --cpuset-cpus 0 \ + # --memory 4GB \ + # -v /tmp/.X11-unix:/tmp/.X11-unix \ + # -e DISPLAY=unix:0 \ + # --device /dev/snd \ + # --device /dev/dri \ + # -v /dev/shm:/dev/shm \ # deletefb @@ -67,4 +76,4 @@ COPY local.conf /etc/fonts/local.conf RUN pip3 install -r requirements.txt CMD python3 -m ./deletefb/deletefb.py - USER ${user} + USER ${user} diff --git a/run.sh b/run.sh new file mode 100755 index 0000000..a9cc385 --- /dev/null +++ b/run.sh @@ -0,0 +1,14 @@ +docker build -t deletefb . && \ +docker run -ti --rm \ + -e DISPLAY=$DISPLAY \ + -v /tmp/.X11-unix:/tmp/.X11-unix \ + --cap-add=SYS_ADMIN \ + --cap-add=NET_ADMIN \ + --cpuset-cpus 0 \ + --memory 4GB \ + -v /tmp/.X11-unix:/tmp/.X11-unix \ + -e DISPLAY=unix:0 \ + --device /dev/snd \ + --device /dev/dri \ + -v /dev/shm:/dev/shm \ + deletefb \ No newline at end of file -- 2.30.2 From 95751423be8c622001f7884cfc075fc2acd5a768 Mon Sep 17 00:00:00 2001 From: Marco Matos Date: Tue, 9 Jul 2019 12:26:04 +0000 Subject: [PATCH 86/93] working files --- Dockerfile | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/Dockerfile b/Dockerfile index 077184b..5175b3d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -31,15 +31,15 @@ FROM ubuntu:bionic vim # creating user - ENV user username - RUN export uid=1000 gid=1000 && \ - mkdir -p /home/${user} && \ - echo "${user}:x:${uid}:${gid}:${user},,,:/home/${user}:/bin/bash" >> /etc/passwd && \ - echo "${user}:x:${uid}:" >> /etc/group && \ - echo "${user} ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/${user} && \ - chmod 0440 /etc/sudoers.d/${user} && \ - chown ${uid}:${gid} -R /home/${user} && \ - usermod -aG sudo ${user} + # ENV user username + # RUN export uid=1000 gid=1000 && \ + # mkdir -p /home/${user} && \ + # echo "${user}:x:${uid}:${gid}:${user},,,:/home/${user}:/bin/bash" >> /etc/passwd && \ + # echo "${user}:x:${uid}:" >> /etc/group && \ + # echo "${user} ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/${user} && \ + # chmod 0440 /etc/sudoers.d/${user} && \ + # chown ${uid}:${gid} -R /home/${user} && \ + # usermod -aG sudo ${user} # Install Chrome RUN apt-get update && apt-get install -y \ @@ -74,6 +74,7 @@ COPY local.conf /etc/fonts/local.conf RUN git clone https://github.com/weskerfoot/DeleteFB.git WORKDIR ./DeleteFB RUN pip3 install -r requirements.txt - CMD python3 -m ./deletefb/deletefb.py - - USER ${user} +# RUN pip3 install selenium oathlib attrs pybloom_live + RUN pip3 install attrs pybloom_live + RUN pip3 install --user selenium + CMD python3 -m deletefb.deletefb \ No newline at end of file -- 2.30.2 From 67085d198b58c25c2206ebd10f440259b00325b7 Mon Sep 17 00:00:00 2001 From: Marco Matos Date: Tue, 9 Jul 2019 15:12:43 +0000 Subject: [PATCH 87/93] updating working files --- Dockerfile | 94 ++++++++++++++++-------------------------------------- run.sh | 2 +- 2 files changed, 29 insertions(+), 67 deletions(-) diff --git a/Dockerfile b/Dockerfile index 5175b3d..c0d6034 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,80 +1,42 @@ -# docker build -t deletefb . && \ -# docker run -ti --rm \ -# -e DISPLAY=$DISPLAY \ -# -v /tmp/.X11-unix:/tmp/.X11-unix \ - # --cap-add=SYS_ADMIN \ - # --cap-add=NET_ADMIN \ - # --cpuset-cpus 0 \ - # --memory 4GB \ - # -v /tmp/.X11-unix:/tmp/.X11-unix \ - # -e DISPLAY=unix:0 \ - # --device /dev/snd \ - # --device /dev/dri \ - # -v /dev/shm:/dev/shm \ -# deletefb - - -FROM ubuntu:bionic - -# Update and apt install - # add your own sources.list file here in order to speed up the build - ADD sources.list /etc/apt/sources.list +# to build and launch, edit ./run.sh +# with your values, then execute it +FROM debian:stable-slim RUN apt-get update && \ - apt-get install -y firefox \ + apt-get install -y \ git \ python3 \ python3-pip \ libcanberra-gtk-module \ curl \ sudo \ - vim - -# creating user - # ENV user username - # RUN export uid=1000 gid=1000 && \ - # mkdir -p /home/${user} && \ - # echo "${user}:x:${uid}:${gid}:${user},,,:/home/${user}:/bin/bash" >> /etc/passwd && \ - # echo "${user}:x:${uid}:" >> /etc/group && \ - # echo "${user} ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/${user} && \ - # chmod 0440 /etc/sudoers.d/${user} && \ - # chown ${uid}:${gid} -R /home/${user} && \ - # usermod -aG sudo ${user} - -# Install Chrome -RUN apt-get update && apt-get install -y \ - apt-transport-https \ - ca-certificates \ - curl \ - gnupg \ - hicolor-icon-theme \ - libcanberra-gtk* \ - libgl1-mesa-dri \ - libgl1-mesa-glx \ - libpango1.0-0 \ - libpulse0 \ - libv4l-0 \ - fonts-symbola \ - --no-install-recommends \ - && curl -sSL https://dl.google.com/linux/linux_signing_key.pub | apt-key add - \ - && echo "deb [arch=amd64] https://dl.google.com/linux/chrome/deb/ stable main" > /etc/apt/sources.list.d/google.list \ - && apt-get update && apt-get install -y \ - google-chrome-stable \ - --no-install-recommends && \ - rm -rf /var/lib/apt/lists/* - -COPY local.conf /etc/fonts/local.conf - + vim \ + unzip \ + chromium \ + chromium-driver + +#creating user + ENV user username + RUN export uid=1000 gid=1000 && \ + mkdir -p /home/${user} && \ + echo "${user}:x:${uid}:${gid}:${user},,,:/home/${user}:/bin/bash" >> /etc/passwd && \ + echo "${user}:x:${uid}:" >> /etc/group && \ + echo "${user} ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/${user} && \ + chmod 0440 /etc/sudoers.d/${user} && \ + chown ${uid}:${gid} -R /home/${user} && \ + usermod -aG sudo ${user} # delete FB repo install + USER ${user} + WORKDIR /home/${user} + ARG mail + ARG pass + ARG url RUN pip3 install --user delete-facebook-posts RUN pip3 install --user git+https://github.com/weskerfoot/DeleteFB.git - RUN git clone https://github.com/weskerfoot/DeleteFB.git - WORKDIR ./DeleteFB - RUN pip3 install -r requirements.txt -# RUN pip3 install selenium oathlib attrs pybloom_live - RUN pip3 install attrs pybloom_live - RUN pip3 install --user selenium - CMD python3 -m deletefb.deletefb \ No newline at end of file + RUN git clone https://github.com/weskerfoot/DeleteFB.git + RUN pip3 install -r ./DeleteFB/requirements.txt + RUN pip3 install --user selenium attrs pybloom_live + CMD python3 -m deletefb.deletefb -E ${mail} -P ${pass} -U ${url} \ No newline at end of file diff --git a/run.sh b/run.sh index a9cc385..bf48132 100755 --- a/run.sh +++ b/run.sh @@ -11,4 +11,4 @@ docker run -ti --rm \ --device /dev/snd \ --device /dev/dri \ -v /dev/shm:/dev/shm \ - deletefb \ No newline at end of file + deletefb -e mail="your@email.com" -e pass="Y0Ur*P4ss" -e url="http://facebook.com/your-username" deletefb:latest -- 2.30.2 From 237aaa60c1381132ad934e7cd346096ccbb114c4 Mon Sep 17 00:00:00 2001 From: Marco Matos Date: Tue, 9 Jul 2019 15:13:03 +0000 Subject: [PATCH 88/93] cleaning trash --- local.conf | 34 ---------------------------------- sources.list | 13 ------------- 2 files changed, 47 deletions(-) delete mode 100644 local.conf delete mode 100644 sources.list diff --git a/local.conf b/local.conf deleted file mode 100644 index f6a5f5c..0000000 --- a/local.conf +++ /dev/null @@ -1,34 +0,0 @@ - - - - - -rgb - - - - -true - - - - -hintslight - - - - -true - - - - -lcddefault - - - - -false - - - diff --git a/sources.list b/sources.list deleted file mode 100644 index e81c8f6..0000000 --- a/sources.list +++ /dev/null @@ -1,13 +0,0 @@ -###### Ubuntu Main Repos -deb http://pl.archive.ubuntu.com/ubuntu/ bionic main restricted universe -deb-src http://pl.archive.ubuntu.com/ubuntu/ bionic main restricted universe - -###### Ubuntu Update Repos -deb http://pl.archive.ubuntu.com/ubuntu/ bionic-security main restricted universe -deb http://pl.archive.ubuntu.com/ubuntu/ bionic-updates main restricted universe -deb http://pl.archive.ubuntu.com/ubuntu/ bionic-proposed main restricted universe -deb http://pl.archive.ubuntu.com/ubuntu/ bionic-backports main restricted universe -deb-src http://pl.archive.ubuntu.com/ubuntu/ bionic-security main restricted universe -deb-src http://pl.archive.ubuntu.com/ubuntu/ bionic-updates main restricted universe -deb-src http://pl.archive.ubuntu.com/ubuntu/ bionic-proposed main restricted universe -deb-src http://pl.archive.ubuntu.com/ubuntu/ bionic-backports main restricted universe -- 2.30.2 From 08305ac8b5c34e146b509fac59cd6bff12cac513 Mon Sep 17 00:00:00 2001 From: Marco Matos Date: Thu, 11 Jul 2019 08:33:50 +0000 Subject: [PATCH 89/93] saving makefile status --- Makefile | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 Makefile diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..55b7eb1 --- /dev/null +++ b/Makefile @@ -0,0 +1,30 @@ +# Makefile + +NAME:= deletefb +url := https://facebook.com/$$username + +.PHONY: all build run + +all: build run + +build: + @docker build -t $(NAME) . + +run: + @read -p "Enter your Facebook email: " email; \ + + @read -p "Enter your Facebook password: " password; \ + + @read -p "Enter your Facebook username: " username; + + @docker run -ti --rm \ + -e DISPLAY=$DISPLAY \ + -v /tmp/.X11-unix:/tmp/.X11-unix \ + --cap-add=SYS_ADMIN \ + --cap-add=NET_ADMIN \ + --cpuset-cpus 0 \ + -v /tmp/.X11-unix:/tmp/.X11-unix \ + --device /dev/dri \ + -v /dev/shm:/dev/shm \ + $(NAME):latest "`which python3` -m deletefb.deletefb -e mail="$$email" -e pass=$$password -e url=$$url" + -- 2.30.2 From 51454a993264b1b64beb3b71a0cd3d1bd4b9e767 Mon Sep 17 00:00:00 2001 From: Marco Matos Date: Thu, 8 Aug 2019 10:17:21 +0000 Subject: [PATCH 90/93] updating working files --- Dockerfile | 35 ++++++++++++++++++++++++----------- Makefile | 17 ++++++----------- run.sh | 16 ++-------------- 3 files changed, 32 insertions(+), 36 deletions(-) diff --git a/Dockerfile b/Dockerfile index c0d6034..a1a9a5d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,19 @@ -# to build and launch, edit ./run.sh -# with your values, then execute it +# docker run -ti --rm \ +# -e DISPLAY=$DISPLAY \ +# -v /tmp/.X11-unix:/tmp/.X11-unix \ +# --cap-add=SYS_ADMIN \ +# --cap-add=NET_ADMIN \ +# --cpuset-cpus 0 \ +# --memory 4GB \ +# -v /tmp/.X11-unix:/tmp/.X11-unix \ +# -e DISPLAY=unix:0 \ +# --device /dev/snd \ +# --device /dev/dri \ +# -v /dev/shm:/dev/shm \ +# deletefb -e mail="your@email.com" -e pass="Y0Ur*P4ss" -e url="http://facebook.com/your-username" deletefb:latest FROM debian:stable-slim + RUN apt-get update && \ apt-get install -y \ git \ @@ -15,8 +27,8 @@ FROM debian:stable-slim chromium \ chromium-driver -#creating user - ENV user username +#creating new user + ENV user deletefb RUN export uid=1000 gid=1000 && \ mkdir -p /home/${user} && \ echo "${user}:x:${uid}:${gid}:${user},,,:/home/${user}:/bin/bash" >> /etc/passwd && \ @@ -26,17 +38,18 @@ FROM debian:stable-slim chown ${uid}:${gid} -R /home/${user} && \ usermod -aG sudo ${user} -# delete FB repo install + +# deletefb install USER ${user} WORKDIR /home/${user} - - ARG mail + + ARG email ARG pass ARG url + #ARG --conversations RUN pip3 install --user delete-facebook-posts - RUN pip3 install --user git+https://github.com/weskerfoot/DeleteFB.git - RUN git clone https://github.com/weskerfoot/DeleteFB.git - RUN pip3 install -r ./DeleteFB/requirements.txt RUN pip3 install --user selenium attrs pybloom_live - CMD python3 -m deletefb.deletefb -E ${mail} -P ${pass} -U ${url} \ No newline at end of file + + ADD run.sh /tmp/run.sh + ENTRYPOINT [ "/tmp/run.sh" ] diff --git a/Makefile b/Makefile index 55b7eb1..976fdb9 100644 --- a/Makefile +++ b/Makefile @@ -11,20 +11,15 @@ build: @docker build -t $(NAME) . run: - @read -p "Enter your Facebook email: " email; \ - - @read -p "Enter your Facebook password: " password; \ - - @read -p "Enter your Facebook username: " username; - - @docker run -ti --rm \ - -e DISPLAY=$DISPLAY \ + @read -p "Enter your Facebook email: " email && read -p "Enter your Facebook password: " password && read -p "Enter your Facebook username: " username && docker run -ti --rm \ + -e DISPLAY=$$DISPLAY \ -v /tmp/.X11-unix:/tmp/.X11-unix \ --cap-add=SYS_ADMIN \ --cap-add=NET_ADMIN \ --cpuset-cpus 0 \ - -v /tmp/.X11-unix:/tmp/.X11-unix \ --device /dev/dri \ -v /dev/shm:/dev/shm \ - $(NAME):latest "`which python3` -m deletefb.deletefb -e mail="$$email" -e pass=$$password -e url=$$url" - + -e EMAIL="$$email" \ + -e PASS="$$password" \ + -e URL="https://facebook.com/$$username" \ + $(NAME):latest \ No newline at end of file diff --git a/run.sh b/run.sh index bf48132..6a6897b 100755 --- a/run.sh +++ b/run.sh @@ -1,14 +1,2 @@ -docker build -t deletefb . && \ -docker run -ti --rm \ - -e DISPLAY=$DISPLAY \ - -v /tmp/.X11-unix:/tmp/.X11-unix \ - --cap-add=SYS_ADMIN \ - --cap-add=NET_ADMIN \ - --cpuset-cpus 0 \ - --memory 4GB \ - -v /tmp/.X11-unix:/tmp/.X11-unix \ - -e DISPLAY=unix:0 \ - --device /dev/snd \ - --device /dev/dri \ - -v /dev/shm:/dev/shm \ - deletefb -e mail="your@email.com" -e pass="Y0Ur*P4ss" -e url="http://facebook.com/your-username" deletefb:latest +#!/bin/bash +/usr/bin/python3 -m deletefb.deletefb -E $EMAIL -P $PASS -U $URL \ No newline at end of file -- 2.30.2 From d2ce6b5ab85dea7e0d116804b22ed0f378b0ea47 Mon Sep 17 00:00:00 2001 From: Marco Matos Date: Thu, 8 Aug 2019 10:35:42 +0000 Subject: [PATCH 91/93] updating dockerfile --- Dockerfile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Dockerfile b/Dockerfile index a1a9a5d..f7c2177 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,3 +1,6 @@ +# To run, just type "make", or + +# docker build -t deletefb . # docker run -ti --rm \ # -e DISPLAY=$DISPLAY \ # -v /tmp/.X11-unix:/tmp/.X11-unix \ -- 2.30.2 From ee34c119750d002d6753c25105f409a136618f34 Mon Sep 17 00:00:00 2001 From: Marco Matos Date: Thu, 8 Aug 2019 10:41:34 +0000 Subject: [PATCH 92/93] fixing makefile --- Makefile | 1 - 1 file changed, 1 deletion(-) diff --git a/Makefile b/Makefile index 976fdb9..f342b0c 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,6 @@ # Makefile NAME:= deletefb -url := https://facebook.com/$$username .PHONY: all build run -- 2.30.2 From e761cc236b8550ba399c13384889b80711bb2853 Mon Sep 17 00:00:00 2001 From: Marco Matos Date: Thu, 8 Aug 2019 12:59:50 +0000 Subject: [PATCH 93/93] fixing spaces --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index f342b0c..87bf189 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,7 @@ build: @docker build -t $(NAME) . run: - @read -p "Enter your Facebook email: " email && read -p "Enter your Facebook password: " password && read -p "Enter your Facebook username: " username && docker run -ti --rm \ + @read -p "Enter your Facebook email: " email && read -p "Enter your Facebook password: " password && read -p "Enter your Facebook username: " username && docker run -ti --rm \ -e DISPLAY=$$DISPLAY \ -v /tmp/.X11-unix:/tmp/.X11-unix \ --cap-add=SYS_ADMIN \ -- 2.30.2