import gradio as gr import time import os import subprocess import sys from PIL import Image import io import logging import traceback import random from pathlib import Path # Configure logging logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') logger = logging.getLogger(__name__) # Create screenshots directory if it doesn't exist os.makedirs('screenshots', exist_ok=True) def download_playwright_browsers(): """Make sure Playwright and its browsers are installed properly""" try: logger.info("Installing Playwright browsers...") # First ensure the package is installed subprocess.run( [sys.executable, "-m", "pip", "install", "playwright==1.39.0"], check=True ) # Then install browsers with output printed directly to console logger.info("Running browser installation (this may take a while)...") process = subprocess.run( [sys.executable, "-m", "playwright", "install", "chromium"], check=True ) logger.info("Playwright installation completed successfully") return True except Exception as e: logger.error(f"Error installing Playwright browsers: {e}") return False def save_placeholder_image(name): """Create a placeholder image when no screenshot is available""" timestamp = int(time.time()) filename = f"screenshots/{name}_{timestamp}.png" # Create a simple colored image img = Image.new('RGB', (400, 300), color=(73, 109, 137)) # Add some text if possible try: from PIL import ImageDraw draw = ImageDraw.Draw(img) draw.text((10, 150), f"Instagram Auto-Liker - {name}", fill=(255, 255, 255)) except Exception as e: logger.warning(f"Couldn't add text to image: {str(e)}") img.save(filename) logger.info(f"Saved placeholder image: {filename}") return filename def verify_playwright_installation(): """Verify that Playwright is properly installed by checking for browser binary""" from pathlib import Path import os # Check common installation paths potential_paths = [ Path.home() / ".cache" / "ms-playwright" / "chromium-1084" / "chrome-linux" / "chrome", Path("/home/user/.cache/ms-playwright/chromium-1084/chrome-linux/chrome") ] for path in potential_paths: if path.exists(): logger.info(f"Found Playwright browser at: {path}") return True logger.warning("Playwright browser not found in expected locations") return False def human_delay(min_seconds=1, max_seconds=5): """Wait for a random amount of time to simulate human behavior""" delay = min_seconds + random.random() * (max_seconds - min_seconds) time.sleep(delay) return delay def human_scroll(page, min_pixels=100, max_pixels=800): """Scroll a random amount in a human-like manner""" # Decide scroll direction (occasionally scroll up to look more human) direction = 1 if random.random() < 0.9 else -1 # Decide scroll amount scroll_amount = direction * random.randint(min_pixels, max_pixels) # Sometimes do a smooth scroll, sometimes do a quick scroll if random.random() < 0.7: # Smooth scroll page.evaluate(f"""() => {{ window.scrollBy({{ top: {scroll_amount}, left: 0, behavior: 'smooth' }}); }}""") else: # Quick scroll page.evaluate(f"window.scrollBy(0, {scroll_amount});") # Sometimes perform multiple small scrolls instead of one big one if random.random() < 0.3 and direction > 0: human_delay(0.5, 1.5) second_amount = random.randint(50, 200) page.evaluate(f"window.scrollBy(0, {second_amount});") return scroll_amount def human_click(page, x, y, click_type="normal"): """Perform a more human-like click with mouse movement and occasional double-clicks""" # Move to a random position first (sometimes) if random.random() < 0.6: random_x = random.randint(100, 1000) random_y = random.randint(100, 600) page.mouse.move(random_x, random_y) human_delay(0.1, 0.5) # Move to target with slight randomness jitter_x = x + random.randint(-5, 5) jitter_y = y + random.randint(-3, 3) page.mouse.move(jitter_x, jitter_y) human_delay(0.1, 0.3) # Click behavior if click_type == "double" or (click_type == "normal" and random.random() < 0.1): # Double click (rarely) page.mouse.dblclick(jitter_x, jitter_y) elif click_type == "slow" or (click_type == "normal" and random.random() < 0.2): # Slow click (sometimes) page.mouse.down() human_delay(0.1, 0.4) page.mouse.up() else: # Normal click page.mouse.click(jitter_x, jitter_y) # Sometimes move mouse after clicking if random.random() < 0.4: human_delay(0.2, 0.6) after_x = jitter_x + random.randint(-100, 100) after_y = jitter_y + random.randint(-80, 80) page.mouse.move(after_x, after_y) def handle_automation_warning(page, status_updates): """Handle Instagram's automation warning dialog""" try: # Check for the warning dialog automation_warning_selectors = [ "text=We suspect automated behavior", "text=automated behavior on your account", "button:has-text('Dismiss')", "button:has-text('Ok')", "button:has-text('Close')" ] for selector in automation_warning_selectors: if page.query_selector(selector): status_updates.append("Detected automation warning dialog") # Take a screenshot of the warning try: warning_screenshot = f"screenshots/automation_warning_{int(time.time())}.png" page.screenshot(path=warning_screenshot) status_updates.append("Automation warning screenshot saved") except Exception as e: status_updates.append(f"Error taking warning screenshot: {str(e)}") # Try to find the dismiss button using various selectors dismiss_selectors = [ "button:has-text('Dismiss')", "button:has-text('Ok')", "button:has-text('Close')", "button[type='button']" ] for dismiss_selector in dismiss_selectors: try: dismiss_button = page.query_selector(dismiss_selector) if dismiss_button: # Get button coordinates coords = dismiss_button.bounding_box() x = coords['x'] + coords['width'] / 2 y = coords['y'] + coords['height'] / 2 # Click using human-like behavior human_delay(1, 3) # Think before dismissing human_click(page, x, y) status_updates.append(f"Dismissed automation warning using: {dismiss_selector}") # Wait after dismissing human_delay(2, 4) return True except Exception as e: logger.debug(f"Dismiss button {dismiss_selector} failed: {str(e)}") # If no buttons found, try clicking in the middle of the screen if random.random() < 0.5: human_delay(1, 2) human_click(page, 640, 360) # Click in middle of screen status_updates.append("Attempted to dismiss by clicking middle of screen") human_delay(2, 3) return True return False # No warning detected except Exception as e: status_updates.append(f"Error handling automation warning: {str(e)}") return False def run_instagram_liker(username, password, max_likes): """Run the Instagram auto-liker with Playwright""" status_updates = ["Starting Instagram Auto-Liker with human-like behavior..."] image_path = save_placeholder_image("start") # Check if browsers are installed, if not install them try: status_updates.append("Verifying Playwright installation...") # First try to import try: from playwright.sync_api import sync_playwright except ImportError: status_updates.append("Playwright not installed. Installing now...") download_playwright_browsers() status_updates.append("Playwright installation completed.") # Then verify browser binary exists if not verify_playwright_installation(): status_updates.append("Browser binary not found. Installing browsers...") download_playwright_browsers() # Double check installation worked if not verify_playwright_installation(): status_updates.append("Failed to install browser binary. Please try running the script again.") return "\n".join(status_updates), image_path status_updates.append("Playwright installation verified.") from playwright.sync_api import sync_playwright status_updates.append("Launching Playwright browser...") with sync_playwright() as p: try: # Use random viewport size to look less like automation viewport_width = random.choice([1280, 1366, 1440, 1536, 1600, 1920]) viewport_height = random.choice([720, 768, 800, 864, 900, 1080]) # Launch chromium with specific arguments browser = p.chromium.launch( headless=True, args=[ '--no-sandbox', '--disable-dev-shm-usage', '--disable-gpu', '--disable-software-rasterizer', '--disable-setuid-sandbox' ] ) # Create a browser context with random user agent user_agents = [ 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Safari/605.1.15', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/115.0', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36 Edg/119.0.0.0' ] user_agent = random.choice(user_agents) context = browser.new_context( user_agent=user_agent, viewport={'width': viewport_width, 'height': viewport_height}, locale=random.choice(['en-US', 'en-GB', 'en-CA']), timezone_id=random.choice(['America/New_York', 'America/Los_Angeles', 'Europe/London', 'Asia/Tokyo']) ) # Set random geolocation (if allowed) if random.random() < 0.5: try: context.set_geolocation({ 'latitude': random.uniform(30, 50), 'longitude': random.uniform(-120, -70) }) except: pass # Open a new page page = context.new_page() # Test browser by visiting Google status_updates.append("Testing browser connection...") page.goto("https://www.google.com") human_delay(1, 3) # Wait like a human status_updates.append(f"Browser working. Title: {page.title()}") # Take a screenshot try: test_screenshot = f"screenshots/test_pw_{int(time.time())}.png" page.screenshot(path=test_screenshot) image_path = test_screenshot status_updates.append("Browser screenshot saved") except Exception as e: status_updates.append(f"Error taking screenshot: {str(e)}") # Navigate to Instagram with delay status_updates.append("Navigating to Instagram...") page.goto("https://www.instagram.com/") # Wait for load with natural variation wait_strategy = random.choice(['domcontentloaded', 'networkidle', 'load']) page.wait_for_load_state(wait_strategy) human_delay(2, 5) # Additional human-like wait # Take a screenshot to see what's happening try: landing_screenshot = f"screenshots/landing_pw_{int(time.time())}.png" page.screenshot(path=landing_screenshot) image_path = landing_screenshot status_updates.append("Instagram page loaded, screenshot saved") except Exception as e: status_updates.append(f"Error taking screenshot: {str(e)}") # Random initial interactions (to look human) if random.random() < 0.7: # Maybe move mouse randomly on the page for _ in range(random.randint(1, 3)): random_x = random.randint(100, viewport_width - 100) random_y = random.randint(100, viewport_height - 100) page.mouse.move(random_x, random_y) human_delay(0.3, 1.5) # Maybe scroll a bit before doing anything if random.random() < 0.5: human_scroll(page, 100, 300) human_delay(1, 3) # Click outside any potential popups (more natural location) try: random_x = random.randint(50, 150) random_y = random.randint(50, 100) human_click(page, random_x, random_y) status_updates.append("Clicked to dismiss any initial popups") except Exception as e: status_updates.append(f"Click error: {str(e)}") # Handle cookie dialog if present try: cookie_buttons = [ "text=Accept", "text=Allow", "text=Only allow essential cookies", "button:has-text('Accept All')", "button:has-text('Allow essential and optional cookies')" ] for button_selector in cookie_buttons: try: button = page.query_selector(button_selector) if button: # Get coordinates coords = button.bounding_box() x = coords['x'] + coords['width'] / 2 y = coords['y'] + coords['height'] / 2 # Human-like delay before clicking human_delay(0.8, 2) # Human-like click human_click(page, x, y) status_updates.append(f"Clicked cookie consent button: {button_selector}") human_delay(1, 3) break except Exception as e: logger.debug(f"Button {button_selector} not found: {str(e)}") continue except Exception as e: status_updates.append(f"Cookie dialog handling: {str(e)}") # Interact with login form like a human status_updates.append("Looking for login form...") # Take a screenshot to see what we're working with try: form_screenshot = f"screenshots/login_form_{int(time.time())}.png" page.screenshot(path=form_screenshot) image_path = form_screenshot status_updates.append("Login form screenshot saved") except Exception as e: status_updates.append(f"Error taking screenshot: {str(e)}") # Check for automation warning (might appear right away) if handle_automation_warning(page, status_updates): status_updates.append("Handled initial automation warning") # Try multiple selectors for username input username_selectors = [ "input[name='username']", "input[aria-label='Phone number, username, or email']", "input[placeholder='Phone number, username, or email']", "input[placeholder='Mobile number, username or email']", "input[type='text']" ] username_field = None for selector in username_selectors: try: status_updates.append(f"Trying to find username field with selector: {selector}") field = page.query_selector(selector) if field: username_field = field status_updates.append(f"Found username field with selector: {selector}") break except Exception as e: logger.debug(f"Selector {selector} failed: {str(e)}") if not username_field: status_updates.append("Could not find username field. Instagram may have changed their interface.") # Take final screenshot before closing final_screenshot = f"screenshots/final_error_{int(time.time())}.png" page.screenshot(path=final_screenshot) image_path = final_screenshot browser.close() return "\n".join(status_updates), image_path # Try multiple selectors for password input password_selectors = [ "input[name='password']", "input[aria-label='Password']", "input[placeholder='Password']", "input[type='password']" ] password_field = None for selector in password_selectors: try: status_updates.append(f"Trying to find password field with selector: {selector}") field = page.query_selector(selector) if field: password_field = field status_updates.append(f"Found password field with selector: {selector}") break except Exception as e: logger.debug(f"Selector {selector} failed: {str(e)}") if not password_field: status_updates.append("Could not find password field. Instagram may have changed their interface.") # Take final screenshot before closing final_screenshot = f"screenshots/final_error_{int(time.time())}.png" page.screenshot(path=final_screenshot) image_path = final_screenshot browser.close() return "\n".join(status_updates), image_path # Enter credentials like a human would status_updates.append(f"Entering username: {username}") # Click the username field first (human behavior) username_coords = username_field.bounding_box() username_x = username_coords['x'] + username_coords['width'] / 2 username_y = username_coords['y'] + username_coords['height'] / 2 human_click(page, username_x, username_y) # Type username with human-like timing for char in username: username_field.type(char, delay=random.randint(100, 300)) time.sleep(random.uniform(0.01, 0.15)) # Sometimes humans pause after entering username human_delay(0.5, 2) # Now click password field password_coords = password_field.bounding_box() password_x = password_coords['x'] + password_coords['width'] / 2 password_y = password_coords['y'] + password_coords['height'] / 2 human_click(page, password_x, password_y) # Type password with human-like timing for char in password: password_field.type(char, delay=random.randint(100, 300)) time.sleep(random.uniform(0.01, 0.15)) status_updates.append("Credentials entered") # Take a screenshot of filled form try: creds_screenshot = f"screenshots/credentials_pw_{int(time.time())}.png" page.screenshot(path=creds_screenshot) image_path = creds_screenshot status_updates.append("Credentials screenshot saved") except Exception as e: status_updates.append(f"Error taking screenshot: {str(e)}") # Find login button login_button_selectors = [ "button[type='submit']", "button:has-text('Log in')", "button:has-text('Sign in')", "button:has-text('Log In')", "form button" ] login_button = None for selector in login_button_selectors: try: status_updates.append(f"Trying to find login button with selector: {selector}") button = page.query_selector(selector) if button: login_button = button status_updates.append(f"Found login button with selector: {selector}") break except Exception as e: logger.debug(f"Selector {selector} failed: {str(e)}") if not login_button: status_updates.append("Could not find login button. Instagram may have changed their interface.") # Take final screenshot before closing final_screenshot = f"screenshots/final_error_{int(time.time())}.png" page.screenshot(path=final_screenshot) image_path = final_screenshot browser.close() return "\n".join(status_updates), image_path # Click login button like a human status_updates.append("Clicking login button...") button_coords = login_button.bounding_box() button_x = button_coords['x'] + button_coords['width'] / 2 button_y = button_coords['y'] + button_coords['height'] / 2 # Pause briefly before clicking (human behavior) human_delay(0.5, 1.5) human_click(page, button_x, button_y) # Wait for navigation to complete with human-like variation status_updates.append("Waiting for login process...") wait_time = random.uniform(3, 7) time.sleep(wait_time) # Take post-login screenshot try: post_login_screenshot = f"screenshots/post_login_pw_{int(time.time())}.png" page.screenshot(path=post_login_screenshot) image_path = post_login_screenshot status_updates.append("Post-login screenshot saved") except Exception as e: status_updates.append(f"Error taking screenshot: {str(e)}") # Check for automation warning after login if handle_automation_warning(page, status_updates): status_updates.append("Handled post-login automation warning") # Check if login was successful current_url = page.url if "/accounts/login" in current_url or "/login" in current_url: # Still on login page - check for error messages error_selectors = [ "#slfErrorAlert", "p[data-testid='login-error-message']", "div[role='alert']", "p.sIKKJ", ".coreSpriteAccessUpsell + div" ] error_message = "" for selector in error_selectors: try: el = page.query_selector(selector) if el: error_message = el.text_content() break except: pass if error_message: status_updates.append(f"Login failed: {error_message}") else: status_updates.append("Login failed: Reason unknown. Check your credentials.") browser.close() return "\n".join(status_updates), image_path status_updates.append("Login successful! Now handling post-login dialogs...") # Handle "Save Login Info" popup if it appears try: human_delay(1, 3) # Wait like a human for popup to appear save_info_selectors = [ "text=Save Login Info", "text=Save Your Login Info", "text=Save Info" ] not_now_selectors = [ "text=Not Now", "button:has-text('Not Now')", "button.sqdOP.yWX7d", "button:not(:has-text('Save'))" ] # Check if any save info dialog appears dialog_found = False for selector in save_info_selectors: if page.query_selector(selector): dialog_found = True status_updates.append(f"Save Login Info dialog found with: {selector}") break if dialog_found: # Wait like a human would before deciding human_delay(1, 4) # Try to click "Not Now" dismissed = False for not_now in not_now_selectors: try: button = page.query_selector(not_now) if button: # Get button coordinates coords = button.bounding_box() x = coords['x'] + coords['width'] / 2 y = coords['y'] + coords['height'] / 2 # Click like a human human_click(page, x, y) status_updates.append(f"Dismissed 'Save Login Info' popup using: {not_now}") human_delay(1, 3) dismissed = True break except Exception as e: logger.debug(f"Not Now button {not_now} failed: {str(e)}") continue if not dismissed: status_updates.append("Found Save Login dialog but couldn't dismiss it") else: status_updates.append("No 'Save Login Info' popup detected") except Exception as e: status_updates.append(f"Error handling Save Login dialog: {str(e)}") # Take a screenshot after handling first dialog try: after_save_screenshot = f"screenshots/after_save_dialog_{int(time.time())}.png" page.screenshot(path=after_save_screenshot) image_path = after_save_screenshot status_updates.append("Screenshot after Save Info dialog") except Exception as e: status_updates.append(f"Error taking screenshot: {str(e)}") # Handle notifications popup if it appears try: human_delay(1, 3) # Wait like a human notifications_selectors = [ "text=Turn on Notifications", "text=Enable Notifications", "h2:has-text('Notifications')" ] not_now_selectors = [ "text=Not Now", "button:has-text('Not Now')", "button.sqdOP.yWX7d", "button:not(:has-text('Allow'))" ] # Check if any notifications dialog appears dialog_found = False for selector in notifications_selectors: if page.query_selector(selector): dialog_found = True status_updates.append(f"Notifications dialog found with: {selector}") break if dialog_found: # Wait like a human before deciding human_delay(1, 4) # Try to click "Not Now" dismissed = False for not_now in not_now_selectors: try: button = page.query_selector(not_now) if button: # Get button coordinates coords = button.bounding_box() x = coords['x'] + coords['width'] / 2 y = coords['y'] + coords['height'] / 2 # Click like a human human_click(page, x, y) status_updates.append(f"Dismissed notifications popup using: {not_now}") human_delay(1, 3) dismissed = True break except Exception as e: logger.debug(f"Not Now button {not_now} failed: {str(e)}") continue if not dismissed: status_updates.append("Found Notifications dialog but couldn't dismiss it") else: status_updates.append("No notifications popup detected") except Exception as e: status_updates.append(f"Error handling Notifications dialog: {str(e)}") # Take feed screenshot try: feed_screenshot = f"screenshots/feed_pw_{int(time.time())}.png" page.screenshot(path=feed_screenshot) image_path = feed_screenshot status_updates.append("Feed screenshot saved") except Exception as e: status_updates.append(f"Error taking screenshot: {str(e)}") # Check for automation warning again if handle_automation_warning(page, status_updates): status_updates.append("Handled feed automation warning") status_updates.append("Successfully navigated to Instagram feed!") # Start liking posts with human-like behavior likes_goal = min(max_likes, random.randint(max_likes - 3, max_likes + 3)) status_updates.append(f"Starting to like posts (target: {likes_goal})...") # Like posts likes_count = 0 scroll_count = 0 max_scrolls = random.randint(25, 35) # Wait for the feed to load fully before looking for posts # Function to find and like posts with human-like behavior def find_and_like_post(): """Find and like a single post with human-like behavior""" try: # Use JavaScript to find like buttons directly in the DOM like_button_info = page.evaluate("""() => { // Try different selectors for like buttons (not already liked) const selectors = [ // Generic selectors for Instagram's like buttons "article svg[aria-label='Like']", "article section svg[aria-label='Like']", "svg[aria-label='Like']", "span[class*='_aamw'] svg[aria-label='Like']", "button svg[aria-label='Like']", // Looser selector that might catch more "article button[type='button']:not([aria-pressed='true'])" ]; // Shuffle the selectors to add randomness const shuffled = selectors.sort(() => 0.5 - Math.random()); // Try each selector for (const selector of shuffled) { const elements = Array.from(document.querySelectorAll(selector)); if (elements.length > 0) { // Shuffle the elements to add randomness const shuffledElements = elements.sort(() => 0.5 - Math.random()); // Choose a random element from the first few found const randomIndex = Math.floor(Math.random() * Math.min(3, shuffledElements.length)); const element = shuffledElements[randomIndex]; // Find the actual clickable button (might be a parent) let button = element; if (element.tagName.toLowerCase() === 'svg') { button = element.closest('button') || element; } // Get element position const rect = button.getBoundingClientRect(); // Check if it's visible in viewport if (rect.top >= 0 && rect.left >= 0 && rect.bottom <= window.innerHeight && rect.right <= window.innerWidth) { return { found: true, x: rect.x + rect.width / 2, y: rect.y + rect.height / 2, selector: selector, totalFound: elements.length }; } } } return { found: false }; }""") if like_button_info['found']: # Wait a bit before clicking (as if examining the post) human_delay(1, 4) # Sometimes explore the post before liking if random.random() < 0.3: # Move mouse around post area explore_x = like_button_info['x'] + random.randint(-100, 100) explore_y = like_button_info['y'] + random.randint(-100, 100) page.mouse.move(explore_x, explore_y) human_delay(0.5, 2) # Take screenshot before click try: before_like_screenshot = f"screenshots/before_like_{likes_count+1}_{int(time.time())}.png" page.screenshot(path=before_like_screenshot) except: pass # Click with human-like behavior like_x = like_button_info['x'] like_y = like_button_info['y'] # Add slight randomness to click position human_click(page, like_x, like_y) # Wait for the like to register human_delay(0.5, 2) return True return False except Exception as e: status_updates.append(f"Error finding/clicking like button: {str(e)}") return False # Variable to track consecutive failures consecutive_failures = 0 last_successful_like_time = time.time() # Main loop for scrolling and liking while likes_count < likes_goal and scroll_count < max_scrolls: try: # Take screenshot occasionally to track progress if scroll_count % 5 == 0: try: progress_screenshot = f"screenshots/progress_{scroll_count}_{int(time.time())}.png" page.screenshot(path=progress_screenshot) image_path = progress_screenshot except: pass # First check for automation warnings if handle_automation_warning(page, status_updates): status_updates.append(f"Handled automation warning during scrolling (#{scroll_count})") human_delay(3, 6) # Longer wait after handling warning # Try to like a post with human-like behavior if find_and_like_post(): likes_count += 1 consecutive_failures = 0 last_successful_like_time = time.time() status_updates.append(f"Liked post {likes_count}/{likes_goal}") # Take post-like screenshot occasionally if random.random() < 0.3: try: after_like_screenshot = f"screenshots/after_like_{likes_count}_{int(time.time())}.png" page.screenshot(path=after_like_screenshot) image_path = after_like_screenshot except: pass # Calculate a random human-like delay between likes # Occasionally take longer breaks to seem more human if random.random() < 0.15: # 15% chance of a longer break wait_time = random.uniform(5, 15) status_updates.append(f"Taking a short break ({wait_time:.1f}s)") time.sleep(wait_time) else: # Normal delay between likes (variable) human_delay(2, 7) # Occasionally interact with the feed in other ways to seem human if random.random() < 0.25: # 25% chance after liking random_action = random.choice([ "move_mouse_random", "small_scroll_up", "pause", "long_hover" ]) if random_action == "move_mouse_random": # Move mouse to random position random_x = random.randint(100, viewport_width - 100) random_y = random.randint(100, viewport_height - 100) page.mouse.move(random_x, random_y) human_delay(0.5, 2) elif random_action == "small_scroll_up": # Sometimes scroll back up a bit page.evaluate("window.scrollBy(0, -100);") human_delay(0.5, 2) elif random_action == "pause": # Just pause for a moment human_delay(1, 3) elif random_action == "long_hover": # Hover over something for a bit random_x = random.randint(200, viewport_width - 200) random_y = random.randint(200, viewport_height - 200) page.mouse.move(random_x, random_y) human_delay(2, 4) else: consecutive_failures += 1 status_updates.append(f"No new like buttons found on scroll {scroll_count}") # If we haven't found anything to like in a while, check if we need to scroll if consecutive_failures >= 3 or (time.time() - last_successful_like_time > 20): # Scroll down to load more content scroll_amount = human_scroll(page, min_pixels=random.randint(200, 400), max_pixels=random.randint(500, 800) ) status_updates.append(f"Scrolled down {scroll_amount} pixels to load more posts") # Wait for new content with variable timing wait_time = random.uniform(2, 5) time.sleep(wait_time) # Reset failure counter after scrolling consecutive_failures = 0 scroll_count += 1 else: # Small wait before trying again human_delay(1, 3) # Occasionally take a longer break to seem more human if likes_count > 0 and likes_count % 5 == 0 and random.random() < 0.7: break_time = random.uniform(8, 20) status_updates.append(f"Taking a longer break after {likes_count} likes ({break_time:.1f}s)") time.sleep(break_time) # Verify we haven't exceeded the target if likes_count >= likes_goal: status_updates.append(f"Reached target of {likes_goal} likes") break except Exception as e: status_updates.append(f"Error during like cycle: {str(e)}") # Take error screenshot try: error_screenshot = f"screenshots/error_{int(time.time())}.png" page.screenshot(path=error_screenshot) image_path = error_screenshot except: pass # Continue despite errors after a pause human_delay(3, 6) # Final status final_message = f"Finished! Liked {likes_count} posts." status_updates.append(final_message) # Final screenshot try: final_screenshot = f"screenshots/final_{int(time.time())}.png" page.screenshot(path=final_screenshot) image_path = final_screenshot status_updates.append("Final screenshot saved") except Exception as e: status_updates.append(f"Error taking final screenshot: {str(e)}") # Close the browser browser.close() status_updates.append("Browser closed") except Exception as e: status_updates.append(f"Error during browser interaction: {str(e)}") traceback_msg = traceback.format_exc() logger.error(traceback_msg) status_updates.append("See logs for detailed error information") # Try to close browser in case of error try: browser.close() status_updates.append("Browser closed after error") except: pass except Exception as e: error_message = f"Error with Playwright: {str(e)}" logger.error(error_message) status_updates.append(error_message) logger.error(traceback.format_exc()) return "\n".join(status_updates), image_path # Gradio Interface def create_interface(): with gr.Blocks(title="Instagram Auto-Liker") as app: gr.Markdown("# Instagram Auto-Liker (Human-like)") gr.Markdown(""" Enter your Instagram credentials and the number of posts to like. **Note:** This tool is for educational purposes only. Using automation tools with Instagram may violate their terms of service. Use at your own risk. """) with gr.Row(): with gr.Column(scale=1): username = gr.Textbox(label="Instagram Username") password = gr.Textbox(label="Instagram Password", type="password") max_likes = gr.Slider(minimum=1, maximum=50, value=10, step=1, label="Number of Posts to Like") submit_btn = gr.Button("Start Liking Posts") with gr.Column(scale=2): status_output = gr.Textbox(label="Status Log", lines=15) image_output = gr.Image(label="Latest Screenshot", type="filepath") submit_btn.click( fn=run_instagram_liker, inputs=[username, password, max_likes], outputs=[status_output, image_output] ) return app # Main entry point if __name__ == "__main__": print(f"===== Application Startup at {time.strftime('%Y-%m-%d %H:%M:%S')} =====") # Make sure all dependencies are installed before starting the application print("Installing dependencies...") success = download_playwright_browsers() if not success: print("Failed to install Playwright browsers. Please check your connection and try again.") sys.exit(1) # Start the app print("Starting the application...") app = create_interface() app.launch()