Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add support for Firefox browser compatibility #707

Merged
merged 22 commits into from
Dec 2, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
9283f37
feat: add support for Firefox browser compatibility
Tverous Nov 2, 2024
8eb24ac
refactor: implement factory pattern for browsers
Tverous Nov 4, 2024
081ae47
refactor: remove duplicates
Tverous Nov 4, 2024
227875f
refactor: remote duplicates and change the browser type names
Tverous Nov 4, 2024
f2f32fb
resolve conflicts
Tverous Nov 8, 2024
0ec4325
remove duplicates
Tverous Nov 8, 2024
e400a25
fix: package path
Tverous Nov 8, 2024
65dc6b4
refactor: move the browser config to config.py instead
Tverous Nov 8, 2024
7a4f4c7
update unit test cases and remove duplicates
Tverous Nov 8, 2024
767b073
Merge branch 'release/v11.15.2024' into firefox-patch
surapuramakhil Nov 11, 2024
03f387f
test: update test cases
Tverous Nov 12, 2024
af7271a
refactor: simplify the browser factory
Tverous Nov 12, 2024
74ac86e
refactor: remove unused package
Tverous Nov 12, 2024
e524214
refactor: browser factory
Tverous Nov 12, 2024
5de4d16
refactor: remove duplicates and using singleton
Tverous Nov 14, 2024
8b30c74
Merge remote-tracking branch 'upstream/release/v11.15.2024' into fire…
Tverous Nov 14, 2024
f289166
Merge branch 'release/v3.1.0' into firefox-patch
surapuramakhil Nov 14, 2024
dc875e6
Update browser_factory.py
surapuramakhil Nov 15, 2024
bca071f
refactor: browser type
Tverous Nov 16, 2024
c160ece
Merge branch 'release/v4.1.0' into firefox-patch
surapuramakhil Nov 19, 2024
84e3cea
Merge branch 'release/v4.1.0' into firefox-patch
surapuramakhil Dec 2, 2024
19b36c1
test code changes, fixed deprication issue
surapuramakhil Dec 2, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 16 additions & 7 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@
import click
from selenium import webdriver
from selenium.webdriver.chrome.service import Service as ChromeService
from selenium.webdriver.firefox.service import Service as FirefoxService
from webdriver_manager.chrome import ChromeDriverManager
from webdriver_manager.firefox import GeckoDriverManager
from selenium.common.exceptions import WebDriverException
from lib_resume_builder_AIHawk import Resume, FacadeManager, ResumeGenerator, StyleManager
from src.utils import chrome_browser_options
from src.utils import chrome_browser_options, firefox_browser_options
from src.llm.llm_manager import GPTAnswerer
from src.aihawk_authenticator import AIHawkAuthenticator
from src.aihawk_bot_facade import AIHawkBotFacade
Expand Down Expand Up @@ -149,11 +151,16 @@ def file_paths_to_dict(resume_file: Path | None, plain_text_resume_file: Path) -

return result

def init_browser() -> webdriver.Chrome:
def init_browser(browser: str = 'chrome') -> webdriver.Chrome | webdriver.Firefox:
surapuramakhil marked this conversation as resolved.
Show resolved Hide resolved
try:
options = chrome_browser_options()
service = ChromeService(ChromeDriverManager().install())
return webdriver.Chrome(service=service, options=options)
if browser == 'chrome':
surapuramakhil marked this conversation as resolved.
Show resolved Hide resolved
options = chrome_browser_options()
service = ChromeService(ChromeDriverManager().install())
return webdriver.Chrome(service=service, options=options)
elif browser == 'firefox':
options = firefox_browser_options()
service = FirefoxService(GeckoDriverManager().install())
return webdriver.Firefox(service=service, options=options)
except Exception as e:
raise RuntimeError(f"Failed to initialize browser: {str(e)}")

Expand All @@ -171,7 +178,7 @@ def create_and_run_bot(parameters, llm_api_key):

job_application_profile_object = JobApplicationProfile(plain_text_resume)

browser = init_browser()
browser = init_browser(parameters['browser'])
surapuramakhil marked this conversation as resolved.
Show resolved Hide resolved
login_component = AIHawkAuthenticator(browser)
apply_component = AIHawkJobManager(browser)
gpt_answerer_component = GPTAnswerer(parameters, llm_api_key)
Expand All @@ -194,8 +201,9 @@ def create_and_run_bot(parameters, llm_api_key):

