sk31415 commited on
Commit
477bd93
·
1 Parent(s): facaa98

hugging face

Browse files
Files changed (7) hide show
  1. Dockerfile +24 -0
  2. HandshakeDMAutomation.py +14 -3
  3. HandshakeJobApply.py +14 -3
  4. README.md +11 -0
  5. app.py +43 -14
  6. browser_utils.py +60 -37
  7. requirements.txt +4 -0
Dockerfile ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.11-slim
2
+
3
+ WORKDIR /app
4
+
5
+ # Install system dependencies
6
+ RUN apt-get update && apt-get install -y \
7
+ build-essential \
8
+ && rm -rf /var/lib/apt/lists/*
9
+
10
+ # Copy requirements and install
11
+ COPY requirements.txt .
12
+ RUN pip install --no-cache-dir -r requirements.txt
13
+
14
+ # Copy application code
15
+ COPY . .
16
+
17
+ # Create directories for uploads and data
18
+ RUN mkdir -p uploads user_resumes user_transcripts generated_cover_letters instance
19
+
20
+ # Expose port 7860 (HF Spaces default)
21
+ EXPOSE 7860
22
+
23
+ # Run the application
24
+ CMD ["python", "app.py"]
HandshakeDMAutomation.py CHANGED
@@ -128,9 +128,19 @@ class HandshakeAutomator:
128
  def setup_driver(self):
129
  """Set up Playwright browser with appropriate options."""
130
  try:
131
- self.browser_manager = BrowserManager(headless=self.headless)
132
- self.page = self.browser_manager.setup()
133
- print(f"Playwright browser initialized successfully")
 
 
 
 
 
 
 
 
 
 
134
 
135
  except Exception as e:
136
  print(f"Error setting up Playwright browser: {str(e)}")
@@ -138,6 +148,7 @@ class HandshakeAutomator:
138
  print("1. Run: pip install playwright")
139
  print("2. Run: playwright install chromium")
140
  print("3. Close any existing browser instances")
 
141
  raise
142
 
143
  def load_contacted_companies(self):
 
128
  def setup_driver(self):
129
  """Set up Playwright browser with appropriate options."""
130
  try:
131
+ browserless_url = os.getenv('BROWSERLESS_URL')
132
+
133
+ if browserless_url:
134
+ # Connect to remote browser (Browserless.io for cloud deployment)
135
+ print("Connecting to remote browser (Browserless.io)...")
136
+ self.browser_manager = BrowserManager(headless=False)
137
+ self.page = self.browser_manager.setup(remote_url=browserless_url)
138
+ print("Connected to remote browser successfully")
139
+ else:
140
+ # Launch local browser
141
+ self.browser_manager = BrowserManager(headless=self.headless)
142
+ self.page = self.browser_manager.setup()
143
+ print("Playwright browser initialized successfully")
144
 
145
  except Exception as e:
146
  print(f"Error setting up Playwright browser: {str(e)}")
 
148
  print("1. Run: pip install playwright")
149
  print("2. Run: playwright install chromium")
150
  print("3. Close any existing browser instances")
151
+ print("4. For cloud deployment, set BROWSERLESS_URL environment variable")
152
  raise
153
 
154
  def load_contacted_companies(self):
HandshakeJobApply.py CHANGED
@@ -52,9 +52,19 @@ class HandshakeJobApplicator:
52
  def setup_driver(self):
53
  """Set up Playwright browser with appropriate options."""
54
  try:
55
- self.browser_manager = BrowserManager(headless=self.headless)
56
- self.page = self.browser_manager.setup()
57
- print(f"Playwright browser initialized successfully")
 
 
 
 
 
 
 
 
 
 
58
 
59
  except Exception as e:
60
  print(f"Error setting up Playwright browser: {str(e)}")
@@ -62,6 +72,7 @@ class HandshakeJobApplicator:
62
  print("1. Run: pip install playwright")
63
  print("2. Run: playwright install chromium")
64
  print("3. Close any existing browser instances")
 
65
  raise
66
 
67
  def load_applied_jobs(self):
 
52
  def setup_driver(self):
53
  """Set up Playwright browser with appropriate options."""
54
  try:
55
+ browserless_url = os.getenv('BROWSERLESS_URL')
56
+
57
+ if browserless_url:
58
+ # Connect to remote browser (Browserless.io for cloud deployment)
59
+ print("Connecting to remote browser (Browserless.io)...")
60
+ self.browser_manager = BrowserManager(headless=False)
61
+ self.page = self.browser_manager.setup(remote_url=browserless_url)
62
+ print("Connected to remote browser successfully")
63
+ else:
64
+ # Launch local browser
65
+ self.browser_manager = BrowserManager(headless=self.headless)
66
+ self.page = self.browser_manager.setup()
67
+ print("Playwright browser initialized successfully")
68
 
69
  except Exception as e:
70
  print(f"Error setting up Playwright browser: {str(e)}")
 
72
  print("1. Run: pip install playwright")
73
  print("2. Run: playwright install chromium")
74
  print("3. Close any existing browser instances")
75
+ print("4. For cloud deployment, set BROWSERLESS_URL environment variable")
76
  raise
77
 
78
  def load_applied_jobs(self):
README.md CHANGED
@@ -1 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
1
  Sends cold emails to employees of companies nationwide, DMs recruiters, and auto-applies to Handshake job postings.
 
1
+ ---
2
+ title: AI Apply
3
+ emoji: 📧
4
+ colorFrom: blue
5
+ colorTo: green
6
+ sdk: docker
7
+ app_port: 7860
8
+ ---
9
+
10
+ # AI Apply
11
+
12
  Sends cold emails to employees of companies nationwide, DMs recruiters, and auto-applies to Handshake job postings.
app.py CHANGED
@@ -13,13 +13,30 @@ import HandshakeJobApply
13
  from models import db, User
14
 
15
  app = Flask(__name__)
16
- app.secret_key = 'your-secret-key-here-change-in-production' # Change this in production
17
- app.config['UPLOAD_FOLDER'] = 'uploads'
18
- app.config['RESUMES_FOLDER'] = 'user_resumes'
19
- app.config['TRANSCRIPTS_FOLDER'] = 'user_transcripts'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  app.config['MAX_CONTENT_LENGTH'] = 25 * 1024 * 1024 # 25MB max file size
21
  app.config['ALLOWED_EXTENSIONS'] = {'pdf'}
22
- app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///users.db'
23
  app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
24
 
25
  # Initialize database
@@ -947,16 +964,28 @@ if __name__ == '__main__':
947
  db.create_all()
948
  print("Database tables created successfully!")
949
 
950
- # Check if production mode is requested
951
- if not('--development' in sys.argv):
952
- # Use Waitress for stable production server (no auto-reload)
953
- print("Starting application in PRODUCTION mode with Waitress...")
954
- print("Server running at http://127.0.0.1:5000")
955
- print("Press CTRL+C to quit")
 
956
  from waitress import serve
957
- serve(app, host='127.0.0.1', port=5000)
958
- else:
 
 
 
 
 
959
  # Use Flask development server with debug mode
960
  print("Starting application in DEVELOPMENT mode with Flask debug server...")
961
- print("Auto-reload enabled. Use --production flag to disable auto-reload.")
962
  app.run(debug=True, host='0.0.0.0', port=5000)
 
 
 
 
 
 
 
13
  from models import db, User
14
 
15
  app = Flask(__name__)
16
+ app.secret_key = os.environ.get('FLASK_SECRET_KEY', 'your-secret-key-here-change-in-production')
17
+
18
+ # Check if running on Hugging Face Spaces (persistent storage at /data)
19
+ if os.environ.get('SPACE_ID'):
20
+ # HF Spaces: use /data for persistent storage
21
+ data_dir = '/data'
22
+ os.makedirs(data_dir, exist_ok=True)
23
+ app.config['UPLOAD_FOLDER'] = f'{data_dir}/uploads'
24
+ app.config['RESUMES_FOLDER'] = f'{data_dir}/user_resumes'
25
+ app.config['TRANSCRIPTS_FOLDER'] = f'{data_dir}/user_transcripts'
26
+ database_url = f'sqlite:///{data_dir}/users.db'
27
+ else:
28
+ # Local development
29
+ app.config['UPLOAD_FOLDER'] = 'uploads'
30
+ app.config['RESUMES_FOLDER'] = 'user_resumes'
31
+ app.config['TRANSCRIPTS_FOLDER'] = 'user_transcripts'
32
+ database_url = os.environ.get('DATABASE_URL', 'sqlite:///users.db')
33
+ # Railway PostgreSQL uses postgres:// but SQLAlchemy needs postgresql://
34
+ if database_url.startswith('postgres://'):
35
+ database_url = database_url.replace('postgres://', 'postgresql://', 1)
36
+
37
  app.config['MAX_CONTENT_LENGTH'] = 25 * 1024 * 1024 # 25MB max file size
38
  app.config['ALLOWED_EXTENSIONS'] = {'pdf'}
39
+ app.config['SQLALCHEMY_DATABASE_URI'] = database_url
40
  app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
41
 
42
  # Initialize database
 
964
  db.create_all()
965
  print("Database tables created successfully!")
966
 
967
+ # Get port from environment (HF Spaces uses 7860, others may set PORT)
968
+ port = int(os.environ.get('PORT', 7860))
969
+
970
+ # Check if running on cloud platform
971
+ if os.environ.get('SPACE_ID'):
972
+ # Hugging Face Spaces
973
+ print(f"Starting application on Hugging Face Spaces (port {port})...")
974
  from waitress import serve
975
+ serve(app, host='0.0.0.0', port=port)
976
+ elif os.environ.get('RAILWAY_ENVIRONMENT') or os.environ.get('RENDER'):
977
+ # Railway or Render
978
+ print(f"Starting application in CLOUD mode with Waitress on port {port}...")
979
+ from waitress import serve
980
+ serve(app, host='0.0.0.0', port=port)
981
+ elif '--development' in sys.argv:
982
  # Use Flask development server with debug mode
983
  print("Starting application in DEVELOPMENT mode with Flask debug server...")
984
+ print("Auto-reload enabled.")
985
  app.run(debug=True, host='0.0.0.0', port=5000)
986
+ else:
987
+ # Local production mode with Waitress
988
+ print(f"Starting application in PRODUCTION mode with Waitress on port 5000...")
989
+ print("Press CTRL+C to quit")
990
+ from waitress import serve
991
+ serve(app, host='127.0.0.1', port=5000)
browser_utils.py CHANGED
@@ -23,43 +23,62 @@ class BrowserManager:
23
  self.browser: Browser = None
24
  self.context = None
25
  self.page: Page = None
 
26
 
27
- def setup(self):
28
- """Initialize browser with anti-detection measures."""
 
 
 
 
 
 
 
 
 
29
  self.playwright = sync_playwright().start()
30
 
31
- # Launch browser with stealth options
32
- self.browser = self.playwright.chromium.launch(
33
- headless=self.headless,
34
- args=[
35
- '--no-sandbox',
36
- '--disable-dev-shm-usage',
37
- '--disable-blink-features=AutomationControlled',
38
- '--disable-gpu',
39
- '--disable-software-rasterizer',
40
- '--window-size=1920,1080',
41
- ]
42
- )
43
-
44
- # Create context with custom user agent
45
- self.context = self.browser.new_context(
46
- user_agent='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
47
- viewport={'width': 1920, 'height': 1080},
48
- java_script_enabled=True,
49
- )
50
-
51
- self.page = self.context.new_page()
52
-
53
- # Apply stealth if available
54
- if HAS_STEALTH:
55
- stealth_sync(self.page)
56
-
57
- # Remove webdriver property
58
- self.page.add_init_script("""
59
- Object.defineProperty(navigator, 'webdriver', {
60
- get: () => undefined
61
- });
62
- """)
 
 
 
 
 
 
 
 
 
63
 
64
  # Set default timeout (equivalent to WebDriverWait 20 seconds)
65
  self.page.set_default_timeout(20000)
@@ -135,14 +154,18 @@ def scroll_to_bottom(page: Page, max_scrolls: int = 10, wait_time: int = 2000):
135
  page.wait_for_timeout(1000)
136
 
137
 
138
- def create_browser(headless=False):
139
  """
140
  Factory function to create a browser manager.
141
 
142
  Args:
143
  headless: Run in headless mode (default: False for debugging)
 
144
 
145
  Returns:
146
- BrowserManager instance
147
  """
148
- return BrowserManager(headless=headless)
 
 
 
 
23
  self.browser: Browser = None
24
  self.context = None
25
  self.page: Page = None
26
+ self.is_remote = False
27
 
28
+ def setup(self, remote_url=None):
29
+ """
30
+ Initialize browser - local or remote via Browserless.io.
31
+
32
+ Args:
33
+ remote_url: WebSocket URL for remote browser (e.g., Browserless.io)
34
+ If None, launches local browser.
35
+
36
+ Returns:
37
+ Page object
38
+ """
39
  self.playwright = sync_playwright().start()
40
 
41
+ if remote_url:
42
+ # Connect to remote browser (Browserless.io)
43
+ self.is_remote = True
44
+ self.browser = self.playwright.chromium.connect_over_cdp(remote_url)
45
+ # Use existing context from remote browser
46
+ self.context = self.browser.contexts[0] if self.browser.contexts else self.browser.new_context()
47
+ self.page = self.context.pages[0] if self.context.pages else self.context.new_page()
48
+ else:
49
+ # Launch local browser with stealth options
50
+ self.is_remote = False
51
+ self.browser = self.playwright.chromium.launch(
52
+ headless=self.headless,
53
+ args=[
54
+ '--no-sandbox',
55
+ '--disable-dev-shm-usage',
56
+ '--disable-blink-features=AutomationControlled',
57
+ '--disable-gpu',
58
+ '--disable-software-rasterizer',
59
+ '--window-size=1920,1080',
60
+ ]
61
+ )
62
+
63
+ # Create context with custom user agent
64
+ self.context = self.browser.new_context(
65
+ user_agent='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
66
+ viewport={'width': 1920, 'height': 1080},
67
+ java_script_enabled=True,
68
+ )
69
+
70
+ self.page = self.context.new_page()
71
+
72
+ # Apply stealth if available (local only)
73
+ if HAS_STEALTH:
74
+ stealth_sync(self.page)
75
+
76
+ # Remove webdriver property (local only)
77
+ self.page.add_init_script("""
78
+ Object.defineProperty(navigator, 'webdriver', {
79
+ get: () => undefined
80
+ });
81
+ """)
82
 
83
  # Set default timeout (equivalent to WebDriverWait 20 seconds)
84
  self.page.set_default_timeout(20000)
 
154
  page.wait_for_timeout(1000)
155
 
156
 
157
+ def create_browser(headless=False, remote_url=None):
158
  """
159
  Factory function to create a browser manager.
160
 
161
  Args:
162
  headless: Run in headless mode (default: False for debugging)
163
+ remote_url: WebSocket URL for remote browser (e.g., Browserless.io)
164
 
165
  Returns:
166
+ BrowserManager instance (call .setup() to initialize)
167
  """
168
+ manager = BrowserManager(headless=headless)
169
+ if remote_url:
170
+ manager.setup(remote_url=remote_url)
171
+ return manager
requirements.txt CHANGED
@@ -7,6 +7,10 @@ flask>=3.0.0
7
  werkzeug>=3.0.0
8
  flask-login>=0.6.3
9
  flask-sqlalchemy>=3.1.1
 
 
 
 
10
 
11
  # Browser Automation (Playwright for Vercel compatibility)
12
  playwright>=1.40.0
 
7
  werkzeug>=3.0.0
8
  flask-login>=0.6.3
9
  flask-sqlalchemy>=3.1.1
10
+ waitress>=2.1.0
11
+
12
+ # Database (PostgreSQL for cloud, SQLite fallback for local)
13
+ psycopg2-binary>=2.9.0
14
 
15
  # Browser Automation (Playwright for Vercel compatibility)
16
  playwright>=1.40.0