fine_ants 1.8.2 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: ec56d5872cd21507fba0d933ad1832ec6cff5e54
4
- data.tar.gz: 803a523efd4633eda2520730953ae1641d7356ba
2
+ SHA256:
3
+ metadata.gz: '09710593f2fe74e7d9eceabd20925d0cf6adac747eb079d66e6ee73fc8b55843'
4
+ data.tar.gz: aab4bfde4086f433632744ad46820f776f99e59778ed5784ac7217a994b7f2b3
5
5
  SHA512:
6
- metadata.gz: 5fc7d0087d6975cef5de69418c33f764e14bee553964cf88ba643b8dc70de514c6b8d16c333499b8f2d4501d5b6581a4fa8c14c0e2dc94bc9370337063886c14
7
- data.tar.gz: a7a9921598b9fd3072085ab36cf4943399cb22bd8564d4d430665e57f3e3d499882633a3d7872df47372fc2bbfaef3b193cf3913afd655f2c3a3428eb5037392
6
+ metadata.gz: a1c16b6f083f70009ed80b043dbc1cf9e21519ee58c93986ff8dae68a67dbb7c94f747b79858da9e82e48366701de2d5e5079bf9062f4ea0876c323ebd0c654d
7
+ data.tar.gz: 1e852b45ea7e269b64f1b1edfda0b3ff29f8e6aa32767000ead02838995a36112012aeb2f6102a63607e1639a7b5870875736ad69e35352e5e639f5aa4f0d5af
data/Gemfile CHANGED
@@ -1,4 +1,4 @@
1
- source 'https://rubygems.org'
1
+ source "https://rubygems.org"
2
2
 
3
3
  # Specify your gem's dependencies in fine_ants.gemspec
4
4
  gemspec
data/Rakefile CHANGED
@@ -1,2 +1,2 @@
1
1
  require "bundler/gem_tasks"
2
- task :default => :spec
2
+ require "standard/rake"
data/fine_ants.gemspec CHANGED
@@ -1,27 +1,26 @@
1
- # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
1
+ lib = File.expand_path("../lib", __FILE__)
3
2
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require 'fine_ants/version'
3
+ require "fine_ants/version"
5
4
 
6
5
  Gem::Specification.new do |spec|
7
- spec.name = "fine_ants"
8
- spec.version = FineAnts::VERSION
9
- spec.authors = ["Justin Searls"]
10
- spec.email = ["searls@gmail.com"]
6
+ spec.name = "fine_ants"
7
+ spec.version = FineAnts::VERSION
8
+ spec.authors = ["Justin Searls"]
9
+ spec.email = ["searls@gmail.com"]
11
10
 
12
- spec.summary = %q{Opens your browser and finds your bank account status.}
13
- spec.description = %q{Opens your browser and finds your bank account status.}
14
- spec.homepage = "https://github.com/searls/fine_ants"
11
+ spec.summary = "Opens your browser and finds your bank account status."
12
+ spec.homepage = "https://github.com/searls/fine_ants"
15
13
 
16
- spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
- spec.bindir = "exe"
18
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
14
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
15
+ spec.bindir = "exe"
16
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
17
  spec.require_paths = ["lib"]
20
18
 
21
- spec.add_dependency "capybara", "~> 2.7"
22
- spec.add_dependency "selenium-webdriver", "~> 2.53"
19
+ spec.add_dependency "capybara", "~> 3.26"
20
+ spec.add_dependency "selenium-webdriver", "~> 3.142.0"
23
21
 
24
- spec.add_development_dependency "bundler", "~> 1.11"
25
- spec.add_development_dependency "rake", "~> 10.0"
22
+ spec.add_development_dependency "bundle"
23
+ spec.add_development_dependency "rake", "~> 12.3"
26
24
  spec.add_development_dependency "pry"
25
+ spec.add_development_dependency "standard"
27
26
  end
@@ -10,48 +10,48 @@ module FineAnts
10
10
 
11
11
  def login
12
12
  visit "https://www.americanexpress.com"
13
- within "#ssoform" do
14
- fill_in "User ID", :with => @user
15
- fill_in "Password", :with => @password
16
- click_link "Log In"
13
+ form_css = find("form")[:id] == "ssoform" ? "#ssoform" : ".eliloMain"
14
+ within form_css do
15
+ fill_in "User ID", with: @user
16
+ fill_in "Password", with: @password
17
+ click_thing "Log In"
17
18
  end
18
19
  verify_login!
19
20
  end
20
21
 
21
22
  def download
22
23
  visit "https://global.americanexpress.com/accounts"
24
+ find(".card-block")
23
25
  all(".card-block > div").map do |account|
