| import io |
| import os |
| import sys |
| import time |
| import socket |
| import shutil |
| import subprocess |
|
|
| |
| if hasattr(sys.stdout, "buffer"): |
| sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace") |
| if hasattr(sys.stderr, "buffer"): |
| sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding="utf-8", errors="replace") |
|
|
| from selenium import webdriver |
| from selenium.webdriver.chrome.service import Service |
| from selenium.webdriver.chrome.options import Options |
| from selenium.webdriver.common.by import By |
| from selenium.webdriver.common.keys import Keys |
| 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 selenium.common.exceptions import ( |
| TimeoutException, |
| NoSuchElementException, |
| StaleElementReferenceException, |
| WebDriverException, |
| ) |
| from webdriver_manager.chrome import ChromeDriverManager |
|
|
| |
| |
| |
|
|
| DEBUG_PORT = 9224 |
| AUTH_TIMEOUT = 120 |
| ELEMENT_TIMEOUT = 30 |
|
|
| |
| |
| GOOGLE_SHEET_URL = "https://docs.google.com/spreadsheets/d/14HcGbkrDpCTcvoParYHWCmT1Y-p4Q1Gz7m2QzdHqdx0/edit?usp=sharing" |
|
|
| |
| |
| DISCORD_WEBHOOK_URL = "https://discord.com/api/webhooks/1482211219187437750/LNBwNAIWiT4IXJvZLeWOL2uEw3Rspvt7YBfm3kKB9ha6jJ4bWQhWz_ZzYydVHWAXT2N8" |
|
|
| |
| |
| |
|
|
| def log(msg): print(f"[+] {msg}", flush=True) |
| def warn(msg): print(f"[!] {msg}", flush=True) |
| def err(msg): print(f"[ERROR] {msg}", flush=True) |
|
|
|
|
| def find_chrome(): |
| for path in [ |
| os.path.join(os.environ.get("ProgramFiles", ""), "Google", "Chrome", "Application", "chrome.exe"), |
| os.path.join(os.environ.get("ProgramFiles(x86)", ""), "Google", "Chrome", "Application", "chrome.exe"), |
| os.path.join(os.environ.get("LOCALAPPDATA", ""), "Google", "Chrome", "Application", "chrome.exe"), |
| ]: |
| if path and os.path.exists(path): |
| return path |
| raise FileNotFoundError("Chrome not found.") |
|
|
|
|
| def kill_chrome(): |
| log("Killing Chrome and chromedriver ...") |
| for _ in range(3): |
| subprocess.run(["taskkill", "/f", "/im", "chrome.exe"], capture_output=True) |
| subprocess.run(["taskkill", "/f", "/im", "chromedriver.exe"], capture_output=True) |
| time.sleep(1) |
| for _ in range(15): |
| r = subprocess.run('tasklist /fi "imagename eq chrome.exe" /fo csv /nh', |
| capture_output=True, shell=True, text=True) |
| if "chrome.exe" not in r.stdout.lower(): |
| log("Chrome is dead.") |
| return |
| time.sleep(1) |
| warn("Chrome may still be running.") |
|
|
|
|
| def clone_profile(): |
| local = os.environ.get("LOCALAPPDATA", "") |
| src = os.path.join(local, "Google", "Chrome", "User Data") |
| tmp = os.path.join(os.environ.get("TEMP", "C:\\Temp"), "chrome_demo_profile") |
|
|
| log(f"Nuking old temp profile at {tmp} ...") |
| shutil.rmtree(tmp, ignore_errors=True) |
| time.sleep(1) |
|
|
| dest_profile = os.path.join(tmp, "Default") |
| os.makedirs(dest_profile, exist_ok=True) |
|
|
| |
| ls_src = os.path.join(src, "Local State") |
| if os.path.exists(ls_src): |
| try: |
| shutil.copy2(ls_src, os.path.join(tmp, "Local State")) |
| log("Copied Local State.") |
| except Exception as e: |
| warn(f"Local State copy failed: {e}") |
|
|
| for item in ["Cookies", "Preferences", "Network"]: |
| s = os.path.join(src, "Default", item) |
| d = os.path.join(dest_profile, item) |
| if not os.path.exists(s): |
| continue |
| try: |
| if os.path.isdir(s): |
| shutil.copytree(s, d) |
| else: |
| shutil.copy2(s, d) |
| log(f"Copied {item}") |
| except Exception as e: |
| warn(f"Could not copy {item}: {e}") |
|
|
| return tmp, "Default" |
|
|
|
|
| def launch_chrome(user_data_dir, profile_dir, chrome_exe): |
| cmd = [ |
| chrome_exe, |
| f"--user-data-dir={user_data_dir}", |
| f"--profile-directory={profile_dir}", |
| f"--remote-debugging-port={DEBUG_PORT}", |
| "--start-maximized", |
| "--no-sandbox", |
| "--disable-dev-shm-usage", |
| "--disable-extensions", |
| "--no-first-run", |
| "--no-default-browser-check", |
| "--disable-popup-blocking", |
| "about:blank", |
| ] |
| log(f"Launching Chrome (port {DEBUG_PORT}) ...") |
| subprocess.Popen(cmd) |
| log("Waiting for debug port ...") |
| for i in range(30): |
| try: |
| with socket.create_connection(("127.0.0.1", DEBUG_PORT), timeout=1): |
| log(f"Port open after {i+1}s.") |
| time.sleep(2) |
| return |
| except OSError: |
| time.sleep(1) |
| warn("Debug port never opened.") |
|
|
|
|
| def attach_selenium(): |
| opts = Options() |
| opts.add_experimental_option("debuggerAddress", f"127.0.0.1:{DEBUG_PORT}") |
| svc = Service(ChromeDriverManager().install()) |
| driver = webdriver.Chrome(service=svc, options=opts) |
| log("Selenium attached.") |
| return driver |
|
|
|
|
| def wait_for_url(driver, fragment, timeout=AUTH_TIMEOUT, label=""): |
| log(f"Waiting for URL fragment '{fragment}' ({label}) ...") |
| try: |
| WebDriverWait(driver, timeout).until( |
| lambda d: fragment in d.current_url.lower() |
| ) |
| log("URL matched.") |
| time.sleep(2) |
| return True |
| except TimeoutException: |
| err(f"Timed out waiting for '{fragment}'.") |
| return False |
|
|
|
|
| def safe_click(driver, el): |
| try: |
| el.click() |
| except Exception: |
| driver.execute_script("arguments[0].click();", el) |
|
|
|
|
| def wait_and_find(driver, css, timeout=ELEMENT_TIMEOUT): |
| return WebDriverWait(driver, timeout).until( |
| EC.element_to_be_clickable((By.CSS_SELECTOR, css)) |
| ) |
|
|
|
|
| def new_tab(driver, url): |
| driver.execute_script(f"window.open('{url}', '_blank');") |
| driver.switch_to.window(driver.window_handles[-1]) |
| time.sleep(3) |
|
|
|
|
| |
| |
| |
|
|
| def phase_gmail(driver): |
| log("--- PHASE 1: Gmail ---") |
| driver.get("https://mail.google.com/mail/u/0/#inbox") |
| time.sleep(3) |
|
|
| if any(x in driver.current_url.lower() for x in ["accounts.google", "signin", "servicelogin"]): |
| log("Please log in to Gmail in the browser ...") |
| if not wait_for_url(driver, "mail.google.com", label="Gmail"): |
| return "" |
|
|
| log("Waiting for inbox to load ...") |
| try: |
| WebDriverWait(driver, 30).until( |
| EC.presence_of_element_located((By.CSS_SELECTOR, "tr.zA")) |
| ) |
| except TimeoutException: |
| warn("Inbox rows not found.") |
| return "" |
|
|
| rows = driver.find_elements(By.CSS_SELECTOR, "tr.zA") |
| if not rows: |
| warn("No emails found.") |
| return "" |
|
|
| log(f"Found {len(rows)} emails. Finding first non-automated email ...") |
|
|
| |
| skip_keywords = ["noreply", "no-reply", "donotreply", "discord", "google", |
| "automated", "notification", "mailer", "support@", "verify"] |
| |
| target_row = None |
| for row in rows[:10]: |
| try: |
| sender_el = row.find_element(By.CSS_SELECTOR, "span.yP, span[email]") |
| sender = (sender_el.get_attribute("email") or sender_el.text).lower() |
| if not any(skip in sender for skip in skip_keywords): |
| log(f"Selected email from: {sender}") |
| target_row = row |
| break |
| except Exception: |
| continue |
|
|
| |
| if target_row is None: |
| warn("No non-automated email found, using first email anyway.") |
| target_row = rows[0] |
|
|
| driver.execute_script("arguments[0].style.outline='3px solid red'", target_row) |
| time.sleep(0.5) |
| safe_click(driver, target_row) |
|
|
| try: |
| body = WebDriverWait(driver, 10).until( |
| EC.presence_of_element_located((By.CSS_SELECTOR, "div.a3s.aiL, div.a3s")) |
| ) |
| driver.execute_script("arguments[0].style.outline='3px solid green'", body) |
| text = body.text[:300].replace("\n", " ").strip() |
| log(f"Email body scraped: {text[:80]}...") |
| return text |
| except TimeoutException: |
| warn("Could not read email body.") |
| return "" |
|
|
|
|
| |
| |
| |
|
|
| def phase_sheets(driver, email_text): |
| log("--- PHASE 2: Google Sheets ---") |
|
|
| if GOOGLE_SHEET_URL == "PASTE_YOUR_GOOGLE_SHEET_URL_HERE": |
| warn("GOOGLE_SHEET_URL not set — skipping Sheets phase.") |
| return |
|
|
| |
| content = email_text.strip() if email_text and len(email_text.strip()) > 10 \ |
| else "New lead identified — follow up required. Source: Gmail inbox triage." |
|
|
| new_tab(driver, GOOGLE_SHEET_URL) |
|
|
| |
| log("Waiting for spreadsheet to load ...") |
| name_box_el = None |
| for sel in ["div.waffle-name-box input", ".cell-input", "#t-name-box", "input.waffle-name-box"]: |
| try: |
| name_box_el = WebDriverWait(driver, 40).until( |
| EC.element_to_be_clickable((By.CSS_SELECTOR, sel)) |
| ) |
| log(f"Sheet loaded (Name Box found via {sel}).") |
| break |
| except TimeoutException: |
| continue |
|
|
| if name_box_el is None: |
| err("Sheet never loaded — cannot write row.") |
| return |
|
|
| time.sleep(2) |
|
|
| try: |
| from datetime import datetime |
| timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") |
|
|
| |
| name_box_el.click() |
| time.sleep(0.2) |
| ActionChains(driver).key_down(Keys.CONTROL).send_keys("a").key_up(Keys.CONTROL).perform() |
| name_box_el.send_keys("A1") |
| name_box_el.send_keys(Keys.RETURN) |
| time.sleep(0.5) |
|
|
| |
| a1_empty = True |
| for fb_sel in ["#t-formula-bar-input", ".cell-input[id*='formula']", "input[id*='formula']"]: |
| try: |
| fb = driver.find_element(By.CSS_SELECTOR, fb_sel) |
| a1_empty = not (fb.get_attribute("value") or "").strip() |
| break |
| except NoSuchElementException: |
| continue |
|
|
| if a1_empty: |
| next_row = "A1" |
| log("Sheet is empty — writing to A1.") |
| else: |
| |
| ActionChains(driver).key_down(Keys.CONTROL).send_keys(Keys.DOWN).key_up(Keys.CONTROL).perform() |
| time.sleep(0.4) |
| name_box_el.click() |
| time.sleep(0.2) |
| addr = (name_box_el.get_attribute("value") or "").strip() |
| if addr: |
| row_num = int("".join(filter(str.isdigit, addr))) + 1 |
| next_row = f"A{row_num}" |
| else: |
| next_row = "A2" |
| log(f"Writing to {next_row}.") |
|
|
| |
| name_box_el.click() |
| time.sleep(0.2) |
| ActionChains(driver).key_down(Keys.CONTROL).send_keys("a").key_up(Keys.CONTROL).perform() |
| name_box_el.send_keys(next_row) |
| name_box_el.send_keys(Keys.RETURN) |
| time.sleep(0.4) |
|
|
| |
| log("Typing row data ...") |
| ActionChains(driver).send_keys(timestamp).perform(); time.sleep(0.15) |
| ActionChains(driver).send_keys(Keys.TAB).perform(); time.sleep(0.15) |
| ActionChains(driver).send_keys("Gmail Lead").perform(); time.sleep(0.15) |
| ActionChains(driver).send_keys(Keys.TAB).perform(); time.sleep(0.15) |
| ActionChains(driver).send_keys(content[:200]).perform(); time.sleep(0.15) |
| ActionChains(driver).send_keys(Keys.RETURN).perform() |
| time.sleep(1) |
|
|
| |
| ActionChains(driver).key_down(Keys.CONTROL).send_keys("s").key_up(Keys.CONTROL).perform() |
| time.sleep(2) |
|
|
| |
| name_box_el.click() |
| time.sleep(0.2) |
| ActionChains(driver).key_down(Keys.CONTROL).send_keys("a").key_up(Keys.CONTROL).perform() |
| name_box_el.send_keys(next_row) |
| name_box_el.send_keys(Keys.RETURN) |
| time.sleep(1) |
| log("Row written and saved — visible in sheet.") |
|
|
| except Exception as e: |
| err(f"Sheets write failed: {e}") |
| import traceback; traceback.print_exc() |
|
|
|
|
| |
| |
| |
|
|
| def phase_discord(driver, email_text): |
| log("--- PHASE 3: Discord ---") |
|
|
| if DISCORD_WEBHOOK_URL == "PASTE_YOUR_DISCORD_WEBHOOK_URL_HERE": |
| warn("DISCORD_WEBHOOK_URL not set — skipping Discord phase.") |
| return |
|
|
| |
| content = email_text.strip() if email_text and len(email_text.strip()) > 10 \ |
| else "New lead identified — follow up required. Source: Gmail inbox triage." |
|
|
| message = f"[NEW LEAD] {content[:200]}" |
|
|
| new_tab(driver, "https://discord.com/app") |
| time.sleep(5) |
|
|
| |
| log("Waiting for Discord message box ...") |
| textbox = None |
| for sel in [ |
| "div[role='textbox'][contenteditable='true']", |
| "div[role='textbox']", |
| "div[data-slate-editor='true']", |
| "div[contenteditable='true'][spellcheck='true']", |
| ]: |
| try: |
| textbox = WebDriverWait(driver, 20).until( |
| EC.element_to_be_clickable((By.CSS_SELECTOR, sel)) |
| ) |
| if textbox.is_displayed(): |
| log(f"Textbox found: {sel}") |
| break |
| textbox = None |
| except TimeoutException: |
| textbox = None |
|
|
| if textbox is None: |
| err("Discord message box not found.") |
| return |
|
|
| driver.execute_script("arguments[0].style.outline='3px solid purple'", textbox) |
| time.sleep(0.5) |
|
|
| |
| try: |
| safe_msg = message.replace('"', "'") |
| ps_cmd = f'Set-Clipboard -Value "{safe_msg}"' |
| subprocess.run(["powershell", "-command", ps_cmd], capture_output=True) |
| time.sleep(0.3) |
| textbox.click() |
| time.sleep(0.3) |
| ActionChains(driver).key_down(Keys.CONTROL).send_keys("a").key_up(Keys.CONTROL).perform() |
| time.sleep(0.1) |
| ActionChains(driver).key_down(Keys.CONTROL).send_keys("v").key_up(Keys.CONTROL).perform() |
| time.sleep(0.8) |
| log("Pasted message via clipboard.") |
| except Exception as e: |
| warn(f"Clipboard paste failed ({e}), falling back to JS insertText ...") |
| textbox.click() |
| time.sleep(0.3) |
| driver.execute_script(""" |
| arguments[0].focus(); |
| document.execCommand('selectAll', false, null); |
| document.execCommand('insertText', false, arguments[1]); |
| """, textbox, message) |
| time.sleep(0.8) |
|
|
| |
| typed = textbox.text.strip() |
| if not typed: |
| warn("Nothing in textbox — message may not send.") |
| else: |
| log(f"Text confirmed in box: {typed[:60]}...") |
|
|
| |
| |
| textbox.click() |
| time.sleep(0.3) |
| textbox.send_keys(Keys.RETURN) |
| time.sleep(1) |
| log("Discord message sent.") |
| time.sleep(2) |
|
|
|
|
| |
| |
| |
|
|
| def phase_meet(driver): |
| log("--- PHASE 4: Google Meet ---") |
| new_tab(driver, "https://meet.google.com/new") |
| time.sleep(5) |
| log(f"Meeting room: {driver.current_url}") |
|
|
|
|
| |
| |
| |
|
|
| def run(): |
| log("=== STARTING AUTOMATION ===") |
|
|
| chrome_exe = find_chrome() |
| log(f"Chrome: {chrome_exe}") |
|
|
| kill_chrome() |
|
|
| try: |
| user_data_dir, profile_dir = clone_profile() |
| except Exception as e: |
| warn(f"Profile clone failed ({e}) — using fresh profile.") |
| user_data_dir = os.path.join(os.environ.get("TEMP", "C:\\Temp"), "chrome_fresh") |
| profile_dir = "Default" |
| os.makedirs(user_data_dir, exist_ok=True) |
|
|
| launch_chrome(user_data_dir, profile_dir, chrome_exe) |
|
|
| try: |
| driver = attach_selenium() |
|
|
| email_text = phase_gmail(driver) |
| time.sleep(3) |
|
|
| phase_sheets(driver, email_text) |
| time.sleep(5) |
|
|
| phase_discord(driver, email_text) |
| time.sleep(5) |
|
|
| phase_meet(driver) |
|
|
| log("=== WORKFLOW COMPLETE ===") |
| time.sleep(10) |
|
|
| except WebDriverException as e: |
| err(f"WebDriver error: {e}") |
| except Exception as e: |
| err(f"Unexpected error: {e}") |
| import traceback; traceback.print_exc() |
| finally: |
| log("Done. Browser left open.") |
|
|
|
|
| if __name__ == "__main__": |
| run() |