bankscrap 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 7567c87bd47ae9505b4abaa485cae8442dd41b40
4
+ data.tar.gz: 9e5a1bdc25a930b5e8340bd65be5f1302d0a7da0
5
+ SHA512:
6
+ metadata.gz: 63da5ae6218c554d0ce6d745cdf252f18ecd2bfa137bd8a06d24018d108802ba02907d97ae9e986ea89de9274e683a5f15b54fdf9696e4258307746db87edfab
7
+ data.tar.gz: 542a627dfcc461df1ad682ffed3af835345e8b85b8c521dede4ce03690fba24f204c97f671057f023b1897a2ad5a0b5b1004c828c07853c5c377b1dfe48ad91a
data/README.md ADDED
@@ -0,0 +1,147 @@
1
+ # Bankscrap
2
+
3
+ [![](http://188.166.39.57:3000/badge.svg)](http://188.166.39.57:3000)
4
+
5
+ Ruby gem to extract account balance and transactions from banks. You can use it either as command line tool or as a library.
6
+
7
+ Feel free to contribute and add your bank if it isn't supported.
8
+
9
+ ## Supported banks
10
+
11
+ * **BBVA**: [bankscrap-bbva](https://github.com/bankscrap/bankscrap-bbva)
12
+ * **ING Direct**: [bankscrap-ing](https://github.com/bankscrap/bankscrap-ing)
13
+ * **Banc Sabadell** (WIP): [bankscrap-banc-sabadell](https://github.com/bankscrap/bankscrap-banc-sabadell)
14
+
15
+ Interested in any other bank? Open a new Issue and we'll try to help.
16
+
17
+ ## Background and motivation
18
+
19
+ Most banks don't offer public APIs and the only way to access your data (balance and transactions) is through their websites... and most bank websites are a f*cking nightmare.
20
+
21
+ We are developers and we don't want to waste time doing things we are able to automate. Having to perform 20 clicks in an awful website just to check how much money we have is not something we like.
22
+
23
+ There are two approaches to solve this problem:
24
+ - Web scraping on the bank's site.
25
+ - Reverse engineering the bank's mobile app or the bank's single page web app (if they have one) to use the same API.
26
+
27
+ BankScrap uses both methods depending on the bank.
28
+
29
+ ## Requirements
30
+
31
+ Some banks needs a JavaScript runtime in order to work. So if you find an error like "Could not find JavaScript runtime" try to install one. It has been tested with nodejs.
32
+
33
+ ## Installation
34
+
35
+ Installation from RubyGems:
36
+
37
+ # BBVA
38
+ gem install bankscrap-bbva
39
+
40
+ # ING
41
+ gem install bankscrap-ing
42
+
43
+ Or, if you're using Bundler, just add the following to your Gemfile:
44
+
45
+ # BBVA
46
+ gem 'bankscrap-bbva'
47
+
48
+ # ING
49
+ gem 'bankscrap-ing'
50
+
51
+ Note that you only need to install the gem for your selected bank – the main gem (`bankscrap`) will be installed as a dependency.
52
+
53
+ ## Usage
54
+
55
+ ### From terminal
56
+ #### Bank account balance
57
+
58
+ ###### BBVA
59
+
60
+ $ bankscrap balance YourBank --user YOUR_BANK_USER --password YOUR_BANK_PASSWORD
61
+
62
+ ###### ING Direct
63
+ ING needs one more argument: your birthday.
64
+
65
+ $ bankscrap balance ING --user YOUR_DNI --password YOUR_PASSWORD --extra=birthday:01/01/1980
66
+
67
+ Replace 01/01/1980 with your actual birthday.
68
+
69
+ #### Transactions for last 30 days
70
+ ###### BBVA
71
+
72
+ $ bankscrap transactions BBVA --user YOUR_BBVA_USER --password YOUR_BBVA_PASSWORD
73
+
74
+ ###### ING Direct
75
+
76
+ $ bankscrap transactions ING --user YOUR_DNI --password YOUR_PASSWORD --extra=birthday:01/01/1980
77
+
78
+ #### Transactions with date range
79
+
80
+ $ bankscrap transactions YourBank --user YOUR_BANK_USER --password YOUR_BANK_PASSWORD --extra=from:01-01-2015 to:01-02-2015
81
+
82
+ ---
83
+
84
+ By default it will use your first bank account, if you want to fetch transactions for a different account you can use this syntax:
85
+
86
+ $ bankscrap transactions YourBank your_iban --user YOUR_DNI --password YOUR_PASSWORD
87
+
88
+ If you don't want to pass your user and password everytime you can define them in your .bash_profile by adding:
89
+
90
+ export BANK_SCRAP_USER=YOUR_BANK_USER
91
+ export BANK_SCRAP_PASSWORD=YOUR_BANK_PASSWORD
92
+
93
+ ### From Ruby code
94
+
95
+ You can also use this gem from your own app as library. To do so first you must initialize a BankScrap::Bank object
96
+
97
+
98
+ ```ruby
99
+ require 'bankscrap-bbva'
100
+ # BBVA
101
+ bbva = Bankscrap::BBVA::Bank.new(YOUR_BBVA_USER, YOUR_BBVA_PASSWORD)
102
+ # ING
103
+ ing = Bankscrap::ING::Bank.new(YOUR_DNI, YOUR_ING_PASSWORD, extra_args: {"birthday" => "dd/mm/yyyy"})
104
+ ```
105
+
106
+
107
+ The initialize method will automatically login and fetch your bank accounts
108
+
109
+ You can now explore your bank accounts accounts:
110
+
111
+ ```ruby
112
+ bank.accounts
113
+ ```
114
+
115
+ And get its balance:
116
+ ```ruby
117
+ bank.accounts.first.balance
118
+ ```
119
+
120
+ Get last month transactions for a particular account:
121
+
122
+ ```ruby
123
+ account = bank.accounts.first
124
+ account.transactions
125
+ ```
126
+
127
+ Get transactions for last year (from now):
128
+
129
+ ```ruby
130
+ account = bank.accounts.first
131
+ account.transactions = account.fetch_transactions(start_date: Date.today - 1.year, end_date: Date.today)
132
+ account.transactions
133
+ ```
134
+
135
+
136
+
137
+ ## Contributing
138
+
139
+ 1. Fork it ( https://github.com/bank-scrap/bank_scrap/fork )
140
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
141
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
142
+ 4. Push to the branch (`git push origin my-new-feature`)
143
+ 5. Create a new Pull Request
144
+
145
+ ## Thanks
146
+
147
+ Thanks to Javier Cuevas (@javiercr) for his [BBVA](https://github.com/javiercr/bbva) gem.
data/bin/bankscrap ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+ $LOAD_PATH.unshift(File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib')))
3
+
4
+ require 'bankscrap'
5
+ Bankscrap::CLI.start(ARGV)
data/lib/.DS_Store ADDED
Binary file
data/lib/bankscrap.rb ADDED
@@ -0,0 +1,11 @@
1
+ require 'active_support/all'
2
+ require 'money'
3
+ require 'rubygems'
4
+ require_relative 'bankscrap/utils/inspectable'
5
+ require_relative 'bankscrap/version'
6
+ require_relative 'bankscrap/config'
7
+ require_relative 'bankscrap/cli'
8
+ require_relative 'bankscrap/bank'
9
+ require_relative 'bankscrap/account'
10
+ require_relative 'bankscrap/investment'
11
+ require_relative 'bankscrap/transaction'
@@ -0,0 +1,25 @@
1
+ module Bankscrap
2
+ class Account
3
+ include Utils::Inspectable
4
+
5
+ attr_accessor :bank, :id, :name, :balance, :currency, :available_balance, :description, :transactions, :iban, :bic
6
+
7
+ def initialize(params = {})
8
+ params.each { |key, value| send "#{key}=", value }
9
+ end
10
+
11
+ def transactions
12
+ @transactions ||= bank.fetch_transactions_for(self)
13
+ end
14
+
15
+ def fetch_transactions(start_date: Date.today - 2.years, end_date: Date.today)
16
+ bank.fetch_transactions_for(self, start_date: start_date, end_date: end_date)
17
+ end
18
+
19
+ private
20
+
21
+ def inspect_attributes
22
+ %i(id name balance currency available_balance description iban bic)
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,82 @@
1
+ require 'mechanize'
2
+ require 'logger'
3
+
4
+ module Bankscrap
5
+ class Bank
6
+ WEB_USER_AGENT = 'Mozilla/5.0 (Linux; Android 4.2.1; en-us; Nexus 4 Build/JOP40D) AppleWebKit/535.19 (KHTML, ' \
7
+ 'like Gecko) Chrome/18.0.1025.166 Mobile Safari/535.19'.freeze
8
+ attr_accessor :headers, :accounts, :investments
9
+
10
+ def initialize(_user, _password, log: false, debug: false, extra_args: nil)
11
+ @accounts = fetch_accounts
12
+ end
13
+
14
+ # Interface method placeholders
15
+
16
+ def fetch_accounts
17
+ raise "#{self.class} should implement a fetch_account method"
18
+ end
19
+
20
+ def fetch_investments
21
+ raise "#{self.class} should implement a fetch_investment method"
22
+ end
23
+
24
+ def fetch_transactions_for(*)
25
+ raise "#{self.class} should implement a fetch_transactions method"
26
+ end
27
+
28
+ def account_with_iban(iban)
29
+ accounts.find do |account|
30
+ account.iban.delete(' ') == iban.delete(' ')
31
+ end
32
+ end
33
+
34
+ private
35
+
36
+ def get(url, params: [], referer: nil)
37
+ @http.get(url, params, referer, @headers).body
38
+ end
39
+
40
+ def post(url, fields: {})
41
+ @http.post(url, fields, @headers).body
42
+ end
43
+
44
+ def put(url, fields: {})
45
+ @http.put(url, fields, @headers).body
46
+ end
47
+
48
+ # Sets temporary HTTP headers, execute a code block
49
+ # and resets the headers
50
+ def with_headers(tmp_headers)
51
+ current_headers = @headers
52
+ add_headers(tmp_headers)
53
+ yield
54
+ ensure
55
+ add_headers(current_headers)
56
+ end
57
+
58
+ def add_headers(headers)
59
+ @headers.merge! headers
60
+ @http.request_headers = @headers
61
+ end
62
+
63
+ def initialize_cookie(url)
64
+ @http.get(url).body
65
+ end
66
+
67
+ def initialize_connection
68
+ @http = Mechanize.new do |mechanize|
69
+ mechanize.user_agent = WEB_USER_AGENT
70
+ mechanize.agent.http.verify_mode = OpenSSL::SSL::VERIFY_NONE
71
+ mechanize.log = Logger.new(STDOUT) if @debug
72
+ # mechanize.set_proxy 'localhost', 8888
73
+ end
74
+
75
+ @headers = {}
76
+ end
77
+
78
+ def log(msg)
79
+ puts msg if @log
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,88 @@
1
+ require 'thor'
2
+ require 'active_support/core_ext/string'
3
+
4
+ module Bankscrap
5
+ class CLI < Thor
6
+ def self.shared_options
7
+ option :user, default: ENV['BANKSCRAP_USER']
8
+ option :password, default: ENV['BANKSCRAP_PASSWORD']
9
+ option :log, default: false
10
+ option :debug, default: false
11
+
12
+ # Some bank needs more input, like birthday, this would go here
13
+ # Usage:
14
+ # bankscrap balance BANK_NAME --extra=birthday:01/12/1980
15
+ option :extra, type: :hash, default: {}
16
+ end
17
+
18
+ desc 'balance BANK', "get accounts' balance"
19
+ shared_options
20
+ def balance(bank)
21
+ assign_shared_options
22
+ initialize_client_for(bank)
23
+
24
+ @client.accounts.each do |account|
25
+ say "Account: #{account.description} (#{account.iban})", :cyan
26
+ say "Balance: #{account.balance}", :green
27
+ end
28
+ end
29
+
30
+ desc 'transactions BANK', "get account's transactions"
31
+ shared_options
32
+ def transactions(bank, iban = nil)
33
+ assign_shared_options
34
+
35
+ begin
36
+ start_date = @extra_args.key?('from') ? Date.strptime(@extra_args['from'], '%d-%m-%Y') : nil
37
+ end_date = @extra_args.key?('to') ? Date.strptime(@extra_args['to'], '%d-%m-%Y') : nil
38
+ rescue ArgumentError
39
+ say 'Invalid date format. Correct format d-m-Y', :red
40
+ end
41
+
42
+ initialize_client_for(bank)
43
+
44
+ account = iban ? @client.account_with_iban(iban) : @client.accounts.first
45
+
46
+ if !start_date.nil? && !end_date.nil?
47
+ if start_date > end_date
48
+ say 'From date must be lower than to date', :red
49
+ exit
50
+ end
51
+
52
+ transactions = account.fetch_transactions(start_date: start_date, end_date: end_date)
53
+ else
54
+ transactions = account.transactions
55
+ end
56
+
57
+ say "Transactions for: #{account.description} (#{account.iban})", :cyan
58
+
59
+ transactions.each do |transaction|
60
+ say transaction.to_s, (transaction.amount > Money.new(0) ? :green : :red)
61
+ end
62
+ end
63
+
64
+ private
65
+
66
+ def assign_shared_options
67
+ @user = options[:user]
68
+ @password = options[:password]
69
+ @log = options[:log]
70
+ @debug = options[:debug]
71
+ @extra_args = options[:extra]
72
+ end
73
+
74
+ def initialize_client_for(bank_name)
75
+ bank_class = find_bank_class_for(bank_name)
76
+ @client = bank_class.new(@user, @password, log: @log, debug: @debug, extra_args: @extra_args)
77
+ end
78
+
79
+ def find_bank_class_for(bank_name)
80
+ require "bankscrap-#{bank_name.downcase}"
81
+ Object.const_get("Bankscrap::#{bank_name}::Bank")
82
+ rescue LoadError
83
+ raise ArgumentError.new('Invalid bank name.')
84
+ rescue NameError
85
+ raise ArgumentError.new("Invalid bank name. Did you mean \"#{bank_name.upcase}\"?")
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,5 @@
1
+ module Bankscrap
2
+ # Default format for money: 1.000,00 €
3
+ Money.default_formatting_rules = { symbol_position: :after }
4
+ I18n.load_path += Dir.glob(File.expand_path('../locale/*.yml', __FILE__))
5
+ end
@@ -0,0 +1,19 @@
1
+ module Bankscrap
2
+ class Investment
3
+ include Utils::Inspectable
4
+
5
+ attr_accessor :bank, :id, :name, :balance, :currency, :investment
6
+
7
+ def initialize(params = {})
8
+ params.each { |key, value| send "#{key}=", value }
9
+ end
10
+
11
+ private
12
+
13
+ def inspect_attributes
14
+ [
15
+ :id, :name, :balance, :currency, :investment
16
+ ]
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,12 @@
1
+ en:
2
+ number:
3
+ currency:
4
+ format:
5
+ delimiter: '.'
6
+ format: '%n %u'
7
+ negative_format: '-%n %u'
8
+ precision: 2
9
+ separator: ','
10
+ significant: false
11
+ strip_insignificant_zeros: false
12
+ unit: '€'
@@ -0,0 +1,27 @@
1
+ module Bankscrap
2
+ class Transaction
3
+ include Utils::Inspectable
4
+
5
+ attr_accessor :id, :amount, :currency,
6
+ :effective_date, :description,
7
+ :balance, :account
8
+
9
+ def initialize(params = {})
10
+ params.each { |key, value| send "#{key}=", value }
11
+ end
12
+
13
+ def to_s
14
+ "#{effective_date.strftime('%d/%m/%Y')} #{description.ljust(45)} #{amount.format.rjust(20)}"
15
+ end
16
+
17
+ private
18
+
19
+ def inspect_attributes
20
+ [
21
+ :id, :amount, :currency,
22
+ :effective_date, :description,
23
+ :balance
24
+ ]
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,19 @@
1
+ module Bankscrap
2
+ module Utils
3
+ module Inspectable
4
+ def inspect
5
+ attributes = inspect_attributes.reject do |x|
6
+ begin
7
+ attribute = send x
8
+ !attribute || (attribute.respond_to?(:empty?) && attribute.empty?)
9
+ rescue NoMethodError
10
+ true
11
+ end
12
+ end.map do |attribute|
13
+ "#{attribute}: #{send(attribute).inspect}"
14
+ end.join ' '
15
+ "#<#{self.class.name}:#{sprintf('0x%x', object_id)} #{attributes}>"
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,3 @@
1
+ module Bankscrap
2
+ VERSION = '1.0.0'.freeze
3
+ end
metadata ADDED
@@ -0,0 +1,178 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bankscrap
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Javier Cuevas
8
+ - Raúl Marcos
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2016-04-30 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '1.7'
21
+ type: :development
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: '1.7'
28
+ - !ruby/object:Gem::Dependency
29
+ name: rake
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: '10.0'
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: '10.0'
42
+ - !ruby/object:Gem::Dependency
43
+ name: byebug
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: '3.5'
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: 3.5.1
52
+ type: :development
53
+ prerelease: false
54
+ version_requirements: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - "~>"
57
+ - !ruby/object:Gem::Version
58
+ version: '3.5'
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: 3.5.1
62
+ - !ruby/object:Gem::Dependency
63
+ name: rubocop
64
+ requirement: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 0.39.0
69
+ type: :development
70
+ prerelease: false
71
+ version_requirements: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 0.39.0
76
+ - !ruby/object:Gem::Dependency
77
+ name: thor
78
+ requirement: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '0.19'
83
+ type: :runtime
84
+ prerelease: false
85
+ version_requirements: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '0.19'
90
+ - !ruby/object:Gem::Dependency
91
+ name: mechanize
92
+ requirement: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 2.7.4
97
+ type: :runtime
98
+ prerelease: false
99
+ version_requirements: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: 2.7.4
104
+ - !ruby/object:Gem::Dependency
105
+ name: activesupport
106
+ requirement: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '4.1'
111
+ type: :runtime
112
+ prerelease: false
113
+ version_requirements: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '4.1'
118
+ - !ruby/object:Gem::Dependency
119
+ name: money
120
+ requirement: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: 6.5.0
125
+ type: :runtime
126
+ prerelease: false
127
+ version_requirements: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: 6.5.0
132
+ description: Command line tools to get bank account details from some banks.
133
+ email:
134
+ - javi@diacode.com
135
+ - raulmarcosl@gmail.com
136
+ executables:
137
+ - bankscrap
138
+ extensions: []
139
+ extra_rdoc_files: []
140
+ files:
141
+ - README.md
142
+ - bin/bankscrap
143
+ - lib/.DS_Store
144
+ - lib/bankscrap.rb
145
+ - lib/bankscrap/account.rb
146
+ - lib/bankscrap/bank.rb
147
+ - lib/bankscrap/cli.rb
148
+ - lib/bankscrap/config.rb
149
+ - lib/bankscrap/investment.rb
150
+ - lib/bankscrap/locale/en.yml
151
+ - lib/bankscrap/transaction.rb
152
+ - lib/bankscrap/utils/inspectable.rb
153
+ - lib/bankscrap/version.rb
154
+ homepage: https://github.com/bank-scrap/bank_scrap
155
+ licenses:
156
+ - MIT
157
+ metadata: {}
158
+ post_install_message:
159
+ rdoc_options: []
160
+ require_paths:
161
+ - lib
162
+ required_ruby_version: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - "~>"
165
+ - !ruby/object:Gem::Version
166
+ version: '2.1'
167
+ required_rubygems_version: !ruby/object:Gem::Requirement
168
+ requirements:
169
+ - - ">="
170
+ - !ruby/object:Gem::Version
171
+ version: '0'
172
+ requirements: []
173
+ rubyforge_project:
174
+ rubygems_version: 2.6.4
175
+ signing_key:
176
+ specification_version: 4
177
+ summary: Get your bank account details.
178
+ test_files: []