banker 0.0.3

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.
@@ -0,0 +1,112 @@
1
+ module Banker
2
+ # This class allows the retrieval of credit score data
3
+ # for Credit Expert UK
4
+ #
5
+ # == Examples
6
+ #
7
+ # Get Score from Credit Expert UK
8
+ #
9
+ # site = Banker::CreditExpertUK.new(:username => "joe",
10
+ # :password => '123456',
11
+ # :memorable_word => 'superduper')
12
+ #
13
+ # site.score #=> 800
14
+ #
15
+ class CreditExpertUK
16
+ attr_accessor :username, :password, :memorable_word, :agent, :score
17
+
18
+ LOGIN_ENDPOINT = "https://www.creditexpert.co.uk/MCCLogin.aspx"
19
+
20
+ FIELDS = {
21
+ username: 'loginUser:txtUsername:ECDTextBox',
22
+ password: 'loginUser:txtPassword:ECDTextBox',
23
+ questions: [
24
+ 'label span#loginUserMemorableWord_SecurityQuestionLetter1',
25
+ 'label span#loginUserMemorableWord_SecurityQuestionLetter2'
26
+ ],
27
+ answers: [
28
+ 'loginUserMemorableWord:SecurityQuestionUK1_SecurityAnswer1_ECDTextBox',
29
+ 'loginUserMemorableWord:SecurityQuestionUK1_SecurityAnswer2_ECDTextBox'
30
+ ],
31
+ score: 'span#MCC_ScoreIntelligence_ScoreIntelligence_Dial1_MyScoreV31_pnlMyScore1_lblMyScore'
32
+ }
33
+
34
+ def initialize(args)
35
+ @username = args[:username]
36
+ @password = args[:password]
37
+ @memorable_word = args[:memorable_word]
38
+
39
+ @agent = Mechanize.new
40
+ @agent.log = Logger.new 'mech.log'
41
+ @agent.user_agent = 'Mozilla/5.0 (Banker)'
42
+
43
+ authenticate
44
+ end
45
+
46
+ private
47
+
48
+ def authenticate
49
+ @score = data
50
+ =begin
51
+ page = @agent.get(LOGIN_ENDPOINT)
52
+
53
+ form = page.form_with(:action => 'MCCLogin.aspx')
54
+
55
+ form[FIELDS[:username]] = @username
56
+ form[FIELDS[:password]] = @password
57
+
58
+
59
+ page = @agent.submit(form, form.buttons.first)
60
+
61
+ form = page.form_with(:name => 'MasterPage')
62
+
63
+ first_letter = page.at(FIELDS[:questions][0]).content.scan(/\d+/).first.to_i
64
+ second_letter = page.at(FIELDS[:questions][1]).content.scan(/\d+/).first.to_i
65
+
66
+ form[FIELDS[:answers][0]] = get_letter(first_letter)
67
+
68
+ form[FIELDS[:answers][1]] = get_letter(second_letter)
69
+
70
+ page = @agent.submit(form, form.buttons.first)
71
+
72
+ @score = page.at(FIELDS[:score]).content.to_i
73
+ =end
74
+ end
75
+
76
+ def start
77
+ page = @agent.get(LOGIN_ENDPOINT)
78
+
79
+ form = page.form_with(:action => 'MCCLogin.aspx')
80
+
81
+ form[FIELDS[:username]] = @username
82
+ form[FIELDS[:password]] = @password
83
+
84
+ @agent.submit(form, form.buttons.first)
85
+ end
86
+
87
+ def step1
88
+ page = start
89
+ form = page.form_with(name: 'MasterPage')
90
+
91
+ first_letter = extract_digit(page.at(FIELDS[:questions][0]).content)
92
+ second_letter = extract_digit(page.at(FIELDS[:questions][1]).content)
93
+
94
+ form[FIELDS[:answers][0]] = get_letter(first_letter)
95
+ form[FIELDS[:answers][1]] = get_letter(second_letter)
96
+
97
+ @agent.submit(form, form.buttons.first)
98
+ end
99
+
100
+ def data
101
+ step1.at(FIELDS[:score]).content.to_i
102
+ end
103
+
104
+ def extract_digit(content)
105
+ content.scan(/\d+/).first.to_i
106
+ end
107
+
108
+ def get_letter(index)
109
+ @memorable_word[index-1]
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,5 @@
1
+ module Banker
2
+ module Error
3
+ class InvalidParams < ArgumentError;end
4
+ end
5
+ end
@@ -0,0 +1,130 @@
1
+ module Banker
2
+ # This class allows the data retrieval of accounts for Lloyds TSB UK
3
+ #
4
+ # == Examples
5
+ #
6
+ # Retrieve Account Balance
7
+ #
8
+ # user_params = { username: 'Joe', password: 'password', memorable_word: 'superduper' }
9
+ # lloyds = Banker::LloydsTSBUK.new(user_params)
10
+ # lloyds.balance
11
+ # # => [ {:current_account => { :balance => 160940,
12
+ # :details => { :sort_code => "928277",
13
+ # :account_number => "92837592" }}},
14
+ #
15
+ # {:savings_account => { :balance => 0.0,
16
+ # :details => { :sort_code => "918260",
17
+ # :account_number=>"91850261" }}},
18
+ #
19
+ # {:lloyds_tsb_platinum_mastercard => { :balance => 0.0,
20
+ # :details => { :card_number => "9284710274618391" }}}
21
+ # ]
22
+ #
23
+ class LloydsTSBUK
24
+ attr_accessor :username, :password, :memorable_word, :balance, :agent
25
+
26
+ LOGIN_ENDPOINT = "https://online.lloydstsb.co.uk/personal/logon/login.jsp"
27
+
28
+ def initialize(args)
29
+ @username = args[:username]
30
+ @password = args[:password]
31
+ @memorable_word = args[:memorable_word]
32
+
33
+ @agent = Mechanize.new
34
+ @agent.log = Logger.new 'mech.log'
35
+ @agent.user_agent = 'Mozilla/5.0 (Banker)'
36
+ @agent.force_default_encoding = 'utf8'
37
+
38
+ @balance = authenticate
39
+ end
40
+
41
+ def get_memorable_word_letter(letter)
42
+ @memorable_word.to_s[letter.to_i - 1]
43
+ end
44
+
45
+ def cleaner(str)
46
+ str.gsub(/[^\d+]/, '')
47
+ end
48
+
49
+ private
50
+
51
+ def authenticate
52
+ page = @agent.get(LOGIN_ENDPOINT)
53
+
54
+ # Login Page
55
+ page = page.form('frmLogin') do |f|
56
+ f.fields[0].value = @username
57
+ f.fields[1].value = @password
58
+ end.click_button
59
+
60
+ # Memorable Word
61
+ form = page.form('frmentermemorableinformation1')
62
+ memorable_set(page, form)
63
+ page = @agent.submit(form, form.buttons.first)
64
+
65
+ # Accounts Page
66
+ return account_return(page)
67
+ end
68
+
69
+ def memorable_letters(page)
70
+ {
71
+ first: memorable_required(page)[0],
72
+ second: memorable_required(page)[1],
73
+ third: memorable_required(page)[2]
74
+ }
75
+ end
76
+
77
+ def memorable_set(page, form)
78
+ letters = memorable_letters(page)
79
+
80
+ form.fields[2].value = '&nbsp;' + get_memorable_word_letter(letters.fetch(:first))
81
+ form.fields[3].value = '&nbsp;' + get_memorable_word_letter(letters.fetch(:second))
82
+ form.fields[4].value = '&nbsp;' + get_memorable_word_letter(letters.fetch(:third))
83
+ end
84
+
85
+ def memorable_required(page)
86
+ page.labels.collect { |char| cleaner(char.to_s) }
87
+ end
88
+
89
+ def account_name(page)
90
+ page.search('div.accountDetails h2').collect {|a| a.content.downcase.gsub(/\s/, '_').to_sym }
91
+ end
92
+
93
+ def account_detail(page)
94
+ page.search('div.accountDetails p.numbers').collect {|n| n.content }
95
+ end
96
+
97
+ def account_balance(page)
98
+ page.search('div.balanceActionsWrapper p.balance').collect {|b| b.content }
99
+ end
100
+
101
+ def formatted_detail(page)
102
+ resp = account_detail(page).map { |detail| detail.split(',').map { |d| cleaner(d) } }
103
+ resp.map! do |r|
104
+ if r.length == 2
105
+ { sort_code: r[0], account_number: r[1] }
106
+ elsif r.length == 1
107
+ { card_number: r[0] }
108
+ else
109
+ STDERR.puts "[Error] It seems we got account details that we did not expect! - #{r.inspect}"
110
+ end
111
+ end
112
+ end
113
+
114
+ def formatted_balance(page)
115
+ account_balance(page).map do |b|
116
+ resp = cleaner(b)
117
+ resp.empty? ? 0.00 : resp.to_i
118
+ end
119
+ end
120
+
121
+ def account_return(page)
122
+ resp = []
123
+ account_name(page).zip(formatted_balance(page), formatted_detail(page)).each_with_index do |acc, index|
124
+ resp << { acc[0] => { balance: acc[1], details: acc[2] } }
125
+ end
126
+ resp
127
+ end
128
+
129
+ end
130
+ end
@@ -0,0 +1,3 @@
1
+ module Banker
2
+ VERSION = "0.0.3"
3
+ end
@@ -0,0 +1,32 @@
1
+ require 'spec_helper'
2
+
3
+ describe Banker::Account do
4
+ let(:attr){{
5
+ name:'',
6
+ uid:'',
7
+ amount:'',
8
+ limit:''
9
+ }}
10
+ subject {Banker::Account}
11
+
12
+ it {subject.new(attr).should respond_to(:name)}
13
+ it {subject.new(attr).should respond_to(:uid)}
14
+ it {subject.new(attr).should respond_to(:amount)}
15
+ it {subject.new(attr).should respond_to(:limit)}
16
+
17
+ it "validates presence of name" do
18
+ expect{
19
+ subject.new
20
+ }.to raise_error(ArgumentError, "missing attribute `name`")
21
+ end
22
+ it "validates presence of uid" do
23
+ expect{
24
+ subject.new(name:'')
25
+ }.to raise_error(ArgumentError, "missing attribute `uid`")
26
+ end
27
+ it "validates presence of amount" do
28
+ expect{
29
+ subject.new(name:'',uid:'')
30
+ }.to raise_error(ArgumentError, "missing attribute `amount`")
31
+ end
32
+ end
@@ -0,0 +1,167 @@
1
+ require 'spec_helper'
2
+
3
+ describe Banker::BarclaycardUK do
4
+ let(:support_files) {File.expand_path('../../support/barclaycard_uk/',__FILE__)}
5
+
6
+ LOGIN_ENDPOINT = 'https://bcol.barclaycard.co.uk/ecom/as2/initialLogon.do'
7
+ EXPORT_ENDPOINT = 'https://bcol.barclaycard.co.uk/ecom/as2/export.do?doAction=processRecentExportTransaction&type=OFX_2_0_2&statementDate=&sortBy=transactionDate&sortType=Dsc'
8
+
9
+ let(:mechanize) {mock('mechanize').as_null_object}
10
+ let(:node) {mock('node').as_null_object}
11
+ let(:form) {mock('form').as_null_object}
12
+ let(:button) {mock('button').as_null_object}
13
+ let(:ofx) {
14
+ f = File.open(File.expand_path('data.ofx',support_files), 'r:iso-8859-1')
15
+ f.read
16
+ }
17
+
18
+ subject { Banker::BarclaycardUK }
19
+
20
+ before do
21
+ subject.any_instance.stub(:params)
22
+ subject.any_instance.stub(:authenticate!)
23
+ subject.any_instance.stub(:get_data)
24
+ end
25
+
26
+ it {subject.new.should respond_to(:accounts)}
27
+
28
+ context "Parameters" do
29
+ before do
30
+ subject.any_instance.unstub(:params)
31
+ end
32
+
33
+ it "raises InvalidParams when username is missing" do
34
+ expect{
35
+ subject.new
36
+ }.to raise_error(Banker::Error::InvalidParams,
37
+ "missing parameters `username` `password` `memorable_word` ")
38
+ end
39
+
40
+ it "raises InvalidParams when password is missing" do
41
+ expect{
42
+ subject.new(username: "joe")
43
+ }.to raise_error(Banker::Error::InvalidParams,
44
+ "missing parameters `password` `memorable_word` ")
45
+ end
46
+
47
+ it "raises InvalidParams when memorable_word is missing" do
48
+ expect{
49
+ subject.new(username: "joe", password: "123456")
50
+ }.to raise_error(Banker::Error::InvalidParams,
51
+ "missing parameters `memorable_word` ")
52
+
53
+ end
54
+
55
+ it "accepts username, password and memorable_word" do
56
+ expect{
57
+
58
+ subject.new(username: "joe", password: "123456",
59
+ memorable_word: "superduper")
60
+ }.to_not raise_error
61
+ end
62
+ end
63
+
64
+ context "calls" do
65
+ it "calls params" do
66
+ subject.any_instance.should_receive(:params)
67
+ subject.new
68
+ end
69
+ it "calls authenticate!" do
70
+ subject.any_instance.should_receive(:authenticate!)
71
+ subject.new
72
+ end
73
+ it "calls get_data" do
74
+ subject.any_instance.should_receive(:get_data)
75
+ subject.new
76
+ end
77
+ end
78
+
79
+ context "private" do
80
+ before do
81
+ Mechanize.stub(:new).and_return(mechanize)
82
+ mechanize.stub(:get).and_return(mechanize)
83
+ mechanize.stub(:form_with).and_return(form)
84
+ mechanize.stub(:at).and_return(node)
85
+ mechanize.stub(:submit).and_return(mechanize)
86
+ end
87
+
88
+ describe "#authenticate!" do
89
+ before do
90
+ subject.any_instance.unstub(:authenticate!)
91
+ subject.any_instance.stub(:get_letter).and_return("A")
92
+ end
93
+ it "gets #{LOGIN_ENDPOINT}" do
94
+ mechanize.should_receive(:get).
95
+ with("https://bcol.barclaycard.co.uk/ecom/as2/initialLogon.do").
96
+ and_return(mechanize)
97
+ subject.new
98
+ end
99
+ it "finds by form action" do
100
+ mechanize.should_receive(:form_with).
101
+ with(action: "/ecom/as2/initialLogon.do").
102
+ and_return(form)
103
+ subject.new
104
+ end
105
+ it "fills in form inputs" do
106
+ mechanize.should_receive(:at).
107
+ with("label[for='lettera']").
108
+ and_return(node)
109
+
110
+ mechanize.should_receive(:at).
111
+ with("label[for='letterb']").
112
+ and_return(node)
113
+
114
+
115
+ node.should_receive(:content).
116
+ at_least(:twice).
117
+ and_return("1")
118
+
119
+ subject.any_instance.unstub(:get_letter)
120
+
121
+ form.should_receive(:[]=).with("username", "joe")
122
+ form.should_receive(:[]=).with("password", "123456")
123
+ form.should_receive(:[]=).with("firstAnswer", "S")
124
+ form.should_receive(:[]=).with("secondAnswer", "S")
125
+
126
+
127
+ subject.new(username: "joe", password: "123456",
128
+ memorable_word: "superduper")
129
+ end
130
+
131
+ it "submits form" do
132
+ form.should_receive(:buttons).
133
+ twice.
134
+ and_return([button])
135
+ mechanize.should_receive(:submit).
136
+ with(form,button)
137
+ subject.new
138
+ end
139
+
140
+ it "parses html for account limit" do
141
+ mechanize.should_receive(:at).
142
+ with(".panelSummary .limit .figure").
143
+ and_return(node)
144
+ subject.new
145
+ end
146
+
147
+ end
148
+
149
+ describe "#get_data" do
150
+ before do
151
+ subject.any_instance.unstub(:get_data)
152
+ mechanize.stub(get: stub(body: ofx))
153
+ end
154
+
155
+ it "gets #{EXPORT_ENDPOINT}" do
156
+ mechanize.should_receive(:get).
157
+ with(EXPORT_ENDPOINT).
158
+ and_return(stub(body: ofx))
159
+ subject.new
160
+ end
161
+ it "finds account balance" do
162
+
163
+ end
164
+ it "finds account limit"
165
+ end
166
+ end
167
+ end