@click.command()
@click.option('--resume', type=click.Path(exists=True, file_okay=True, dir_okay=False, path_type=Path), help="Path to the resume PDF file")
@click.option('--browser', type=click.Choice(['chrome', 'firefox']), default=None, help='Browser to use for the bot')
@click.option('--collect', is_flag=True, help="Only collects data job information into data.json file")
def main(collect: False, resume: Path = None):
def main(collect: False, browser: str = 'chrome', resume: Path = None):
surapuramakhil marked this conversation as resolved.
Show resolved Hide resolved
try:
data_folder = Path("data_folder")
secrets_file, config_file, plain_text_resume_file, output_folder = FileManager.validate_data_folder(data_folder)
Expand All @@ -206,6 +214,7 @@ def main(collect: False, resume: Path = None):
parameters['uploads'] = FileManager.file_paths_to_dict(resume, plain_text_resume_file)
parameters['outputFileDirectory'] = output_folder
parameters['collectMode'] = collect
parameters['browser'] = browser
surapuramakhil marked this conversation as resolved.
Show resolved Hide resolved

create_and_run_bot(parameters, llm_api_key)
except ConfigError as ce:
Expand Down
49 changes: 49 additions & 0 deletions src/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
logger.add(sys.stderr, level="DEBUG")

chromeProfilePath = os.path.join(os.getcwd(), "chrome_profile", "linkedin_profile")
firefoxProfilePath = os.path.join(os.getcwd(), "firefox_profile", "linkedin_profile")

def ensure_chrome_profile():
logger.debug(f"Ensuring Chrome profile exists at path: {chromeProfilePath}")
Expand All @@ -33,6 +34,16 @@ def ensure_chrome_profile():
logger.debug(f"Created Chrome profile directory: {chromeProfilePath}")
return chromeProfilePath

def ensure_firefox_profile():
surapuramakhil marked this conversation as resolved.
Show resolved Hide resolved
logger.debug(f"Ensuring Firefox profile exists at path: {firefoxProfilePath}")
profile_dir = os.path.dirname(firefoxProfilePath)
if not os.path.exists(profile_dir):
os.makedirs(profile_dir)
logger.debug(f"Created directory for Firefox profile: {profile_dir}")
if not os.path.exists(firefoxProfilePath):
os.makedirs(firefoxProfilePath)
logger.debug(f"Created Firefox profile directory: {firefoxProfilePath}")
return firefoxProfilePath

def is_scrollable(element):
scroll_height = element.get_attribute("scrollHeight")
Expand Down Expand Up @@ -153,6 +164,44 @@ def chrome_browser_options():

return options

def firefox_browser_options():
surapuramakhil marked this conversation as resolved.
Show resolved Hide resolved
logger.debug("Setting Firefox browser options")
ensure_firefox_profile()
options = webdriver.FirefoxOptions()
options.add_argument("--start-maximized")
options.add_argument("--no-sandbox")
options.add_argument("--disable-dev-shm-usage")
options.add_argument("--ignore-certificate-errors")
options.add_argument("--disable-extensions")
options.add_argument("--disable-gpu")
options.add_argument("--disable-background-timer-throttling")
options.add_argument("--disable-backgrounding-occluded-windows")
options.add_argument("--disable-translate")
options.add_argument("--disable-popup-blocking")
options.add_argument("--no-first-run")
options.add_argument("--no-default-browser-check")
options.add_argument("--disable-logging")
options.add_argument("--disable-autofill")
options.add_argument("--disable-plugins")
options.add_argument("--disable-animations")
options.add_argument("--disable-cache")

prefs = {
"permissions.default.image": 2,
"permissions.default.stylesheet": 2,
}
for key, value in prefs.items():
options.set_preference(key, value)

if len(firefoxProfilePath) > 0: # You'll need to define firefoxProfilePath similar to chromeProfilePath
profile = webdriver.FirefoxProfile(firefoxProfilePath)
options.profile = profile
logger.debug(f"Using Firefox profile directory: {firefoxProfilePath}")
else:
options.set_preference("browser.privatebrowsing.autostart", True)
logger.debug("Using Firefox in private browsing mode")

return options

def printred(text):
red = "\033[91m"
Expand Down