24
- name = account.find('.heading-3').text
25
- owed = account.all("table td").size == 3
26
+ name = account.find(".card > .pad > .heading-3").text
27
+ owed = account.text.include?("Total Balance")
26
28
  {
27
- :adapter => :amex,
28
- :user => @user,
29
- :id => name,
30
- :name => name,
31
- :amount => -1 * BigDecimal.new(if owed
32
- account.find("table td:nth-child(2) span").text.gsub(/[\$,]/,'')
33
- else
34
- "0"
35
- end)
29
+ adapter: :amex,
30
+ user: @user,
31
+ id: name,
32
+ name: name,
33
+ amount: -1 * BigDecimal(if owed
34
+ account.all("table td:nth-child(2) span").first.text.gsub(/[\$,]/, "")
35
+ else
36
+ "0"
37
+ end)
36
38
  }
37
- end.tap do
38
- page.driver.go_back
39
- click_button "Log Out"
40
39
  end
41
40
  end
42
41
 
43
- private
42
+ private
44
43
 
45
44
  def verify_login!
46
- find_button "Log Out"
45
+ page.has_text? "Log Out"
47
46
  rescue
48
47
  raise FineAnts::LoginFailedError.new
49
48
  end
49
+
50
+ def click_thing(locator)
51
+ click_link locator
52
+ rescue
53
+ click_button locator
54
+ end
50
55
  end
51
56
  end
52
57
  end
53
-
54
-
55
-
56
-
57
-
@@ -10,68 +10,68 @@ module FineAnts
10
10
 
11
11
  def login
12
12
  visit "https://wwws.betterment.com/app/login"
13
- fill_in "web_authentication[email]", :with => @user
14
- fill_in "web_authentication[password]", :with => @password
13
+ fill_in "web_authentication[email]", with: @user
14
+ fill_in "web_authentication[password]", with: @password
15
15
  click_button "Log in"
16
16
  begin
17
17
  find_field "web_second_factor_authentication[verification_code]"
18
- return false
18
+ false
19
19
  rescue Capybara::ElementNotFound
20
20
  verify_login!
21
- return true
21
+ true
22
22
  end
23
23
  end
24
24
 
25
25
  def two_factor_response(answer)
26
- fill_in "web_second_factor_authentication[verification_code]", :with => answer
26
+ fill_in "web_second_factor_authentication[verification_code]", with: answer
27
27
  find(".web_second_factor_authentication_trust_device").click
28
28
  click_button "Verify"
29
29
  verify_login!
30
30
  end
31
31
 
32
32
  def download
33
- betterment_accounts = find("h2", text: "Betterment Accounts").
34
- find(:xpath, "../..")
35
- accounts = betterment_accounts.all(".SummaryTable-card")
36
- accounts.map do |account|
33
+ all(".ft-goalAccordionLabel").each do |accordion|
34
+ accordion.click
35
+ sleep 0.3
36
+ end
37
+ accounts = all ".ft-goalExpandedFooter .sc-ContentLayout:nth-child(1) .ft-accounts .ft-subAccountExpandedRow"
38
+ accounts.map { |account|
37
39
  {
38
- :adapter => :betterment,
39
- :user => @user,
40
- :id => id_for(account),
41
- :name => name_for(account),
42
- :amount => total_for(account)
40
+ adapter: :betterment,
41
+ user: @user,
42
+ id: id_for(account),
43
+ name: name_for(account),
44
+ amount: total_for(account)
43
45
  }
44
- end.tap do
45
- find('.sc-Nav-panelTrigger').click
46
- find('.sc-Nav-panelTrigger').click
47
- find('.sc-Nav-panelTrigger .SecondaryNavLogoutAction button', visible: false).click
46
+ }.tap do
47
+ find_logout_button.click
48
48
  end
49
49
  end
50
50
 
51
- private
51
+ private
52
52
 
53
53
  def verify_login!
54
- find ".Dashboard-summaryCards"
54
+ find_logout_button
55
55
  rescue
56
56
  raise FineAnts::LoginFailedError.new
57
57
  end
58
58
 
59
+ def find_logout_button
60
+ find(".Navigation-logoutAction")
61
+ end
62
+
59
63
  def id_for(account)
