ofx-processor/ofx_processor/downloaders/lcl.py

137 lines
5 KiB
Python
Raw Normal View History

import datetime
2021-11-20 15:00:43 +01:00
import time
from pathlib import Path
import click
from selenium import webdriver
from selenium.common.exceptions import NoSuchElementException
2024-11-29 21:36:36 +01:00
from selenium.webdriver import Keys
2021-11-20 15:00:43 +01:00
from selenium.webdriver.common.by import By
from selenium.webdriver.support.select import Select
from ofx_processor.utils.config import (
get_config,
get_config_file_name,
handle_config_file_error,
)
class LclDownloader:
def __init__(self, download_folder: Path = None):
self.config = get_config("lcl")
if not self.config.bank_identifier or not self.config.bank_password:
handle_config_file_error(
get_config_file_name(), "Missing credentials in config file"
)
if not download_folder:
download_folder = Path.home() / "Downloads"
self.download_folder = download_folder.resolve()
options = webdriver.FirefoxOptions()
2024-11-29 21:36:36 +01:00
# options.add_argument("-headless")
2021-11-20 15:00:43 +01:00
options.set_preference("browser.download.dir", str(self.download_folder))
options.set_preference(
"browser.helperApps.neverAsk.saveToDisk", "application/x-ofx"
)
self.selenium = webdriver.Firefox(options=options)
2022-05-17 12:12:19 +02:00
self.selenium.implicitly_wait(30)
2024-10-14 20:19:38 +02:00
self.selenium.set_window_size(1280, 4000)
2021-11-20 15:00:43 +01:00
def download(self) -> str:
2023-06-14 08:54:03 +02:00
try:
return self._download()
except Exception:
screenshot = Path(self.config.screenshot_dir) / "error_download_lcl.png"
2023-06-14 08:54:03 +02:00
self.selenium.save_screenshot(screenshot)
raise
def _download(self) -> str:
2021-11-20 15:00:43 +01:00
selenium = self.selenium
click.secho("Logging in to LCL...", fg="blue")
selenium.get("https://monespace.lcl.fr/connexion")
2021-11-20 15:59:46 +01:00
try:
self._click(By.ID, "popin_tc_privacy_button_2")
2022-09-22 21:23:54 +02:00
click.secho("Accepting privacy policy...", fg="blue")
except NoSuchElementException:
2022-09-22 21:23:54 +02:00
click.secho("Privacy policy already accepted", fg="blue")
2021-11-20 15:00:43 +01:00
login_input = selenium.find_element(By.ID, "identifier")
login_input.send_keys(self.config.bank_identifier)
self._click(By.CLASS_NAME, "app-cta-button")
for char in self.config.bank_password:
self._click(By.CSS_SELECTOR, f".pad-button[value='{char}']")
self._click(By.CLASS_NAME, "app-cta-button")
click.secho("Logged in!", fg="green")
2024-11-29 21:36:36 +01:00
retry = False
while retry:
try:
self._click(By.CSS_SELECTOR, ".app-cta-button--primary")
click.secho("Dismissing welcome screen...", fg="blue")
time.sleep(1)
except NoSuchElementException:
click.secho("No welcome screen found.", fg="blue")
retry = False
2023-06-14 08:02:30 +02:00
self._click(By.CLASS_NAME, "extended-zone")
self._click(By.ID, "export-button")
end = datetime.date.today() - datetime.timedelta(days=1)
2024-11-29 21:36:36 +01:00
start = end - datetime.timedelta(days=30)
2024-11-29 21:36:36 +01:00
self._type_date(By.ID, "mat-input-0", start)
self._type_date(By.ID, "mat-input-1", end)
self._click(By.CSS_SELECTOR, "ui-desktop-select button")
self._click_nth(By.CSS_SELECTOR, "ui-select-list ul li", 2)
self._click(By.CLASS_NAME, "download-button")
2021-11-20 15:00:43 +01:00
click.secho("Found it!", fg="green")
2024-10-14 20:19:38 +02:00
time.sleep(5)
2021-11-20 15:00:43 +01:00
selenium.get("about:downloads")
return self._get_last_download_file_name()
def _click(self, by: By, value: str):
self.selenium.find_element(by, value).click()
def _click_nth(self, by: By, value: str, idx: int):
self.selenium.find_elements(by, value)[idx].click()
2021-11-20 15:00:43 +01:00
def _select(self, by: By, value: str, index: int):
Select(self.selenium.find_element(by, value)).select_by_index(index)
2024-11-29 21:36:36 +01:00
def _type(self, by: By, value: str, value_to_type: str):
self.selenium.find_element(by, value).send_keys(value_to_type)
def _clear(self, by: By, value: str):
self.selenium.find_element(by, value).clear()
def _type_date(self, by: By, value: str, _date: datetime.date):
# The new version of the form doesn't behave nicely when typing the date.
self._type(by, value, "1")
self._type(by, value, Keys.ARROW_LEFT*5)
self._type(by, value, Keys.BACKSPACE*5)
self._type(by, value, _date.strftime("%d/%m"))
self._type(by, value, Keys.ARROW_RIGHT*5)
self._type(by, value, Keys.BACKSPACE*4)
self._type(by, value, _date.strftime("%Y"))
2021-11-20 15:00:43 +01:00
def _get_last_download_file_name(self, wait_seconds: int = 30):
end_time = time.time() + wait_seconds
while time.time() < end_time:
try:
file_name = self.selenium.execute_script(
"return document.querySelector('#contentAreaDownloadsView .downloadMainArea .downloadContainer description:nth-of-type(1)').value"
)
if file_name:
return self.download_folder / file_name
except:
pass
time.sleep(1)
if __name__ == "__main__":
filename = LclDownloader().download()
print(filename)