|
|
import os |
|
|
import time |
|
|
import json |
|
|
import logging |
|
|
from playwright.sync_api import sync_playwright |
|
|
|
|
|
|
|
|
logging.basicConfig( |
|
|
level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s" |
|
|
) |
|
|
logger = logging.getLogger("SessionManager") |
|
|
|
|
|
|
|
|
SESSIONS_DIR = ".sessions" |
|
|
USER_DATA_DIR = ".browser_data" |
|
|
|
|
|
|
|
|
PLATFORMS = { |
|
|
"twitter": { |
|
|
"name": "Twitter/X", |
|
|
"login_url": "https://twitter.com/i/flow/login", |
|
|
"domain": "twitter.com", |
|
|
}, |
|
|
"facebook": { |
|
|
"name": "Facebook", |
|
|
"login_url": "https://www.facebook.com/login", |
|
|
"domain": "facebook.com", |
|
|
}, |
|
|
"linkedin": { |
|
|
"name": "LinkedIn", |
|
|
"login_url": "https://www.linkedin.com/login", |
|
|
"domain": "linkedin.com", |
|
|
}, |
|
|
"reddit": { |
|
|
"name": "Reddit", |
|
|
"login_url": "https://old.reddit.com/login", |
|
|
"domain": "reddit.com", |
|
|
}, |
|
|
"instagram": { |
|
|
"name": "Instagram", |
|
|
"login_url": "https://www.instagram.com/accounts/login/", |
|
|
"domain": "instagram.com", |
|
|
}, |
|
|
} |
|
|
|
|
|
|
|
|
def ensure_dirs(): |
|
|
"""Creates necessary directories.""" |
|
|
if not os.path.exists(SESSIONS_DIR): |
|
|
os.makedirs(SESSIONS_DIR) |
|
|
if not os.path.exists(USER_DATA_DIR): |
|
|
os.makedirs(USER_DATA_DIR) |
|
|
|
|
|
|
|
|
def create_session(platform_key: str): |
|
|
""" |
|
|
Launches a Persistent Browser Context. |
|
|
- Uses FIREFOX for Reddit (bypasses Google-focused bot detection). |
|
|
- Uses CHROMIUM for others (standard compatibility). |
|
|
""" |
|
|
platform = PLATFORMS.get(platform_key) |
|
|
if not platform: |
|
|
logger.error(f"Platform '{platform_key}' not found.") |
|
|
return |
|
|
|
|
|
ensure_dirs() |
|
|
session_file = os.path.join(SESSIONS_DIR, f"{platform_key}_storage_state.json") |
|
|
platform_user_data = os.path.join(USER_DATA_DIR, platform_key) |
|
|
|
|
|
logger.info(f"Starting Persistent Session for {platform['name']}...") |
|
|
|
|
|
with sync_playwright() as p: |
|
|
|
|
|
|
|
|
|
|
|
if platform_key == "reddit": |
|
|
logger.info("Using Firefox Engine (Best for Reddit evasion)...") |
|
|
context = p.firefox.launch_persistent_context( |
|
|
user_data_dir=platform_user_data, |
|
|
headless=False, |
|
|
viewport={"width": 1280, "height": 720}, |
|
|
|
|
|
user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/115.0", |
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
else: |
|
|
logger.info("Using Chromium Engine (Standard)...") |
|
|
context = p.chromium.launch_persistent_context( |
|
|
user_data_dir=platform_user_data, |
|
|
headless=False, |
|
|
viewport={"width": 1280, "height": 720}, |
|
|
user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36", |
|
|
args=[ |
|
|
"--disable-blink-features=AutomationControlled", |
|
|
"--no-sandbox", |
|
|
"--disable-infobars", |
|
|
"--disable-dev-shm-usage", |
|
|
"--disable-browser-side-navigation", |
|
|
"--disable-features=IsolateOrigins,site-per-process", |
|
|
], |
|
|
) |
|
|
|
|
|
|
|
|
page = context.pages[0] if context.pages else context.new_page() |
|
|
page.add_init_script( |
|
|
""" |
|
|
Object.defineProperty(navigator, 'webdriver', { |
|
|
get: () => undefined |
|
|
}); |
|
|
""" |
|
|
) |
|
|
|
|
|
try: |
|
|
logger.info(f"Navigating to {platform['login_url']}...") |
|
|
page.goto(platform["login_url"], wait_until="domcontentloaded") |
|
|
|
|
|
|
|
|
print("\n" + "=" * 50) |
|
|
print(f"ACTION REQUIRED: Log in to {platform['name']} manually.") |
|
|
|
|
|
if platform_key == "reddit": |
|
|
print( |
|
|
">> You are on 'Old Reddit'. The login box is on the right-hand side." |
|
|
) |
|
|
print( |
|
|
">> Once logged in, it might redirect you to New Reddit. That is fine." |
|
|
) |
|
|
|
|
|
print("=" * 50 + "\n") |
|
|
|
|
|
input( |
|
|
f"Press ENTER here ONLY after you see the {platform['name']} Home Feed... " |
|
|
) |
|
|
|
|
|
|
|
|
logger.info("Capturing storage state...") |
|
|
context.storage_state(path=session_file) |
|
|
|
|
|
|
|
|
if os.path.exists(session_file): |
|
|
size = os.path.getsize(session_file) |
|
|
logger.info(f"SUCCESS: Session saved to {session_file} ({size} bytes)") |
|
|
else: |
|
|
logger.error("Failed to save session file.") |
|
|
|
|
|
except Exception as e: |
|
|
logger.error(f"An error occurred: {e}") |
|
|
finally: |
|
|
context.close() |
|
|
|
|
|
|
|
|
def list_sessions(): |
|
|
ensure_dirs() |
|
|
files = [f for f in os.listdir(SESSIONS_DIR) if f.endswith("_storage_state.json")] |
|
|
if not files: |
|
|
print("No sessions found.") |
|
|
else: |
|
|
print(f"Found {len(files)} active sessions:") |
|
|
for f in files: |
|
|
print(f" - {f}") |
|
|
|
|
|
|
|
|
if __name__ == "__main__": |
|
|
while True: |
|
|
print("\n--- Roger Session Manager (Stealth Mode) ---") |
|
|
print("1. Create/Refresh Twitter Session") |
|
|
print("2. Create/Refresh Facebook Session") |
|
|
print("3. Create/Refresh LinkedIn Session") |
|
|
print("4. Create/Refresh Reddit Session") |
|
|
print("5. Create/Refresh Instagram Session") |
|
|
print("6. List Saved Sessions") |
|
|
print("q. Quit") |
|
|
|
|
|
choice = input("Select an option: ").strip().lower() |
|
|
|
|
|
if choice == "1": |
|
|
create_session("twitter") |
|
|
elif choice == "2": |
|
|
create_session("facebook") |
|
|
elif choice == "3": |
|
|
create_session("linkedin") |
|
|
elif choice == "4": |
|
|
create_session("reddit") |
|
|
elif choice == "5": |
|
|
create_session("instagram") |
|
|
elif choice == "6": |
|
|
list_sessions() |
|
|
elif choice == "q": |
|
|
break |
|
|
else: |
|
|
print("Invalid option.") |
|
|
|