60
- link = account.find(".SummaryTable-donutContainer a")
61
- link[:href].match(/\/app\/goals\/(\d+)\//)[1]
64
+ account.find("a[data-track-name=\"AutoDepositIntended\"]")["data-track-broker-dealer-account-id"]
62
65
  end
63
66
 
64
67
  def name_for(account)
65
- "#{account.find(".SummaryTable-accountName .SummaryTable-label").text} - #{account.find(".SummaryTable-accountName .u-secondaryHeading").text}"
68
+ "#{account.find(".ft-subAccountName").text} - #{account.ancestor(".sc-Card").find(".ft-goalName").text}"
66
69
  end
67
70
 
68
71
  def total_for(account)
69
- total_string = account.find(".SummaryTable-rightAlignedText .u-secondaryHeading").text
70
- BigDecimal.new(total_string.match(/\$(.*)$/)[1].gsub(/,/,''))
72
+ total_string = account.find(".ft-subAccountBalance").text
73
+ BigDecimal(total_string.match(/\$(.*)$/)[1].delete(","))
71
74
  end
72
75
  end
73
76
  end
74
77
  end
75
-
76
-
77
-
@@ -12,8 +12,8 @@ module FineAnts
12
12
  visit "https://www.chase.com"
13
13
 
14
14
  within_frame(find("#logonbox")) do
15
- fill_in "Username", :with => @user
16
- fill_in "Password", :with => @password
15
+ fill_in "Username", with: @user
16
+ fill_in "Password", with: @password
17
17
  # I'm not happy with this. Chase's site seems to blank out the
18
18
  # username and password fields if you hit "Sign in" immediately after
19
19
  # the dialog appears. We're sleeping to sidestep this behavior.
@@ -43,14 +43,14 @@ module FineAnts
43
43
 
44
44
  accounts = [
45
45
  {
46
- :adapter => :chase,
47
- :user => @user,
48
- :id => find(".ACTNAME").text,
49
- :name => find(".ACTNAME").text,
50
- :type => :credit_card,
51
- :amount => -1 * parse_currency(balance),
52
- :available_amount => parse_currency(available_balance),
53
- :next_due_date => parse_due_date(next_due_date)
46
+ adapter: :chase,
47
+ user: @user,
48
+ id: find(".ACTNAME").text,
49
+ name: find(".ACTNAME").text,
50
+ type: :credit_card,
51
+ amount: -1 * parse_currency(balance),
52
+ available_amount: parse_currency(available_balance),
53
+ next_due_date: parse_due_date(next_due_date)
54
54
  }
55
55
  ]
56
56
 
@@ -61,7 +61,7 @@ module FineAnts
61
61
  private
62
62
 
63
63
  def parse_currency(currency_string)
64
- BigDecimal.new(currency_string.match(/\$(.*)$/)[1].delete(","))
64
+ BigDecimal(currency_string.match(/\$(.*)$/)[1].delete(","))
65
65
  end
66
66
 
67
67
  def parse_due_date(due_date_string)
@@ -76,4 +76,3 @@ module FineAnts
76
76
  end
77
77
  end
78
78
  end
79
-
@@ -10,27 +10,28 @@ module FineAnts
10
10
 
11
11
  def login
12
12
  visit "https://us.etrade.com/e/t/user/login"
13
- fill_in "User ID", :with => @user
14
- fill_in "Password", :with => @password
13
+ fill_in "USER", with: @user
14
+ sleep 0.3
15
+ fill_in "PASSWORD", with: @password
15
16
  click_button "Log on"
16
17
  verify_login!
17
18
  end
18
19
 
19
20
  def download
20
- all("section.account").map do |account|
21
+ all("section.account").map { |account|
21
22
  {
22
- :adapter => :etrade,
23
- :user => @user,
24
- :id => account.find("#acctNum .number-reveal", :visible => false).text(:all),
25
- :name => account.find("a.account-id").text,
26
- :amount => BigDecimal.new(account.find(".table-horizontal .text-right.secondary").text.gsub(/[\$,]/,''))
23
+ adapter: :etrade,
24
+ user: @user,
25
+ id: account.find("#acctNum .number-reveal", visible: false).text(:all),
26
+ name: account.find("a.account-id").text,
27
+ amount: BigDecimal(account.find(".table-horizontal .text-right.secondary").text.gsub(/[\$,]/, ""))
27
28
  }
28
- end.tap do
29
+ }.tap do
29
30
  click_link "Log Off"
30
31
  end
31
32
  end
32
33
 
33
- private
34
+ private
34
35
 
35
36
  def verify_login!
36
37
  find_link "Log Off"
@@ -40,7 +41,3 @@ module FineAnts
40
41
  end
41
42
  end
42
43
  end
43
-
44
-
45
-
46
-
@@ -10,26 +10,26 @@ module FineAnts
10
10
 
11
11
  def login
12
12
  visit "https://www.onlinebanking.pnc.com/alservlet/SignonInitServlet"
13
- fill_in "userId", :with => @user
14
- fill_in "password", :with => @password
15
- click_button "Sign On"
13
+ fill_in "userId", with: @user
14
+ fill_in "password", with: @password
15
+ click_button "Sign In"
16
16
 
17
17
  if all("input[value='Generate Code']").any?
18
18
  click_button "Generate Code"
19
- return false
19
+ false
20
20
  elsif all("input[name=answer]").any?
21
- return false
21
+ false
22
22
  else
23
23
  verify_login!
24
- return true
24
+ true
25
25
  end
26
26
  end
27
27
 
28
28
  def two_factor_response(text_or_answer)
29
29
  if all("input[name=answer]").any? # Security Q
30
- fill_in "answer", :with => text_or_answer
30
+ fill_in "answer", with: text_or_answer
31
31
  else # Text message challenge
32
- fill_in "otpNumber", :with => text_or_answer
32
+ fill_in "otpNumber", with: text_or_answer
33
33
  end
34
34
  click_button "Continue"
35
35
  verify_login!
@@ -37,28 +37,25 @@ module FineAnts
37
37
 
38
38
  def download
39
39
  rows = all("#depositAccountsWrapper tr.depAccount")
40
- rows.map do |row|
40
+ rows.map { |row|
41
41
  cells = row.all("td")
42
42
  {
43
- :adapter => :pnc,
44
- :user => @user,
45
- :id => cells[2].text, # TODO - this won't be unique, only gives last 4 :-/
46
- :name => cells[0].text,
47
- :amount => BigDecimal.new(cells[3].text.match(/\$(.*)$/)[1].gsub(/,/,''))
43
+ adapter: :pnc,
44
+ user: @user,
45
+ id: cells[2].text, # TODO - this won't be unique, only gives last 4 :-/
46
+ name: cells[0].text,
47
+ amount: BigDecimal(cells[3].text.match(/\$(.*)$/)[1].delete(","))
48
48
  }
49
- end.tap { click_link "Sign Off" }
49
+ }.tap { click_link "Sign Off" }
50
50
  end
51
51
 
52
- private
52
+ private
53
53
 
54
54
  def verify_login!
55
55
  find ".lastSignOn"
56
56
  rescue
57
57
  raise FineAnts::LoginFailedError.new
58
58
  end
59
-
60
59
  end
61
60
  end
62
61
  end
63
-
64
-
@@ -18,11 +18,11 @@ module FineAnts
18
18
 
19
19
  def download
20
20
  deposit_table = find("span", text: /Deposit Accounts/)
21
- .find(:xpath, "../..")
22
- .find(".module_container")
21
+ .find(:xpath, "../..")
22
+ .find(".module_container")
23
23
  loan_table = find("span", text: /Loans/)
24
- .find(:xpath, "../..")
25
- .find(".module_container")
24
+ .find(:xpath, "../..")
25
+ .find(".module_container")
26
26
  deposit_transactions = process_table(deposit_table)
27
27
  loan_transactions = process_table(loan_table, type: :loan)
28
28
 
@@ -65,7 +65,7 @@ module FineAnts
65
65
  end
66
66
 
67
67
  def parse_currency(currency_string)
68
- BigDecimal.new(currency_string.match(/\$(.*)$/)[1].delete(","))
68
+ BigDecimal(currency_string.match(/\$(.*)$/)[1].delete(","))
69
69
  end
70
70
 
71
71
  def empty_table?(table)
@@ -0,0 +1,57 @@
1
+ require "bigdecimal"
2
+
3
+ module FineAnts
4
+ module Adapters
5
+ class Schwab
6
+ def initialize(credentials)
7
+ @user = credentials[:user]
8
+ @password = credentials[:password]
9
+ end
10
+
11
+ def login
12
+ visit "https://client.schwab.com/Login/SignOn/CustomerCenterLogin.aspx"
13
+ within_frame 'lmsSecondaryLogin' do
14
+ fill_in "loginIdInput", with: @user
15
+ fill_in "passwordInput", with: @password
16
+ select "Accounts Summary", from: "landingPageOptions"
17
+ click_button "btnLogin"
18
+ end
19
+
20
+ begin
21
+ find("#otp_sms").click
22
+ false
23
+ rescue Capybara::ElementNotFound
24
+ verify_login!
25
+ true
26
+ end
27
+ end
28
+
29
+ def two_factor_response(answer)
30
+ fill_in "securityCode", with: answer
31
+ click_button "continueButton"
32
+ verify_login!
33
+ end
34
+
35
+ def download
36
+ rows = all(".account-row")
37
+ rows.map { |row|
38
+ {
39
+ adapter: :schwab,
40
+ user: @user,
41
+ id: row.all(".account.number").first.text.strip,
42
+ name: row.all(".nickName-wrapper").first.text.strip,
43
+ amount: BigDecimal(row.all(".balance-container-cs .values-wrapper").first.text.match(/\$(.*)$/)[1].delete(","))
44
+ }
45
+ }.tap { click_button "Log Out" }
46
+ end
47
+
48
+ private
49
+
50
+ def verify_login!
51
+ find_button "Log Out"
52
+ rescue Capybara::ElementNotFound
53
+ raise FineAnts::LoginFailedError.new
54
+ end
55
+ end
56
+ end
57
+ end
@@ -12,20 +12,20 @@ module FineAnts
12
12
  visit "https://www.simple.com"
13
13
  click_link "Log In"
14
14
 
15
- fill_in "login_username", :with => @user
16
- fill_in "login_password", :with => @password
15
+ fill_in "login_username", with: @user
16
+ fill_in "login_password", with: @password
17
17
  click_button "Sign In"
18
18
  begin
19
19
  find_field "Enter 4-digit code"
20
- return false
20
+ false
21
21
  rescue Capybara::ElementNotFound
22
22
  verify_login!
23
- return true
23
+ true
24
24
  end
25
25
  end
26
26
 
27
27
  def two_factor_response(answer)
28
- fill_in "Enter 4-digit code", :with => answer
28
+ fill_in "Enter 4-digit code", with: answer
29
29
  click_button "Verify"
30
30
  end
31
31
 
@@ -36,23 +36,24 @@ module FineAnts
36
36
 
37
37
  [
38
38
  {
39
- :adapter => :simple,
40
- :user => @user,
41
- :id => "#{user_name}",
42
- :name => "#{user_name}",
43
- :amount => parse_currency(balance),
44
- :available_amount => parse_currency(available_balance),
39
+ adapter: :simple,
40
+ user: @user,
41
+ id: user_name.to_s,
42
+ name: user_name.to_s,
43
+ amount: parse_currency(balance),
44
+ available_amount: parse_currency(available_balance)
45
45
  }
46
- ].tap{ logout! }
46
+ ].tap { logout! }
47
47
  end
