Spaces:
Paused
Paused
Download Telegram_Job_Bot/instahyre_engine.py from telegramjobupload/README: direct link, hf CLI and curl.
- Browser
- Download file 28 kB
-
https://huggingface.co/spaces/telegramjobupload/README/resolve/main/Telegram_Job_Bot/instahyre_engine.py
- Command line
-
hf download hf://spaces/telegramjobupload/README/Telegram_Job_Bot/instahyre_engine.py
-
curl -L -o instahyre_engine.py https://huggingface.co/spaces/telegramjobupload/README/resolve/main/Telegram_Job_Bot/instahyre_engine.py
28 kB
| import time | |
| import random | |
| import os | |
| import json | |
| from playwright.sync_api import sync_playwright | |
| from playwright_stealth import stealth_sync | |
| from bot_features import wait_for_otp | |
| def human_scroll(page): | |
| """Simulates a human scrolling up and down the page while reading.""" | |
| scroll_amount = random.randint(300, 700) | |
| page.mouse.wheel(0, scroll_amount) | |
| time.sleep(random.uniform(1.0, 2.5)) | |
| page.mouse.wheel(0, -random.randint(100, 300)) | |
| time.sleep(random.uniform(0.5, 1.5)) | |
| def human_distraction(context): | |
| """Simulates a human getting distracted by opening a new tab, browsing, and coming back.""" | |
| if random.random() < 0.15: # 15% chance | |
| print("[Instahyre Stealth] User got distracted. Opening a new tab...") | |
| distraction_page = context.new_page() | |
| try: | |
| distraction_page.goto("https://www.google.com", timeout=10000) | |
| time.sleep(random.uniform(2, 5)) | |
| distraction_page.type("textarea[name='q']", "software engineering news", delay=random.randint(50, 150)) | |
| distraction_page.keyboard.press("Enter") | |
| time.sleep(random.uniform(3, 8)) | |
| except Exception: | |
| pass | |
| finally: | |
| distraction_page.close() | |
| time.sleep(random.uniform(1, 2)) | |
| def coffee_break(): | |
| """Simulates the user stepping away from the computer or checking their phone.""" | |
| if random.random() < 0.10: # 10% chance | |
| break_time = random.randint(25, 45) | |
| print(f"[Instahyre Stealth] Taking a coffee break for {break_time} seconds...") | |
| time.sleep(break_time) | |
| def human_text_highlighting(page): | |
| """Simulates a human dragging their mouse to highlight text while reading.""" | |
| try: | |
| width = page.viewport_size['width'] | |
| height = page.viewport_size['height'] | |
| start_x = random.randint(200, width - 200) | |
| start_y = random.randint(200, height - 200) | |
| page.mouse.move(start_x, start_y) | |
| page.mouse.down() | |
| time.sleep(random.uniform(0.1, 0.4)) | |
| page.mouse.move(start_x + random.randint(50, 300), start_y + random.randint(10, 50), steps=10) | |
| time.sleep(random.uniform(0.5, 1.5)) | |
| page.mouse.up() | |
| # Click somewhere to un-highlight | |
| time.sleep(random.uniform(1, 3)) | |
| page.mouse.click(start_x - 50, start_y - 50) | |
| except Exception: | |
| pass | |
| def human_mouse_wandering(page): | |
| """Randomly moves the mouse around the screen to simulate a bored or reading human.""" | |
| try: | |
| width = page.viewport_size['width'] | |
| height = page.viewport_size['height'] | |
| for _ in range(random.randint(2, 4)): | |
| x = random.randint(100, width - 100) | |
| y = random.randint(100, height - 100) | |
| page.mouse.move(x, y, steps=random.randint(10, 25)) | |
| time.sleep(random.uniform(0.2, 0.7)) | |
| except Exception: | |
| pass | |
| def human_typing_with_mistakes(page, selector, text): | |
| """Types text like a human, occasionally making a typo and using backspace to correct it.""" | |
| page.click(selector) | |
| time.sleep(random.uniform(0.2, 0.8)) | |
| for char in text: | |
| # 5% chance to make a typo | |
| if random.random() < 0.05: | |
| wrong_char = random.choice("abcdefghijklmnopqrstuvwxyz") | |
| page.keyboard.press(wrong_char) | |
| time.sleep(random.uniform(0.1, 0.3)) | |
| page.keyboard.press("Backspace") | |
| time.sleep(random.uniform(0.2, 0.5)) | |
| page.keyboard.press(char) | |
| time.sleep(random.uniform(0.05, 0.18)) | |
| def send_instahyre_results_telegram(applied_jobs_list, total_found, success): | |
| """Send Instahyre results to Telegram as a consolidated message.""" | |
| try: | |
| import telebot | |
| bot_token = os.getenv("TELEGRAM_TOKEN") or os.getenv("TELEGRAM_BOT_TOKEN") | |
| chat_id = os.getenv("TELEGRAM_CHAT_ID") | |
| if not chat_id: | |
| chat_id_file = "chat_id.json" | |
| if os.path.exists(chat_id_file): | |
| try: | |
| with open(chat_id_file, "r") as f: | |
| data = json.load(f) | |
| chat_id = data.get("chat_id") | |
| except: pass | |
| if not bot_token or not chat_id: | |
| return | |
| tg_bot = telebot.TeleBot(bot_token, parse_mode=None) | |
| status_icon = "β " if success else "β οΈ" | |
| applied_count = len(applied_jobs_list) | |
| msg = ( | |
| f"{status_icon} *Instahyre Campaign Results*\n" | |
| f"ββββββββββββββββββββββ\n\n" | |
| f"π *Summary:*\n" | |
| f" π Jobs Found: *{total_found}*\n" | |
| f" β Successfully Applied: *{applied_count}*\n\n" | |
| ) | |
| if applied_jobs_list: | |
| msg += "πΌ *Applied To:*\n" | |
| for i, job in enumerate(applied_jobs_list[:20]): | |
| title = job.get("title", "Unknown")[:40] | |
| company = job.get("company", "Unknown")[:25] | |
| msg += f" {i+1}. {title} β _{company}_\n" | |
| if len(applied_jobs_list) > 20: | |
| msg += f" _...+{len(applied_jobs_list)-20} more_\n" | |
| msg += ( | |
| f"\nββββββββββββββββββββββ\n" | |
| f"_Instahyre engine completed. Next auto-run at 11 PM._" | |
| ) | |
| tg_bot.send_message(chat_id, msg, parse_mode="Markdown") | |
| except Exception as e: | |
| print(f"[Instahyre] Telegram notification failed: {e}") | |
| def run_instahyre_mass_apply(skills="Software Engineer Fresher", max_applications=20, email=None, password=None): | |
| """ | |
| Autonomous engine to log into Instahyre and mass-apply to jobs using human-mimicking behavior. | |
| Returns (success: bool, message: str, applied_jobs: list) | |
| """ | |
| # Credentials from params or environment | |
| if not email: | |
| email = os.getenv("INSTAHYRE_EMAIL", "manojprofessional007@gmail.com") | |
| if not password: | |
| password = os.getenv("INSTAHYRE_PASSWORD") | |
| if not password: | |
| return False, "Instahyre password not set. Use `/instahyre your_email | your_password` in Telegram.", [] | |
| print(f"[Instahyre] Starting Stealth Mass-Applier Engine for {email}...") | |
| applied_count = 0 | |
| applied_jobs_list = [] | |
| total_jobs_found = 0 | |
| with sync_playwright() as p: | |
| is_huggingface = "SPACE_ID" in os.environ | |
| # Force headless=False to bypass Cloudflare Turnstile (xvfb handles it on Hugging Face) | |
| browser = p.chromium.launch(headless=False, args=['--no-sandbox', '--disable-blink-features=AutomationControlled']) | |
| auth_file = "instahyre_auth.json" | |
| has_auth = os.path.exists(auth_file) | |
| context_args = { | |
| "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36", | |
| "viewport": {'width': 1920, 'height': 1080} | |
| } | |
| if has_auth: | |
| context_args["storage_state"] = auth_file | |
| context = browser.new_context(**context_args) | |
| page = context.new_page() | |
| stealth_sync(page) | |
| # --- ENHANCEMENT: Advanced Stealth Injection --- | |
| # Bypasses Cloudflare Turnstile and Datadome fingerprinting | |
| page.add_init_script(""" | |
| Object.defineProperty(navigator, 'webdriver', { get: () => undefined }); | |
| window.navigator.chrome = { runtime: {} }; | |
| Object.defineProperty(navigator, 'languages', { get: () => ['en-US', 'en'] }); | |
| Object.defineProperty(navigator, 'plugins', { get: () => [1, 2, 3] }); | |
| const originalQuery = window.navigator.permissions.query; | |
| window.navigator.permissions.query = (parameters) => ( | |
| parameters.name === 'notifications' ? | |
| Promise.resolve({ state: Notification.permission }) : | |
| originalQuery(parameters) | |
| ); | |
| """) | |
| try: | |
| # 0. Check Session | |
| needs_login = True | |
| if has_auth: | |
| print("[Instahyre] Found saved session. Checking if it's still valid...") | |
| page.goto("https://www.instahyre.com/candidate/opportunities/", timeout=60000) | |
| time.sleep(random.uniform(3, 5)) | |
| # Cloudflare check on session opportunities page | |
| for _ in range(4): # Check up to 4 times (40 seconds max) | |
| try: | |
| page_text = page.locator("body").inner_text(timeout=2000).lower() | |
| if "just a moment" in page_text or "cloudflare" in page_text or "security check" in page_text or "verify you are human" in page_text: | |
| print("[Instahyre] Cloudflare protection detected on opportunities page! Waiting 10s for it to resolve...") | |
| time.sleep(10) | |
| human_mouse_wandering(page) | |
| else: | |
| break | |
| except: pass | |
| # Verify we are on opportunities and body text has dashboard characteristics | |
| try: | |
| body_text = page.locator("body").inner_text(timeout=3000).lower() | |
| is_logged_in = ( | |
| "login" not in page.url.lower() and | |
| ("opportunity" in body_text or "job" in body_text or "apply" in body_text or "profile" in body_text or "sign out" in body_text) | |
| ) | |
| except: | |
| is_logged_in = False | |
| if is_logged_in: | |
| print("[Instahyre] Session is valid! Skipping login.") | |
| needs_login = False | |
| else: | |
| print("[Instahyre] Session is invalid or blocked. Navigating to login...") | |
| if needs_login: | |
| # 1. Login | |
| print("[Instahyre] Navigating to login page...") | |
| page.goto("https://www.instahyre.com/login/", timeout=60000) | |
| time.sleep(random.uniform(3, 5)) | |
| # Cloudflare check | |
| for _ in range(4): # Check up to 4 times (40 seconds max) | |
| try: | |
| page_text = page.locator("body").inner_text(timeout=2000).lower() | |
| if "just a moment" in page_text or "cloudflare" in page_text or "security check" in page_text or "verify you are human" in page_text: | |
| print("[Instahyre] Cloudflare protection detected! Waiting 10s for it to resolve...") | |
| time.sleep(10) | |
| human_mouse_wandering(page) | |
| else: | |
| break | |
| except: pass | |
| # Human-like typing with mistakes | |
| human_mouse_wandering(page) | |
| # Try multiple selectors for email field | |
| email_selectors = ["input[name='email']", "input[type='email']", "input[placeholder*='email']", "#email", "input.email-input"] | |
| email_filled = False | |
| for sel in email_selectors: | |
| try: | |
| el = page.locator(sel).first | |
| if el.is_visible(timeout=3000): | |
| el.hover() | |
| el.click() | |
| time.sleep(random.uniform(0.5, 1.0)) | |
| human_typing_with_mistakes(page, sel, email) | |
| email_filled = True | |
| break | |
| except: continue | |
| if not email_filled: | |
| return False, "Could not find email input field on Instahyre login page.", [] | |
| time.sleep(random.uniform(1, 2)) | |
| # Try multiple selectors for password field | |
| human_mouse_wandering(page) | |
| pwd_selectors = ["input[name='password']", "input[type='password']", "input[placeholder*='password']", "#password"] | |
| pwd_filled = False | |
| for sel in pwd_selectors: | |
| try: | |
| el = page.locator(sel).first | |
| if el.is_visible(timeout=3000): | |
| el.hover() | |
| el.click() | |
| time.sleep(random.uniform(0.5, 1.0)) | |
| human_typing_with_mistakes(page, sel, password) | |
| pwd_filled = True | |
| break | |
| except: continue | |
| if not pwd_filled: | |
| return False, "Could not find password input field on Instahyre login page.", [] | |
| time.sleep(random.uniform(1.5, 3)) | |
| print("[Instahyre] Clicking login...") | |
| # Try multiple submit button selectors | |
| submit_selectors = ["button[type='submit']", "input[type='submit']", "button:has-text('Login')", "button:has-text('Sign In')", ".login-btn", ".btn-primary"] | |
| for sel in submit_selectors: | |
| try: | |
| btn = page.locator(sel).first | |
| if btn.is_visible(timeout=2000): | |
| btn.hover() | |
| time.sleep(random.uniform(0.5, 1.2)) | |
| btn.click() | |
| break | |
| except: continue | |
| page.wait_for_load_state("networkidle", timeout=30000) | |
| time.sleep(random.uniform(3, 5)) | |
| # Auto-OTP Handling (If Instahyre asks for email verification) | |
| otp_detected = False | |
| try: | |
| page_text = page.locator("body").inner_text(timeout=5000).lower() | |
| otp_detected = ( | |
| "verify" in page.url or | |
| "verification" in page_text or | |
| "otp" in page_text or | |
| "enter code" in page_text | |
| ) | |
| except: pass | |
| if otp_detected: | |
| print("[Instahyre] OTP Verification Detected! Booting up IMAP Sniper...") | |
| bot_email_pass = os.getenv("BOT_EMAIL_PASSWORD") | |
| if bot_email_pass: | |
| otp_code = wait_for_otp(email, bot_email_pass, timeout_seconds=90) | |
| if otp_code: | |
| # Try to find the OTP input field | |
| otp_selectors = [ | |
| "input[placeholder*='OTP']", "input[placeholder*='otp']", | |
| "input[placeholder*='code']", "input[placeholder*='Code']", | |
| "input[name*='code']", "input[name*='otp']", | |
| "input[type='text']", "input[type='number']" | |
| ] | |
| for sel in otp_selectors: | |
| try: | |
| el = page.locator(sel).first | |
| if el.is_visible(timeout=2000): | |
| human_typing_with_mistakes(page, sel, otp_code) | |
| break | |
| except: continue | |
| page.keyboard.press("Enter") | |
| page.wait_for_load_state("networkidle") | |
| time.sleep(random.uniform(3, 5)) | |
| else: | |
| return False, "Login failed. OTP Verification required but sniper timed out.", [] | |
| else: | |
| return False, "Login failed. OTP required but BOT_EMAIL_PASSWORD is not set in env.", [] | |
| # Check if login was successful | |
| current_url = page.url.lower() | |
| if "login" in current_url and "verify" not in current_url and "dashboard" not in current_url: | |
| return False, "Login failed. Please check your Instahyre password in environment variables.", [] | |
| print("[Instahyre] Login successful! Saving session and navigating to jobs...") | |
| try: | |
| context.storage_state(path=auth_file) | |
| except Exception as e: | |
| print(f"[Instahyre] Could not save session state: {e}") | |
| # End of login block - indent the rest properly if needed. | |
| # But here the search jobs block just continues in the same scope, which is fine since both paths (needs_login or not) end up here. | |
| # 2. Search Jobs β try multiple search URL patterns | |
| search_urls = [ | |
| "https://www.instahyre.com/candidate/opportunities/", | |
| "https://www.instahyre.com/search-jobs/", | |
| ] | |
| jobs_page_loaded = False | |
| for search_url in search_urls: | |
| try: | |
| print(f"[Instahyre] Trying search URL: {search_url}") | |
| page.goto(search_url, timeout=60000) | |
| page.wait_for_load_state("networkidle") | |
| time.sleep(random.uniform(3, 6)) | |
| # Check if page has job listings | |
| body_text = page.locator("body").inner_text(timeout=5000) | |
| if len(body_text) > 200 and ("opportunity" in body_text.lower() or "job" in body_text.lower() or "apply" in body_text.lower()): | |
| jobs_page_loaded = True | |
| print(f"[Instahyre] Job listings found on: {search_url}") | |
| break | |
| except Exception as e: | |
| print(f"[Instahyre] Search URL failed: {e}") | |
| continue | |
| if not jobs_page_loaded: | |
| # Take screenshot for debugging | |
| try: | |
| page.screenshot(path="instahyre_debug.png") | |
| except: pass | |
| return False, f"Could not load job listings. Current page: {page.url}", [] | |
| # 3. Apply Loop β try multiple selectors for job cards | |
| while applied_count < max_applications: | |
| # Find all job cards using multiple selector strategies | |
| job_card_selectors = [ | |
| ".employer-details", | |
| ".opportunity-card", | |
| "[class*='opportunity']", | |
| "[class*='job-card']", | |
| ".card-body", | |
| ".job-listing", | |
| "div[data-opportunity-id]", | |
| ] | |
| job_cards = [] | |
| for sel in job_card_selectors: | |
| try: | |
| cards = page.locator(sel).all() | |
| if cards and len(cards) > 0: | |
| job_cards = cards | |
| print(f"[Instahyre] Found {len(cards)} jobs using selector: {sel}") | |
| break | |
| except: continue | |
| if not job_cards: | |
| # Try getting all clickable elements that look like job entries | |
| print("[Instahyre] No job cards found with known selectors. Trying generic approach...") | |
| try: | |
| # Look for any link or card that contains job-related text | |
| all_links = page.locator("a[href*='opportunity'], a[href*='job'], div.card").all() | |
| if all_links: | |
| job_cards = all_links | |
| print(f"[Instahyre] Found {len(all_links)} potential job elements via generic approach.") | |
| except: pass | |
| if not job_cards: | |
| print("[Instahyre] No more jobs found on this page.") | |
| break | |
| total_jobs_found += len(job_cards) | |
| print(f"[Instahyre] Processing {len(job_cards)} jobs on current page.") | |
| for card in job_cards: | |
| if applied_count >= max_applications: | |
| break | |
| try: | |
| human_mouse_wandering(page) | |
| # 15% chance to get distracted by another tab | |
| human_distraction(context) | |
| # Scroll to the card to simulate human reading | |
| card.scroll_into_view_if_needed() | |
| human_scroll(page) | |
| # Extract job title/company before clicking | |
| job_title = "Unknown" | |
| job_company = "Unknown" | |
| try: | |
| card_text = card.inner_text(timeout=3000) | |
| lines = [l.strip() for l in card_text.split('\n') if l.strip()] | |
| if lines: | |
| job_title = lines[0][:50] | |
| if len(lines) > 1: | |
| job_company = lines[1][:30] | |
| except: pass | |
| # Randomly highlight text on the screen while reading | |
| if random.random() < 0.3: | |
| human_text_highlighting(page) | |
| # Hover over the specific job card before clicking | |
| card.hover() | |
| time.sleep(random.uniform(0.8, 1.5)) | |
| card.click() | |
| time.sleep(random.uniform(3, 5)) | |
| human_mouse_wandering(page) | |
| human_scroll(page) | |
| # Look for the Apply button in the popup/details view β try multiple selectors | |
| apply_selectors = [ | |
| "button:has-text('Apply')", | |
| "button:has-text('Interested')", | |
| "button:has-text('I am interested')", | |
| "a:has-text('Apply')", | |
| ".apply-btn", | |
| "[class*='apply']", | |
| "button.btn-primary:has-text('Apply')", | |
| ] | |
| applied = False | |
| for apply_sel in apply_selectors: | |
| try: | |
| apply_btn = page.locator(apply_sel).first | |
| if apply_btn.is_visible(timeout=2000): | |
| apply_btn.hover() | |
| time.sleep(random.uniform(1.0, 2.5)) | |
| # Randomly click near the button before clicking the button (simulating hesitation) | |
| try: | |
| box = apply_btn.bounding_box() | |
| if box: | |
| page.mouse.click(box['x'] - 20, box['y'] - 20) | |
| time.sleep(random.uniform(0.5, 1.5)) | |
| except: pass | |
| apply_btn.click() | |
| applied_count += 1 | |
| applied_jobs_list.append({ | |
| "title": job_title, | |
| "company": job_company, | |
| }) | |
| print(f"[Instahyre] β Successfully applied to job #{applied_count}: {job_title}") | |
| applied = True | |
| time.sleep(random.uniform(5, 10)) # Human reading the success message | |
| # 10% chance to take a long coffee break after an application | |
| coffee_break() | |
| break | |
| except: continue | |
| if not applied: | |
| print(f"[Instahyre] β Already applied or Apply button missing for: {job_title}. Skipping.") | |
| # Close popup or go back if necessary | |
| page.keyboard.press("Escape") | |
| time.sleep(random.uniform(1, 2)) | |
| except Exception as e: | |
| print(f"[Instahyre] Error clicking job card: {e}") | |
| page.keyboard.press("Escape") | |
| time.sleep(1) | |
| if applied_count >= max_applications: | |
| break | |
| # Go to next page β try multiple selectors | |
| next_selectors = [ | |
| "li.next a", | |
| "a:has-text('Next')", | |
| ".pagination .next", | |
| "button:has-text('Next')", | |
| "a[rel='next']", | |
| ] | |
| next_found = False | |
| for next_sel in next_selectors: | |
| try: | |
| next_btn = page.locator(next_sel).first | |
| if next_btn.is_visible(timeout=3000): | |
| print("[Instahyre] Moving to next page...") | |
| next_btn.click() | |
| page.wait_for_load_state("networkidle") | |
| time.sleep(random.uniform(3, 6)) | |
| next_found = True | |
| break | |
| except: continue | |
| if not next_found: | |
| print("[Instahyre] No next page button found. Search complete.") | |
| break | |
| result_msg = f"Successfully applied to {applied_count} out of {total_jobs_found} jobs found on Instahyre!" | |
| # Send results to Telegram | |
| send_instahyre_results_telegram(applied_jobs_list, total_jobs_found, applied_count > 0) | |
| return applied_count > 0, result_msg, applied_jobs_list | |
| except Exception as e: | |
| error_msg = f"Engine crashed: {str(e)}" | |
| # Send error to Telegram | |
| send_instahyre_results_telegram(applied_jobs_list, total_jobs_found, False) | |
| return False, error_msg, [] | |
| finally: | |
| context.close() | |
| browser.close() | |
| if __name__ == "__main__": | |
| run_instahyre_mass_apply(None, None, 'Software Engineer Fresher', 1) | |
| pass | |