From c0773178c3307c7738663c6acfdccd77c088c49e Mon Sep 17 00:00:00 2001 From: David Stosik <816901+davidstosik@users.noreply.github.com> Date: Thu, 27 Feb 2025 22:01:05 +0900 Subject: [PATCH 1/2] Prepare an example on how to run MFYNAB in Docker --- README.md | 43 ++++++++++++++- docker_example/.gitignore | 1 + docker_example/Dockerfile | 19 +++++++ docker_example/Gemfile | 6 +++ docker_example/Gemfile.lock | 62 ++++++++++++++++++++++ docker_example/entrypoint.sh | 7 +++ docker_example/mfynab-cron | 10 ++++ lib/mfynab/money_forward/account_status.rb | 2 +- lib/mfynab/money_forward/session.rb | 8 ++- 9 files changed, 155 insertions(+), 3 deletions(-) create mode 100644 docker_example/.gitignore create mode 100644 docker_example/Dockerfile create mode 100644 docker_example/Gemfile create mode 100644 docker_example/Gemfile.lock create mode 100755 docker_example/entrypoint.sh create mode 100644 docker_example/mfynab-cron diff --git a/README.md b/README.md index 5198022..79a36bd 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,9 @@ MONEYFORWARD_PASSWORD=Passw0rd! YNAB_ACCESS_TOKEN=abunchofcharacters ``` -Alternatively, you can also use something [1Password's CLI](https://developer.1password.com/docs/cli/) + + +Alternatively, you can also use something like [1Password's CLI](https://developer.1password.com/docs/cli/) to avoid storing clear secrets: ``` @@ -71,6 +73,44 @@ op run --env-file=.env -- mfynab mfynab-david.yml After checking out the repo, run `bundle install` to install dependencies. Then, run `bin/rake test` to run the tests. +## Running MFYNAB with cron and Docker + +The `docker-example/` directory contains sample files that'll help you schedule MFYNAB inside a Docker container. + +See the comments in each file for more details on how it works. + +First you'll want to bring your MFYNAB configuration file into this directory: + +```sh +cp path_to/config.yml docker_example/ +``` + +Then you can build the Docker image: + +```sh +docker build -t mfynab docker_example/ +``` + +Finally, you can run the Docker image. Note that you need to pass secrets as environment variables: + +```sh +docker run -d \ + --env YNAB_ACCESS_TOKEN=... \ + --env MONEYFORWARD_USERNAME=... \ + --env MONEYFORWARD_PASSWORD='...' \ + --name mfynab mfynab +``` + +You can also use the 1Password CLI ([documented here](#one-password-cli)) for this step: + +```sh +op run --env-file=.env -- sh -c 'docker run -d \ + --env YNAB_ACCESS_TOKEN=$YNAB_ACCESS_TOKEN \ + --env MONEYFORWARD_USERNAME=$MONEYFORWARD_USERNAME \ + --env MONEYFORWARD_PASSWORD="$MONEYFORWARD_PASSWORD" \ + --name mfynab mfynab' +``` + ## Roadmap ### Deploy/Automate @@ -115,3 +155,4 @@ Previous notes: ``` - Passing logger everywhere feels weird. - Prompt user for captcha and other account extra authentication required by Money Forward? +- One might want to run a single Docker instance for multiple users, but the current setup does not allow that easily. We'll want to bring the secret environment variables into the config file, making it possible to assign them to a given "user", and name them accordingly. diff --git a/docker_example/.gitignore b/docker_example/.gitignore new file mode 100644 index 0000000..1d3ed4c --- /dev/null +++ b/docker_example/.gitignore @@ -0,0 +1 @@ +config.yml diff --git a/docker_example/Dockerfile b/docker_example/Dockerfile new file mode 100644 index 0000000..8fbf692 --- /dev/null +++ b/docker_example/Dockerfile @@ -0,0 +1,19 @@ +FROM ruby:3.4 + +# Install cron and chromium +RUN apt-get update && apt-get install -y cron chromium + +WORKDIR /app + +# Copy Gemfile first and install gems +COPY Gemfile Gemfile.lock ./ +RUN bundle install + +# Copy the rest of the application (this two-step process helps with layer caching) +COPY . . + +# Add cron job +RUN crontab /app/mfynab-cron + +# Run the entrypoint script by default +CMD [ "/app/entrypoint.sh" ] diff --git a/docker_example/Gemfile b/docker_example/Gemfile new file mode 100644 index 0000000..7a0c57c --- /dev/null +++ b/docker_example/Gemfile @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +source "https://rubygems.org" + +# FIXME: point to the latest RubyGems release +gem "mfynab", github: "davidstosik/mfynab", branch: "sto/docker" diff --git a/docker_example/Gemfile.lock b/docker_example/Gemfile.lock new file mode 100644 index 0000000..cd6ac16 --- /dev/null +++ b/docker_example/Gemfile.lock @@ -0,0 +1,62 @@ +GIT + remote: https://github.com/davidstosik/mfynab.git + revision: 73f36f266bbd879983644c8aadc2189b70d22665 + branch: sto/docker + specs: + mfynab (0.1.4) + csv + ferrum (~> 0.15) + nokogiri (~> 1.18) + psych + ynab (~> 3.6) + +GEM + remote: https://rubygems.org/ + specs: + addressable (2.8.7) + public_suffix (>= 2.0.2, < 7.0) + base64 (0.2.0) + concurrent-ruby (1.3.5) + csv (3.3.2) + date (3.4.1) + ethon (0.16.0) + ffi (>= 1.15.0) + ferrum (0.16) + addressable (~> 2.5) + base64 (~> 0.2) + concurrent-ruby (~> 1.1) + webrick (~> 1.7) + websocket-driver (~> 0.7) + ffi (1.17.1) + ffi (1.17.1-arm64-darwin) + mini_portile2 (2.8.8) + nokogiri (1.18.3) + mini_portile2 (~> 2.8.2) + racc (~> 1.4) + nokogiri (1.18.3-arm64-darwin) + racc (~> 1.4) + psych (5.2.3) + date + stringio + public_suffix (6.0.1) + racc (1.8.1) + stringio (3.1.5) + typhoeus (1.4.1) + ethon (>= 0.9.0) + webrick (1.9.1) + websocket-driver (0.7.7) + base64 + websocket-extensions (>= 0.1.0) + websocket-extensions (0.1.5) + ynab (3.6.0) + typhoeus (~> 1.0, >= 1.0.1) + +PLATFORMS + arm64-darwin-24 + ruby + +DEPENDENCIES + mfynab! + +BUNDLED WITH + 2.6.2 diff --git a/docker_example/entrypoint.sh b/docker_example/entrypoint.sh new file mode 100755 index 0000000..eae6f8c --- /dev/null +++ b/docker_example/entrypoint.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env sh + +# Save environment variables to a file, so we can forward them to cron jobs +env >> /etc/environment + +# Run cron in the foreground +cron -f diff --git a/docker_example/mfynab-cron b/docker_example/mfynab-cron new file mode 100644 index 0000000..7274733 --- /dev/null +++ b/docker_example/mfynab-cron @@ -0,0 +1,10 @@ +# Pick up environment variables that were forwarded when starting the container +SHELL=/bin/bash +BASH_ENV=/etc/environment + +# See: +# https://www.reddit.com/r/docker/comments/mzqibu/how_to_correctly_run_cron_in_docker/ + +# Run MFYNAB at 6am, 12pm, and 9pm JST every day +# FIXME: I'd like to be able to use UTC+9 times +0 3,9,21 * * * cd /app; bundle exec mfynab config.yml >/proc/1/fd/1 2>/proc/1/fd/2 diff --git a/lib/mfynab/money_forward/account_status.rb b/lib/mfynab/money_forward/account_status.rb index 7c74028..46c9388 100644 --- a/lib/mfynab/money_forward/account_status.rb +++ b/lib/mfynab/money_forward/account_status.rb @@ -67,7 +67,7 @@ def parse_html_table_row(node) end end - FRESHNESS_LIMIT = 24 * 60 * 60 # 1 day + FRESHNESS_LIMIT = 60 * 60 # 1 hour attr_reader :id_hash, :name, :raw_status, :updated_at, :key, :message diff --git a/lib/mfynab/money_forward/session.rb b/lib/mfynab/money_forward/session.rb index 9ff4dd7..9976d05 100644 --- a/lib/mfynab/money_forward/session.rb +++ b/lib/mfynab/money_forward/session.rb @@ -75,7 +75,13 @@ def http_request(request) end def with_ferrum - browser = Ferrum::Browser.new(timeout: 30, headless: !ENV.key?("NO_HEADLESS")) + browser = Ferrum::Browser.new( + timeout: 30, + headless: !ENV.key?("NO_HEADLESS"), + # FIXME: this was needed to be able to run Chromium headless + # within Docker as root, but I'd rather not rely on it. + browser_options: { "no-sandbox": nil }, + ) user_agent = browser.default_user_agent.sub("HeadlessChrome", "Chrome") browser.headers.add({ "Accept-Language" => "en-US,en", From 1617bd4a9113324fd77f63e949768cd7d098ddea Mon Sep 17 00:00:00 2001 From: David Stosik <816901+davidstosik@users.noreply.github.com> Date: Fri, 28 Feb 2025 21:14:49 +0900 Subject: [PATCH 2/2] WIP --- README.md | 6 + docker_example/Dockerfile | 3 + docker_example/kamal/.kamal/secrets | 8 ++ docker_example/kamal/Dockerfile | 22 ++++ docker_example/kamal/Gemfile | 6 + docker_example/kamal/Gemfile.lock | 145 +++++++++++++++++++++ docker_example/kamal/config/crontab | 14 ++ docker_example/kamal/config/deploy.yml | 27 ++++ docker_example/kamal/config/mfynab.yml | 20 +++ docker_example/kamal/entrypoint.sh | 11 ++ docker_example/mfynab-cron | 2 +- lib/mfynab/money_forward/account_status.rb | 8 ++ lib/mfynab/money_forward/session.rb | 21 ++- 13 files changed, 290 insertions(+), 3 deletions(-) create mode 100644 docker_example/kamal/.kamal/secrets create mode 100644 docker_example/kamal/Dockerfile create mode 100644 docker_example/kamal/Gemfile create mode 100644 docker_example/kamal/Gemfile.lock create mode 100644 docker_example/kamal/config/crontab create mode 100644 docker_example/kamal/config/deploy.yml create mode 100644 docker_example/kamal/config/mfynab.yml create mode 100755 docker_example/kamal/entrypoint.sh diff --git a/README.md b/README.md index 79a36bd..3bed7cd 100644 --- a/README.md +++ b/README.md @@ -156,3 +156,9 @@ Previous notes: - Passing logger everywhere feels weird. - Prompt user for captcha and other account extra authentication required by Money Forward? - One might want to run a single Docker instance for multiple users, but the current setup does not allow that easily. We'll want to bring the secret environment variables into the config file, making it possible to assign them to a given "user", and name them accordingly. + + + +WIP: +Container on Kamal fails to log in because of the "Additional Authentication via Email". +Will have to implement the "log in on deploy" to get it to work. diff --git a/docker_example/Dockerfile b/docker_example/Dockerfile index 8fbf692..37b93bc 100644 --- a/docker_example/Dockerfile +++ b/docker_example/Dockerfile @@ -15,5 +15,8 @@ COPY . . # Add cron job RUN crontab /app/mfynab-cron +# Add a health check to ensure the cron process is running +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD pgrep cron + # Run the entrypoint script by default CMD [ "/app/entrypoint.sh" ] diff --git a/docker_example/kamal/.kamal/secrets b/docker_example/kamal/.kamal/secrets new file mode 100644 index 0000000..142f988 --- /dev/null +++ b/docker_example/kamal/.kamal/secrets @@ -0,0 +1,8 @@ +SECRETS=$(bundle exec kamal secrets fetch --adapter 1password --account my 'Private/GitHub/secrets/registry token' 'Private/Moneyforward/username' 'Private/Moneyforward' 'Private/YNAB/secrets/API token') + +GHCR_REGISTRY_PASSWORD=$(kamal secrets extract 'Private/GitHub/secrets/registry token' $SECRETS) +MONEYFORWARD_USERNAME=$(kamal secrets extract 'Private/Moneyforward/username' $SECRETS) +MONEYFORWARD_PASSWORD=$(kamal secrets extract 'Private/Moneyforward' $SECRETS) +YNAB_ACCESS_TOKEN=$(kamal secrets extract 'Private/YNAB/secrets/API token' $SECRETS) +# FIXME: test cookie validity, renew if necessary, worst case open a browser to let the user log in (eg, `mfynab login`?) +COOKIE_CACHE_PRIME=$(cat ~/.config/mfynab/cookie) diff --git a/docker_example/kamal/Dockerfile b/docker_example/kamal/Dockerfile new file mode 100644 index 0000000..6afd55f --- /dev/null +++ b/docker_example/kamal/Dockerfile @@ -0,0 +1,22 @@ +FROM ruby:3.4 + +# Install cron and chromium +RUN apt-get update && apt-get install -y cron chromium + +WORKDIR /app + +# Copy Gemfile first and install gems +COPY Gemfile Gemfile.lock ./ +RUN bundle install + +# Copy the rest of the application (this two-step process helps with layer caching) +COPY . . + +# Add cron job +RUN crontab /app/config/crontab + +# Add a health check to ensure the cron process is running +HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 CMD pgrep cron + +# Run the entrypoint script by default +CMD [ "/app/entrypoint.sh" ] diff --git a/docker_example/kamal/Gemfile b/docker_example/kamal/Gemfile new file mode 100644 index 0000000..12a7ed4 --- /dev/null +++ b/docker_example/kamal/Gemfile @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +source "https://rubygems.org" + +gem "kamal", "~> 2.5" +gem "mfynab", github: "davidstosik/mfynab", branch: "sto/kamal" diff --git a/docker_example/kamal/Gemfile.lock b/docker_example/kamal/Gemfile.lock new file mode 100644 index 0000000..91a1a68 --- /dev/null +++ b/docker_example/kamal/Gemfile.lock @@ -0,0 +1,145 @@ +GIT + remote: https://github.com/davidstosik/mfynab.git + revision: 764740ee3b6d5f5f3b9a84924134c65e08c5e383 + branch: sto/kamal + specs: + mfynab (0.1.4) + csv + ferrum (~> 0.15) + nokogiri (~> 1.18) + psych + ynab (~> 3.6) + +GEM + remote: https://rubygems.org/ + specs: + activesupport (8.0.1) + base64 + benchmark (>= 0.3) + bigdecimal + concurrent-ruby (~> 1.0, >= 1.3.1) + connection_pool (>= 2.2.5) + drb + i18n (>= 1.6, < 2) + logger (>= 1.4.2) + minitest (>= 5.1) + securerandom (>= 0.3) + tzinfo (~> 2.0, >= 2.0.5) + uri (>= 0.13.1) + addressable (2.8.7) + public_suffix (>= 2.0.2, < 7.0) + base64 (0.2.0) + bcrypt_pbkdf (1.1.1) + benchmark (0.4.0) + bigdecimal (3.1.9) + concurrent-ruby (1.3.5) + connection_pool (2.5.0) + csv (3.3.2) + date (3.4.1) + dotenv (3.1.7) + drb (2.2.1) + ed25519 (1.3.0) + ethon (0.16.0) + ffi (>= 1.15.0) + ferrum (0.16) + addressable (~> 2.5) + base64 (~> 0.2) + concurrent-ruby (~> 1.1) + webrick (~> 1.7) + websocket-driver (~> 0.7) + ffi (1.17.1) + ffi (1.17.1-aarch64-linux-gnu) + ffi (1.17.1-aarch64-linux-musl) + ffi (1.17.1-arm-linux-gnu) + ffi (1.17.1-arm-linux-musl) + ffi (1.17.1-arm64-darwin) + ffi (1.17.1-x86-linux-gnu) + ffi (1.17.1-x86-linux-musl) + ffi (1.17.1-x86_64-linux-gnu) + ffi (1.17.1-x86_64-linux-musl) + i18n (1.14.7) + concurrent-ruby (~> 1.0) + kamal (2.5.3) + activesupport (>= 7.0) + base64 (~> 0.2) + bcrypt_pbkdf (~> 1.0) + concurrent-ruby (~> 1.2) + dotenv (~> 3.1) + ed25519 (~> 1.2) + net-ssh (~> 7.3) + sshkit (>= 1.23.0, < 2.0) + thor (~> 1.3) + zeitwerk (>= 2.6.18, < 3.0) + logger (1.6.6) + mini_portile2 (2.8.8) + minitest (5.25.4) + net-scp (4.1.0) + net-ssh (>= 2.6.5, < 8.0.0) + net-sftp (4.0.0) + net-ssh (>= 5.0.0, < 8.0.0) + net-ssh (7.3.0) + nokogiri (1.18.3) + mini_portile2 (~> 2.8.2) + racc (~> 1.4) + nokogiri (1.18.3-aarch64-linux-gnu) + racc (~> 1.4) + nokogiri (1.18.3-aarch64-linux-musl) + racc (~> 1.4) + nokogiri (1.18.3-arm-linux-gnu) + racc (~> 1.4) + nokogiri (1.18.3-arm-linux-musl) + racc (~> 1.4) + nokogiri (1.18.3-arm64-darwin) + racc (~> 1.4) + nokogiri (1.18.3-x86_64-linux-gnu) + racc (~> 1.4) + nokogiri (1.18.3-x86_64-linux-musl) + racc (~> 1.4) + ostruct (0.6.1) + psych (5.2.3) + date + stringio + public_suffix (6.0.1) + racc (1.8.1) + securerandom (0.4.1) + sshkit (1.24.0) + base64 + logger + net-scp (>= 1.1.2) + net-sftp (>= 2.1.2) + net-ssh (>= 2.8.0) + ostruct + stringio (3.1.5) + thor (1.3.2) + typhoeus (1.4.1) + ethon (>= 0.9.0) + tzinfo (2.0.6) + concurrent-ruby (~> 1.0) + uri (1.0.3) + webrick (1.9.1) + websocket-driver (0.7.7) + base64 + websocket-extensions (>= 0.1.0) + websocket-extensions (0.1.5) + ynab (3.6.0) + typhoeus (~> 1.0, >= 1.0.1) + zeitwerk (2.7.2) + +PLATFORMS + aarch64-linux-gnu + aarch64-linux-musl + arm-linux-gnu + arm-linux-musl + arm64-darwin-24 + ruby + x86-linux-gnu + x86-linux-musl + x86_64-linux-gnu + x86_64-linux-musl + +DEPENDENCIES + kamal (~> 2.5) + mfynab! + +BUNDLED WITH + 2.6.2 diff --git a/docker_example/kamal/config/crontab b/docker_example/kamal/config/crontab new file mode 100644 index 0000000..61fe3a5 --- /dev/null +++ b/docker_example/kamal/config/crontab @@ -0,0 +1,14 @@ +# Pick up environment variables that were forwarded when starting the container +SHELL=/bin/bash +BASH_ENV=/etc/environment + +# See: +# https://www.reddit.com/r/docker/comments/mzqibu/how_to_correctly_run_cron_in_docker/ + +# FIXME: I'd like to be able to use UTC+9 times +# TODO: rethink schedule. From a bank perspective, 6pm and 6am might yield the same results (no activity overnight?) +# Run MFYNAB at 6am, 12pm, and 6pm JST every day +#* 3,9,21 * * * cd /app; bundle exec mfynab config/mfynab.yml >/proc/1/fd/1 2>/proc/1/fd/2 + +# 9, 12, 15, 18, 21 UTC+9 +0 0,3,6,9,12 * * * cd /app; bundle exec mfynab config/mfynab.yml >/proc/1/fd/1 2>/proc/1/fd/2 diff --git a/docker_example/kamal/config/deploy.yml b/docker_example/kamal/config/deploy.yml new file mode 100644 index 0000000..95cf4a9 --- /dev/null +++ b/docker_example/kamal/config/deploy.yml @@ -0,0 +1,27 @@ +service: mfynab +image: davidstosik/mfynab + +primary_role: cron +servers: + cron: + hosts: + - 157.180.23.52 + proxy: false + +registry: + server: ghcr.io + username: davidstosik + password: + - GHCR_REGISTRY_PASSWORD +builder: + arch: amd64 + +env: + secret: + - MONEYFORWARD_USERNAME + - MONEYFORWARD_PASSWORD + - YNAB_ACCESS_TOKEN + - COOKIE_CACHE_PRIME + +aliases: + shell: app exec --interactive --reuse "bash" diff --git a/docker_example/kamal/config/mfynab.yml b/docker_example/kamal/config/mfynab.yml new file mode 100644 index 0000000..c132206 --- /dev/null +++ b/docker_example/kamal/config/mfynab.yml @@ -0,0 +1,20 @@ +David: + ynab_budget: "2022 💪🏻" + months_to_sync: 3 + accounts: + - money_forward_name: "みずほ銀行" + ynab_name: "Mizuho Bank" + - money_forward_name: "楽天銀行" + ynab_name: "Rakuten Bank" + - money_forward_name: "楽天カード" + ynab_name: "Rakuten Card" + - money_forward_name: "モバイルSuica" + ynab_name: "SUICA" + - money_forward_name: "VIEW CARD" + ynab_name: "VIEW card" + - money_forward_name: "三井住友銀行" + ynab_name: "SMBC account" + - money_forward_name: "Amazon.co.jp" + ynab_name: "AmazonJP" + - money_forward_name: "Amazon Mastercard" + ynab_name: "SMBC credit cards" diff --git a/docker_example/kamal/entrypoint.sh b/docker_example/kamal/entrypoint.sh new file mode 100755 index 0000000..323985f --- /dev/null +++ b/docker_example/kamal/entrypoint.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env sh + +# Save environment variables to a file, so we can forward them to cron jobs +env >> /etc/environment + +# Prime the cookie cache +mkdir -p ~/.config/mfynab +echo $COOKIE_CACHE_PRIME > ~/.config/mfynab/cookie + +# Run cron in the foreground +cron -f diff --git a/docker_example/mfynab-cron b/docker_example/mfynab-cron index 7274733..39f0661 100644 --- a/docker_example/mfynab-cron +++ b/docker_example/mfynab-cron @@ -5,6 +5,6 @@ BASH_ENV=/etc/environment # See: # https://www.reddit.com/r/docker/comments/mzqibu/how_to_correctly_run_cron_in_docker/ -# Run MFYNAB at 6am, 12pm, and 9pm JST every day +# Run MFYNAB at 6am, 12pm, and 6pm JST every day # FIXME: I'd like to be able to use UTC+9 times 0 3,9,21 * * * cd /app; bundle exec mfynab config.yml >/proc/1/fd/1 2>/proc/1/fd/2 diff --git a/lib/mfynab/money_forward/account_status.rb b/lib/mfynab/money_forward/account_status.rb index 46c9388..066615c 100644 --- a/lib/mfynab/money_forward/account_status.rb +++ b/lib/mfynab/money_forward/account_status.rb @@ -62,6 +62,14 @@ def parse_html_table_row(node) # - this should not depend on the timezone the server is running in # - this should explicitly fail if parsing is not possible # - what if it's January 1st and last update was last year? + # This is actually broken when running on a server which is in UTC. + # Eg: + # - it is 2025/03/01 18:00 UTC+9 + # - in UTC, that is 2025/03/01 09:00 + # - some account's last update is displayed as "(03/01 10:00)" + # - when parsing that date in a server that runs in UTC, we get + # 2025/03/01 10:00 UTC, which is in the future, whereas it should + # be detected as 8 hours in the past updated_at: Time.parse(node.css("td.created").text[/(?<=\().*?(?=\))/]), ) end diff --git a/lib/mfynab/money_forward/session.rb b/lib/mfynab/money_forward/session.rb index 9976d05..8622194 100644 --- a/lib/mfynab/money_forward/session.rb +++ b/lib/mfynab/money_forward/session.rb @@ -23,7 +23,9 @@ def login with_ferrum do |browser| submit_login_form(browser) - self.cookie = browser.cookies[COOKIE_NAME] + self.cookie = browser.cookies[COOKIE_NAME].tap do |cookie| + write_cookie_cache(cookie) + end rescue Timeout::Error # FIXME: use custom error class raise "Login failed" @@ -31,7 +33,22 @@ def login end def cookie - @cookie || login + @cookie || read_cookie_cache || login + end + + COOKIE_CACHE_PATH = File.join(Dir.home, ".config", "mfynab", "cookie") + + def read_cookie_cache + return unless File.exist?(COOKIE_CACHE_PATH) + + cookie_attributes = JSON.parse(File.read(COOKIE_CACHE_PATH)) + self.cookie = Ferrum::Cookies::Cookie.new(cookie_attributes) + end + + def write_cookie_cache(cookie) + FileUtils.mkdir_p(File.dirname(COOKIE_CACHE_PATH)) + + File.write(COOKIE_CACHE_PATH, JSON.dump(cookie.attributes)) end def http_get(path, params = {})