48
48
 
49
49
  private
50
+
50
51
  def logout!
51
52
  visit "https://bank.simple.com/signout"
52
53
  end
53
54
 
54
55
  def parse_currency(currency_string)
55
- BigDecimal.new(currency_string.match(/\$?(.*)$/)[1].delete(","))
56
+ BigDecimal(currency_string.match(/\$?(.*)$/)[1].delete(","))
56
57
  end
57
58
 
58
59
  def verify_login!
@@ -63,4 +64,3 @@ module FineAnts
63
64
  end
64
65
  end
65
66
  end
66
-
@@ -12,8 +12,8 @@ module FineAnts
12
12
  visit "https://www.simple.com"
13
13
  click_link "Log In"
14
14
 
15
- fill_in "username", :with => @user
16
- fill_in "passphrase", :with => @password
15
+ fill_in "username", with: @user
16
+ fill_in "passphrase", with: @password
17
17
  click_button "Sign in"
18
18
  verify_login!
19
19
  end
@@ -25,23 +25,24 @@ module FineAnts
25
25
 
26
26
  [
27
27
  {
28
- :adapter => :simple_bancorp,
29
- :user => @user,
30
- :id => "#{user_name}",
31
- :name => "#{user_name}",
32
- :amount => parse_currency(balance),
33
- :available_amount => parse_currency(available_balance),
28
+ adapter: :simple_bancorp,
29
+ user: @user,
30
+ id: user_name.to_s,
31
+ name: user_name.to_s,
32
+ amount: parse_currency(balance),
33
+ available_amount: parse_currency(available_balance)
34
34
  }
35
- ].tap{ logout! }
35
+ ].tap { logout! }
36
36
  end
37
37
 
38
38
  private
39
+
39
40
  def logout!
40
41
  visit "https://bank.simple.com/signout"
41
42
  end
42
43
 
43
44
  def parse_currency(currency_string)
44
- BigDecimal.new(currency_string.match(/\$?(.*)$/)[1].gsub(/,/,''))
45
+ BigDecimal(currency_string.match(/\$?(.*)$/)[1].delete(","))
45
46
  end
46
47
 
47
48
  def verify_login!
@@ -52,4 +53,3 @@ module FineAnts
52
53
  end
53
54
  end
54
55
  end
55
-
@@ -10,20 +10,20 @@ module FineAnts
10
10
 
11
11
  def login
12
12
  visit "https://www.nrsstrsoh.org/iApp/tcm/nrsstrsoh/index.jsp"
13
- fill_in "Username", :with => @user
14
- fill_in "Password (Case sensitive)", :with => @password
13
+ fill_in "Username", with: @user
14
+ fill_in "Password (Case sensitive)", with: @password
15
15
  click_button "Log In"
16
16
  begin
17
17
  find_field "contactPoint"
18
- return false
18
+ false
19
19
  rescue Capybara::ElementNotFound
20
20
  verify_login!
21
- return true
21
+ true
22
22
  end
23
23
  end
24
24
 
25
25
  def two_factor_response(answer)
26
- fill_in "confirmationCode", :with => answer
26
+ fill_in "confirmationCode", with: answer
27
27
  find_field("continue").click
28
28
  begin
29
29
  find "#rememberTrue"
@@ -37,18 +37,18 @@ module FineAnts
37
37
  def download
38
38
  [
39
39
  {
40
- :adapter => :strs,
41
- :user => @user,
42
- :id => find(".plan-info-plan .disabled-phone-link").text,
43
- :name => find(".plan-info-plan").text,
44
- :amount => BigDecimal.new(find(".dash-health-alt__total_number").text.gsub(/[\$,]/,''))
40
+ adapter: :strs,
41
+ user: @user,
42
+ id: find(".plan-info-plan .disabled-phone-link").text,
43
+ name: find(".plan-info-plan").text,
44
+ amount: BigDecimal(find(".dash-health-alt__total_number").text.gsub(/[\$,\s]/, ""))
45
45
  }
46
46
  ].tap do
47
47
  click_link "Log Out"
48
48
  end
49
49
  end
50
50
 
51
- private
51
+ private
52
52
 
53
53
  def verify_login!
54
54
  find(".dash-health-alt__current_total_balance")
@@ -58,8 +58,3 @@ module FineAnts
58
58
  end
59
59
  end
60
60
  end
61
-
62
-
63
-
64
-
65
-
@@ -11,8 +11,8 @@ module FineAnts
11
11
  def login
12
12
  visit "https://rcam.target.com/default.aspx"
13
13
 
14
- fill_in "Login_UserName", :with => @user
15
- fill_in "Login_Password", :with => @password
14
+ fill_in "Login_UserName", with: @user
15
+ fill_in "Login_Password", with: @password
16
16
  find("#Login_btnSignIn_btnSignIn").click
17
17
 
18
18
  verify_login!
@@ -26,14 +26,14 @@ module FineAnts
26
26
 
27
27
  [
28
28
  {
29
- :adapter => :target,
30
- :user => @user,
31
- :id => "REDcard #{card_number}",
32
- :name => "REDcard #{card_number}",
33
- :type => :credit_card,
34
- :amount => -1 * parse_currency(balance),
35
- :available_amount => parse_currency(available_balance),
36
- :next_due_date => parse_due_date(next_due_date)
29
+ adapter: :target,
30
+ user: @user,
31
+ id: "REDcard #{card_number}",
32
+ name: "REDcard #{card_number}",
33
+ type: :credit_card,
34
+ amount: -1 * parse_currency(balance),
35
+ available_amount: parse_currency(available_balance),
36
+ next_due_date: parse_due_date(next_due_date)
37
37
  }
38
38
  ]
39
39
  end
@@ -41,7 +41,7 @@ module FineAnts
41
41
  private
42
42
 
43
43
  def parse_currency(currency_string)
44
- BigDecimal.new(currency_string.match(/\$(.*)$/)[1].delete(","))
44
+ BigDecimal(currency_string.match(/\$(.*)$/)[1].delete(","))
45
45
  end
46
46
 
47
47
  def parse_due_date(date_string)
@@ -57,4 +57,3 @@ module FineAnts
57
57
  end
58
58
  end
59
59
  end
60
-
@@ -0,0 +1,61 @@
1
+ require "bigdecimal"
2
+
3
+ module FineAnts
4
+ module Adapters
5
+ class UfbDirect
6
+ def initialize(credentials)
7
+ @user = credentials[:user]
8
+ @password = credentials[:password]
9
+ end
10
+
11
+ def login
12
+ visit "https://onlinebanking.ufbdirect.com/auth/login"
13
+ sleep 2
14
+ click_link "Login"
15
+ sleep 2
16
+ fill_in "Username", with: @user
17
+ fill_in "Password", with: @password
18
+ sleep 1
19
+ click_button "Login"
20
+ begin
21
+ find ".multi-factor"
22
+ click_button "btn-sms"
23
+ false
24
+ rescue Capybara::ElementNotFound
25
+ verify_login!
26
+ true
27
+ end
28
+ end
29
+
30
+ def two_factor_response(answer)
31
+ fill_in "access-code-entry", with: answer
32
+ click_button "Continue"
33
+ verify_login!
34
+ end
35
+
36
+ def download
37
+ sleep 5
38
+ rows = all(".details-container")
39
+ rows.map { |row|
40
+ {
41
+ adapter: :ufb_direct,
42
+ user: @user,
43
+ id: row.find(".details-container__account-name").text,
44
+ name: row.find(".details-container__account-name").text,
45
+ amount: BigDecimal(
46
+ row.find(".details-container__account-amount").text.match(/\$(.*)$/)[1].delete(",")
47
+ )
48
+ }
49
+ }
50
+ end
51
+
52
+ private
53
+
54
+ def verify_login!
55
+ find ".details-container"
56
+ rescue Capybara::ElementNotFound
57
+ raise FineAnts::LoginFailedError.new
58
+ end
59
+ end
60
+ end
61
+ end
@@ -10,47 +10,47 @@ module FineAnts
10
10
 
11
11
  def login
12
12
  visit "https://personal.vanguard.com/us/hnwnesc/nesc/LoginPage"
13
- fill_in "LoginForm:USER", :with => @user
14
- fill_in "LoginForm:PASSWORD-blocked", :with => @password
13
+ fill_in "LoginForm:USER", with: @user
14
+ sleep 0.2
15
+ fill_in "LoginForm:PASSWORD-blocked", with: @password
15
16
  click_button "LoginForm:submitInput"
16
17
  begin
17
18
  find_field "LoginForm:ANSWER"
18
- return false
19
+ false
19
20
  rescue Capybara::ElementNotFound
20
21
  verify_login!
21
- return true
22
+ true
22
23
  end
23
24
  end
24
25
 
25
26
  def two_factor_response(answer)
26
- fill_in "LoginForm:ANSWER", :with => answer
27
+ fill_in "LoginForm:ANSWER", with: answer
27
28
  choose "LoginForm:DEVICE:0"
28
29
  click_button "LoginForm:ContinueInput"
29
30
  verify_login!
30
31
  end
31
32
 
32
33
  def download
33
- rows = find(".accountsList table").all("tr")
34
- rows.map do |row|
34
+ rows = find("[id='BalancesTabBoxId:balancesForm:balancesTable']").all("tr:not([tbodyid])")
35
+ rows.map { |row|
35
36
  cells = row.all("td")
36
37
  {
37
- :adapter => :vanguard,
38
- :user => @user,
39
- :id => cells.first.find("a")[:href].match(/.*#(.*)$/)[1],
40
- :name => cells.first.text,
41
- :amount => BigDecimal.new(cells.last.text.match(/\$(.*)$/)[1].gsub(/,/,''))
38
+ adapter: :vanguard,
39
+ user: @user,
40
+ id: cells.first.all("a").first[:href].match(/.*#(.*)$/)[1],
41
+ name: cells.first.text,
42
+ amount: BigDecimal(cells.last.text.match(/\$(.*)$/)[1].delete(","))
42
43
  }
43
- end.tap { click_link "Log off" }
44
+ }.tap { click_link "Log off" }
44
45
  end
45
46
 
46
- private
47
+ private
47
48
 
48
49
  def verify_login!
49
- find ".lastLogon"
50
+ find_link "Log off"
50
51
  rescue Capybara::ElementNotFound
51
52
  raise FineAnts::LoginFailedError.new
52
53
  end
53
54
  end
54
55
  end
55
56
  end
56
-
@@ -8,24 +8,33 @@ module FineAnts
8
8
  end
9
9
 
10
10
  def login
11
- return true # No login necessary
11
+ true # No login necessary
12
12
  end
13
13
 
14
14
  def download
15
15
  visit "https://www.zillow.com/homedetails/total_nonsense/#{@user}_zpid/?fullpage=true"
16
- zestimate = find('.estimates .home-summary-row:nth-child(2) span:nth-child(2)').text
16
+ zestimate = find_first(
17
+ ".estimates .home-summary-row:nth-child(2) span:nth-child(2)",
18
+ ".zestimate.primary-quote"
19
+ ).text.match(/(\$.*)/)[1]
20
+
17
21
  [{
18
- :adapter => :zillow,
19
- :user => @user,
20
- :id => @user,
21
- :name => find('.addr h1').text,
22
- :amount => BigDecimal.new(zestimate.gsub(/[\$,]/,''))
22
+ adapter: :zillow,
23
+ user: @user,
24
+ id: @user,
25
+ name: find_first(".addr h1", ".hdp-home-header-st-addr").text,
26
+ amount: BigDecimal(zestimate.gsub(/[\$,]/, ""))
23
27
  }]
24
28
  end
29
+
30
+ private
31
+
32
+ def find_first(*locators)
33
+ locators.each do |locator|
34
+ return find(locator)
35
+ rescue Capybara::ElementNotFound
36
+ end
37
+ end
25
38
  end
26
39
  end
27
40
  end
28
-
29
-
30
-
31
-
@@ -1,12 +1,12 @@
1
1
  require "capybara/dsl"
2
2
 
3
3
  # Autoload all predefined adapters
4
- Dir[File.join(File.dirname(__FILE__), 'adapters', '*.rb')].each { |f| require f }
4
+ Dir[File.join(File.dirname(__FILE__), "adapters", "*.rb")].sort.each { |f| require f }
5
5
 
6
6
  module FineAnts
7
7
  module Adapters
8
8
  def self.look_up(name)
9
- const_name = name.to_s.gsub(/_/, ' ').split(' ').map(&:capitalize).join
9
+ const_name = name.to_s.tr("_", " ").split(" ").map(&:capitalize).join
10
10
  const_get(const_name).tap do |adapter|
11
11
  adapter.class_eval do
12
12
  include Capybara::DSL
@@ -6,7 +6,7 @@
6
6
  require "capybara"
7
7
 
8
8
  Capybara.register_driver :selenium do |app|
9
- Capybara::Selenium::Driver.new(app, :browser => :chrome)
9
+ Capybara::Selenium::Driver.new(app, browser: :chrome)
10
10
  end
11
11
  Capybara.default_driver = :selenium
12
- Capybara.default_max_wait_time = 5
12
+ Capybara.default_max_wait_time = 10
@@ -2,4 +2,3 @@ module FineAnts
2
2
  class LoginFailedError < StandardError
3
3
  end
4
4
  end
5
-
@@ -10,13 +10,13 @@ module FineAnts
10
10
  @adapter.download
11
11
  end
12
12
 
13
- private
13
+ private
14
14
 
15
15
  def login!
16
16
  puts "Attempting to login to #{@adapter.class.name} as '#{@credentials[:user]}'"
17
17
  login_complete = @adapter.login
18
18
  if !login_complete && @adapter.respond_to?(:two_factor_response)
19
- puts <<-TEXT.gsub(/^\s+/,'')
19
+ puts <<-TEXT.gsub(/^\s+/, "")
20
20
  #{@adapter.class.name} is requiring two-factor auth.
21
21
  Check your SMS/Email/TOTP and type it here:
22
22
  TEXT
@@ -24,7 +24,5 @@ module FineAnts
24
24
  @adapter.two_factor_response(response)
25
25
  end
26
26
  end
27
-
28
27
  end
29
28
  end
30
-
@@ -1,3 +1,3 @@
1
1
  module FineAnts
2
- VERSION = "1.8.2"
2
+ VERSION = "1.9.0"
3
3
  end
data/lib/fine_ants.rb CHANGED
@@ -7,10 +7,10 @@ require "fine_ants/login_failed_error"
7
7
  module FineAnts
8
8
  def self.download(adapter_name, credentials)
9
9
  adapter = if adapter_name.instance_of?(Class)
10
- adapter_name
11
- else
12
- Adapters.look_up(adapter_name)
13
- end
10
+ adapter_name
11
+ else
12
+ Adapters.look_up(adapter_name)
13
+ end
14
14
  Runner.new(adapter.new(credentials), credentials).download
15
15
  end
16
16
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fine_ants
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.8.2
4
+ version: 1.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Justin Searls
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-10-01 00:00:00.000000000 Z
11
+ date: 2022-02-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: capybara
@@ -16,56 +16,56 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '2.7'
19
+ version: '3.26'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '2.7'
26
+ version: '3.26'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: selenium-webdriver
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '2.53'
33
+ version: 3.142.0
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '2.53'
40
+ version: 3.142.0
41
41
  - !ruby/object:Gem::Dependency
42
- name: bundler
42
+ name: bundle
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - "~>"
45
+ - - ">="
46
46
  - !ruby/object:Gem::Version
47
- version: '1.11'
47
+ version: '0'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - "~>"
52
+ - - ">="
53
53
  - !ruby/object:Gem::Version
54
- version: '1.11'
54
+ version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: rake
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '10.0'
61
+ version: '12.3'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '10.0'
68
+ version: '12.3'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: pry
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -80,7 +80,21 @@ dependencies:
80
80
  - - ">="
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
- description: Opens your browser and finds your bank account status.
83
+ - !ruby/object:Gem::Dependency
84
+ name: standard
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description:
84
98
  email:
85
99
  - searls@gmail.com
86
100
  executables: []
@@ -103,10 +117,12 @@ files:
103
117
  - lib/fine_ants/adapters/etrade.rb
104
118
  - lib/fine_ants/adapters/pnc.rb
105
119
  - lib/fine_ants/adapters/purduefed.rb
120
+ - lib/fine_ants/adapters/schwab.rb
106
121
  - lib/fine_ants/adapters/simple.rb
107
122
  - lib/fine_ants/adapters/simple_bancorp.rb
108
123
  - lib/fine_ants/adapters/strs.rb
109
124
  - lib/fine_ants/adapters/target.rb
125
+ - lib/fine_ants/adapters/ufb_direct.rb
110
126
  - lib/fine_ants/adapters/vanguard.rb
111
127
  - lib/fine_ants/adapters/zillow.rb
112
128
  - lib/fine_ants/capybara_setup.rb
@@ -116,7 +132,7 @@ files:
116
132
  homepage: https://github.com/searls/fine_ants
117
133
  licenses: []
118
134
  metadata: {}
119
- post_install_message:
135
+ post_install_message:
120
136
  rdoc_options: []
121
137
  require_paths:
122
138
  - lib
@@ -131,9 +147,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
131
147
  - !ruby/object:Gem::Version
132
148
  version: '0'
133
149
  requirements: []
134
- rubyforge_project:
135
- rubygems_version: 2.6.11
136
- signing_key:
150
+ rubygems_version: 3.3.6
151
+ signing_key:
137
152
  specification_version: 4
138
153
  summary: Opens your browser and finds your bank account status.
139
154
  test_files: []