Created
November 6, 2023 05:14
-
-
Save ariankordi/bae839598cd20680a5b2f85cd565336e to your computer and use it in GitHub Desktop.
EllisProtect™ Technology wipes Tom Ellis off the face of the earth. No context is provided.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from selenium import webdriver | |
# import the service so a custom executable path can be specified | |
from selenium.webdriver.chrome.service import Service | |
from shutil import which | |
from selenium.webdriver.common.by import By | |
from selenium.webdriver.support.ui import WebDriverWait | |
from selenium.webdriver.support import expected_conditions as EC | |
import selenium.common.exceptions | |
import traceback | |
import re | |
import time | |
import os | |
from sys import argv | |
# for notify-send | |
from subprocess import Popen | |
# Initialize global variables | |
previous_chat_url = None | |
# names of removed pages will be stored here | |
names = [] | |
# match names based on misspellings of "ellis" | |
# alongside other names and keywords | |
# "facebook user" = banned user | |
ellis_misspellings_pattern = re.compile( | |
r'(?:[E3]+[lIi1!]+[lIi1!]+[s5]+)|' # Matches ellis (with various misspellings) | |
r'(?:Facebook\suser)|' # "Facebook user" | |
r'(?:musk)|' # (Elon) Musk | |
r'(?:kardashian)|' # Kardashian | |
r'(?:timberlake)|' # Timberlake | |
r'(?:fan)|' # fan | |
r'(?:page)', # page | |
re.IGNORECASE | |
) | |
# detect flukes across the ENTIRE page at the end, canNOT be erroneous... | |
fluke_pattern = re.compile( | |
r'(?:ellis)|' # Matches ellis (with various misspellings) | |
r'(?:musk)|' # (Elon) Musk | |
r'(?:kardashian)|' # Kardashian | |
r'(?:timberlake)', # Timberlake | |
re.IGNORECASE | |
) | |
# Configure Chrome profile to disable image loading | |
options = webdriver.ChromeOptions() | |
# if is true then will print all chats uhhhhhh | |
debug = False | |
try: | |
# if there's any second argument then disable headless | |
if argv[1]: | |
print('\033[1mDEBUG MODE ENABLED:\033[0m browser will not run headless. it will also stick around. if you get an error next time you run this it\'s probably because the chromium didn\'t launch because there is already one open. ok?') | |
# makes the browser hang around for testing | |
options.add_experimental_option("detach", True) | |
debug = True | |
except IndexError: | |
options.add_argument("--headless") # Comment this line if you want to see the browser in action | |
pass | |
# optimization to disable images | |
# (ublock should also do this additionally disabling fonts & trackers) | |
options.add_argument('--blink-settings=imagesEnabled=false') | |
options.add_argument("--enable-gpu") | |
options.add_argument("--ozone-platform-hint=auto") | |
options.add_argument("--ignore-gpu-blocklist") | |
options.add_argument("--use-vulkan") | |
options.add_argument("--enable-raw-draw") | |
options.add_argument("--enable-gpu-rasterization") | |
options.add_argument("--enable-gpu-compositing") | |
options.add_argument("--password-store=basic") | |
# make sure we can find a home directory. otherwise, Just Book It! | |
assert 'HOME' in os.environ | |
# Specify and set the path to the Chrome user profile in the user home .config | |
options.add_argument(f"--user-data-dir={os.environ['HOME']}/.config/FUCK KING FACE BOOK") | |
# test timeouts with this profile | |
#options.add_argument("--user-data-dir=/dev/shm/tezt") | |
service = Service(executable_path=which('chromedriver')) | |
# Initialize the WebDriver with the custom profile | |
driver = webdriver.Chrome(options=options, service=service) | |
print('opening webdriver...') | |
# the window size can probably be smaller | |
# but this is important otherwise it will not load at all | |
driver.set_window_size(1366, 768) | |
# Open Facebook Messenger | |
driver.get("https://www.messenger.com") | |
print('navigated to messenger.com') | |
# Wait for chats to load | |
try: | |
WebDriverWait(driver, 60).until( | |
EC.presence_of_element_located((By.CSS_SELECTOR, 'div[aria-label="Chats"]')) | |
) | |
except selenium.common.exceptions.TimeoutException as e: | |
# chat was not able to load..!!! now notify me | |
Popen(['notify-send', '-u', 'critical', 'EllisProtect™ Fault!!', 'Was not able to load chats. OH NO! Inspect the systemd logs.']) | |
raise e | |
print('chats loaded, matching...') | |
# Find chats containing "Ellis" (or its misspellings) | |
#chat_entries = driver.find_elements(By.CSS_SELECTOR, 'a[role=link] div[class=""] > span[dir=auto] > span') | |
# above broke oct 8, this is a lot more vague but it should work | |
chat_entries = driver.find_elements(By.CSS_SELECTOR, 'a[role=link] span[dir=auto]') | |
matching_chats = [] | |
#matching_chats = [chat for chat in chat_entries if ellis_misspellings_pattern.search(chat.text)] | |
for chat in chat_entries: | |
does_match = ellis_misspellings_pattern.search(chat.text) | |
if debug: | |
print(f'"{chat.text}" {("does" if does_match else "does NOT")} match') | |
if does_match: | |
matching_chats.append(chat) | |
# Iterate through matching chats | |
# TODO: use enumerate, and make it so that at the end it will re-search the chat list and add to the array | |
for chat in matching_chats: | |
try: | |
# Click on the chat | |
chat.click() | |
print('clicked on a matching chat, trying to see when change finished...') | |
# poll to find out when the page transitions | |
# by checking the user's href at the top of the page | |
# should be 2 seconds max, 0.1 * 20 | |
for _ in range(20): | |
# also retry on some exceptions | |
try: | |
chat_url_button = driver.find_element(By.CSS_SELECTOR, '[role="main"] a[role="link"][tabindex="0"][aria-label]') | |
current_chat_url = chat_url_button.get_attribute('href') | |
if current_chat_url != previous_chat_url: | |
previous_chat_url = current_chat_url | |
page_name = chat_url_button.get_attribute('aria-label') | |
print(f"successfully navigated to: \033[1m{page_name}\033[0m") | |
names.append(page_name) | |
break | |
time.sleep(0.1) | |
# retry if there's a stale element or no element found | |
except (selenium.common.exceptions.StaleElementReferenceException, selenium.common.exceptions.NoSuchElementException): | |
continue | |
# Check if "Conversation information" section is open | |
conversation_info_section = driver.find_elements(By.CSS_SELECTOR, 'div[style="transform: translateX(0%) translateZ(1px);"]') | |
if not conversation_info_section: | |
# Click on the "Conversation information" (dot dot dot) button if it doesn't exist | |
print('clicking on ellipsis button...') | |
WebDriverWait(driver, 3).until( | |
EC.presence_of_element_located((By.CSS_SELECTOR, '[aria-label="Conversation information"]')) | |
).click() | |
print('waiting for sidebar to load after Conversation information was clicked...') | |
# Wait for the sidebar to load | |
WebDriverWait(driver, 3).until( | |
EC.presence_of_element_located((By.CSS_SELECTOR, 'div[style="transform: translateX(0%) translateZ(1px);"]')) | |
) | |
print('checking for Privacy & Support section...') | |
# Check if Privacy & Support section exists | |
privacy_support_section = driver.find_elements(By.CSS_SELECTOR, '#privacy_support_section') | |
if not privacy_support_section: | |
# Click on the Privacy & Support dropdown if it doesn't exist | |
privacy_support_dropdown = driver.find_elements(By.CSS_SELECTOR, 'div[role="list"] > div [role="button"]') | |
for dropdown_item in privacy_support_dropdown: | |
if "Privacy" in dropdown_item.text: | |
dropdown_item.click() | |
break | |
# Wait for the Privacy & Support section to load | |
print('waiting for Privacy & support section') | |
WebDriverWait(driver, 3).until( | |
EC.presence_of_element_located((By.CSS_SELECTOR, '#privacy_support_section')) | |
) | |
print('looking for block button') | |
# Search for and click on the "Block" button | |
block_button = driver.find_elements(By.CSS_SELECTOR, '#privacy_support_section [role="button"]') | |
the_button = None | |
for button in block_button: | |
# detect Block or Unblock | |
if "lock" in button.text: | |
the_button = button | |
break | |
# this ugly definition is due to wanting to continue the outer loop | |
if the_button: | |
if "Unblock" in the_button.text: | |
# TODO: sometimes is wrong | |
print('person is already blocked! moving along...') | |
#continue | |
else: | |
# kinda messy to include literally the rest of the logic in here | |
# but this is all that involves clicking... | |
# ... the block button and dealing with the dialog | |
the_button.click() | |
print('clicked it.') | |
print('now waiting for dialog (or its block button)') | |
# Wait for the confirmation dialog to load | |
# make it a short wait bc this is the wait that happpens if they are already blocked | |
confirmation_dialog = WebDriverWait(driver, 6).until( | |
EC.presence_of_element_located((By.CSS_SELECTOR, '[role="dialog"]')) | |
) | |
print('waiting for Block messages and calls button') | |
# click Block messages and calls button | |
block_button = WebDriverWait(driver, 3).until( | |
EC.presence_of_element_located((By.CSS_SELECTOR, '[role="dialog"] [role=button]:not([aria-label=Close]):first-child')) | |
) | |
if block_button: | |
block_button.click() | |
print('waiting for block confirm button') | |
# Wait for and click the "Block" button in the confirmation dialog | |
block_confirm_button = WebDriverWait(driver, 3).until( | |
EC.presence_of_element_located((By.CSS_SELECTOR, '[role="dialog"] [aria-label="Block"]:first-child')) | |
) | |
if block_confirm_button: | |
# make sure it is interactable | |
for _ in range(9): | |
if block_confirm_button.is_displayed() and block_confirm_button.is_enabled(): | |
block_confirm_button.click() | |
break | |
time.sleep(0.3) | |
print('closing dialog, loop will repeat') | |
# wait for and click close button for first dialog (either one work) | |
close_button = WebDriverWait(driver, 3).until( | |
# invisible element | |
EC.presence_of_element_located((By.CSS_SELECTOR, '[role="dialog"] div[aria-hidden="true"] [aria-label="Close"]')) | |
#EC.presence_of_element_located((By.CSS_SELECTOR, '[role="dialog"] div:not(aria-hidden) > div > div > div > div > [aria-label="Close"]')) | |
) | |
if close_button: | |
#close_button.click() | |
# for whatever reason selenium is super BITCHy about wanting to close this. so... | |
# close the hidden close button as a matter of fact | |
driver.execute_script("document.querySelector('[role=\"dialog\"] div[aria-hidden=\"true\"] [aria-label=\"Close\"]').click()") | |
print('closed block dialog, now going to look for menu button') | |
# find chat menu button, three dots alongside name | |
#menu_button = chat.find_element(By.XPATH, '../../../../../../../../../../../../../../../..//div[@aria-label="Menu"]') | |
#menu_button.click() | |
# returns not interactable so have to use this js method instead... | |
driver.execute_script("arguments[0].parentElement.parentElement.parentElement.parentElement.parentElement.parentElement.parentElement.parentElement.parentElement.parentElement.parentElement.querySelector('[aria-label=\"Menu\"]').click()", chat) | |
# click archive button in the menu we just opened | |
archive_button = driver.find_elements(By.CSS_SELECTOR, 'div[role="menuitem"]') | |
for button in archive_button: | |
# detect Block or Unblock | |
if "Archive" in button.text: | |
print('found and clicking archive button') | |
button.click() | |
break | |
# skip if some sort of timeoutexception happened | |
#except (selenium.common.exceptions.TimeoutException, selenium.common.exceptions.ElementNotInteractableException): | |
except Exception as e: | |
Popen(['notify-send', 'EllisProtect™ Non-Critical', f'Exception {type(e)} was caught, you should probably check it out.']) | |
traceback.print_exc() | |
continue | |
# check len by end (or actually TODO see if i should use a manual counter instead) | |
if len(matching_chats): | |
s_plural = ('s' if len(matching_chats) > 1 else '') | |
names_list = ', '.join(names) | |
print('\033[91mtsk tsk tsk! looks like she has ' + str(len(matching_chats)) + ' chat' + s_plural + ' with tom ellis!\n' + names_list + '\033[0m') | |
Popen(['notify-send', 'EllisProtect™ Detection Results', f'Removed {len(matching_chats)} chat{s_plural} with: {names_list}.']) | |
# sleep to let stuff post before exiting | |
time.sleep(3) | |
else: | |
# NO MATCHING CHATS??? check the entire page once again | |
# to make sure that it is not a fluke. | |
search_entire_page = fluke_pattern.search(driver.page_source) | |
if search_entire_page: | |
Popen(['notify-send', '-u', 'critical', 'EllisProtect™ CRITICAL FAULT!!', 'What the hell? Tom Ellis was detected but couldn\'t be removed. Time to debug the script!!']) | |
print('\033[91mWHAT THE FEVC???\033[0m no ellis found in any chats but also it was found on the entire page so hmmmMMMMMMMMMMMMMMMMMMMMMMM. here:', search_entire_page) | |
else: | |
# ok, all may be clear... | |
print('\033[92mall clear!\033[0m') | |
#except Exception as e: | |
# print(f"An error occurred: {str(e)}") | |
#finally: | |
# # Close the WebDriver (it should do this automatically regardless) | |
# driver.quit() | |
""" | |
* load fb messenger, messenger.com | |
* probably use a custom chrome profile here | |
* choose not to load images with the chrome config bc they're unneeded and it would save bandwidth | |
* should probably check if it doesn't load right and show a notify-send | |
* wait for chats to load: div[aria-label=Chats] | |
* search for "ellis" in all chats | |
* regex: | |
const ellisMisspellingsPattern = /[E3]+[l|I|i|1|!]+[l|I|i|1|!]+[s|5]+/i; | |
Array.from(document.querySelectorAll('a[role=link] div[class=\'\'] > span[dir=auto] > span')).filter(e => { | |
return e.textContent.match(ellisMisspellingsPattern); | |
}); | |
* for each chat entry... | |
* click on it (click on the respective conversation) | |
* wait until after you have navigated to a new URL... (TBD): | |
* click on this: [aria-label="Conversation information"] (dot dot dot) | |
* track that this was already set in a variable and skip it next time | |
* wait for the whole sidebar to load: div[style="transform: translateX(0%) translateZ(1px);"] | |
* search for and click on the Privacy & support dropdown: | |
Array.from(document.querySelectorAll('div[role=list] > div [role=button]')).filter(e => { | |
return e.textContent.contains('Privacy'); | |
})[0].click(); | |
* track this and skip it next time too | |
* wait for privacy support section to load: #privacy_support_section (yay, easy!) | |
* wait for block button, then click it: | |
Array.from(document.querySelectorAll('#privacy_support_section [role=button]')).filter(e => { | |
return e.textContent.contains('Block'); | |
})[0].click() | |
//* wait for the box to pop up: [role=dialog] | |
// * you may have to add a selector for the | |
* wait for and then click the block button: [role=dialog] [role=button]:not([aria-label=Close]):first-child | |
* may be unreliable, matches ALL buttons, and just clicks the first one. the first one SHOULD be "block messages and calls" if you are friends with them | |
* wait for and then click the confirmation block button: [aria-label=Block] | |
* click this [role=dialog] [aria-label=Close] | |
* move on to next list entry | |
""" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[Unit] | |
Description=ellisprotect wipes tom ellis off the face of the earth | |
[Service] | |
Type=oneshot | |
WorkingDirectory=/home/arian/Downloads | |
ExecStart=/usr/bin/python3 ellisprotect.py |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[Unit] | |
Description=run ellisprotect every x hours? | |
[Timer] | |
#OnCalendar=*:0/2 | |
OnCalendar=0/3:00:00 | |
RandomizedDelaySec=1h | |
Persistent=true | |
[Install] | |
WantedBy=timers.target |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment