fine_ants 1.8.5 → 1.9.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: e1172b4e3fe328425ef87e41caec3a2c211c19eb
4
- data.tar.gz: fe2e980bee738c6800b35ee3a733253711e3fdb7
2
+ SHA256:
3
+ metadata.gz: '09710593f2fe74e7d9eceabd20925d0cf6adac747eb079d66e6ee73fc8b55843'
4
+ data.tar.gz: aab4bfde4086f433632744ad46820f776f99e59778ed5784ac7217a994b7f2b3
5
5
  SHA512:
6
- metadata.gz: f0af24a4bbdd5e9927bb0f7b61d61990887ba58776ec62659126400747ec8e2b4720bc403cb2c49e42d4264c82d7f8c5e822a8df2739b4c38cf405be4cd8e529
7
- data.tar.gz: 1b11dfdc0670d498d95ea2b52fe26c7c270ee866e6863a613ac9a92647f5fe800ece4276fbeeb0b5fe23235cbf0b332ba58f580934b512ce81d9b22fbde88d6c
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", "~> 3.6"
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
@@ -12,8 +12,8 @@ module FineAnts
12
12
  visit "https://www.americanexpress.com"
13
13
  form_css = find("form")[:id] == "ssoform" ? "#ssoform" : ".eliloMain"
14
14
  within form_css do
15
- fill_in "User ID", :with => @user
16
- fill_in "Password", :with => @password
15
+ fill_in "User ID", with: @user
16
+ fill_in "Password", with: @password
17
17
  click_thing "Log In"
18
18
  end
19
19
  verify_login!
@@ -23,29 +23,26 @@ module FineAnts
23
23
  visit "https://global.americanexpress.com/accounts"
24
24
  find(".card-block")
25
25
  all(".card-block > div").map do |account|
26
- name = account.find('.heading-3').text
26
+ name = account.find(".card > .pad > .heading-3").text
27
27
  owed = account.text.include?("Total Balance")
28
28
  {
29
- :adapter => :amex,
30
- :user => @user,
31
- :id => name,
32
- :name => name,
33
- :amount => -1 * BigDecimal.new(if owed
34
- account.all("table td:nth-child(2) span").first.text.gsub(/[\$,]/,'')
35
- else
36
- "0"
37
- 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)
38
38
  }
39
- end.tap do |accounts|
40
- page.driver.go_back
41
- click_button "Log Out"
42
39
  end
43
40
  end
44
41
 
45
- private
42
+ private
46
43
 
47
44
  def verify_login!
48
- find_button "Log Out"
45
+ page.has_text? "Log Out"
49
46
  rescue
50
47
  raise FineAnts::LoginFailedError.new
51
48
  end
@@ -58,8 +55,3 @@ module FineAnts
58
55
  end
59
56
  end
60
57
  end
61
-
62
-
63
-
64
-
65
-
@@ -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,40 +10,41 @@ 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
50
  find_link "Log off"
@@ -53,4 +54,3 @@ module FineAnts
53
54
  end
54
55
  end
55
56
  end
56
-
@@ -8,22 +8,22 @@ 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
16
  zestimate = find_first(
17
- '.estimates .home-summary-row:nth-child(2) span:nth-child(2)',
18
- '.zestimate.primary-quote'
17
+ ".estimates .home-summary-row:nth-child(2) span:nth-child(2)",
18
+ ".zestimate.primary-quote"
19
19
  ).text.match(/(\$.*)/)[1]
20
20
 
21
21
  [{
22
- :adapter => :zillow,
23
- :user => @user,
24
- :id => @user,
25
- :name => find_first('.addr h1', '.hdp-home-header-st-addr').text,
26
- :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(/[\$,]/, ""))
27
27
  }]
28
28
  end
29
29
 
@@ -31,16 +31,10 @@ module FineAnts
31
31
 
32
32
  def find_first(*locators)
33
33
  locators.each do |locator|
34
- begin
35
- return find(locator)
36
- rescue Capybara::ElementNotFound
37
- end
34
+ return find(locator)
35
+ rescue Capybara::ElementNotFound
38
36
  end
39
37
  end
40
38
  end
41
39
  end
42
40
  end
43
-
44
-
45
-
46
-
@@ -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.5"
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.5
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-12-09 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: '3.6'
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: '3.6'
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: []