squeegee 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,19 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ .zshrc
7
+ Gemfile.lock
8
+ InstalledFiles
9
+ _yardoc
10
+ coverage
11
+ doc/
12
+ lib/bundler/man
13
+ pkg
14
+ rdoc
15
+ spec/reports
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
19
+ kyle.rb
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm use 1.9.3@squeegee --create
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.2
4
+ - 1.9.3
5
+ script: rspec
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in squeegee.gemspec
4
+ gemspec
5
+
6
+ gem 'guard-rspec'
7
+ gem "cane", "~> 1.1.0"
data/Guardfile ADDED
@@ -0,0 +1,11 @@
1
+ guard 'rspec', :notification => false,:version => 2, :cli => "--colour", :run_all => {:cli => '--profile'} do
2
+ watch(%r{^spec/.+_spec\.rb$})
3
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
4
+ watch('spec/spec_helper.rb') { "spec/" }
5
+ end
6
+
7
+ guard 'bundler' do
8
+ watch('Gemfile')
9
+ watch(/^.+\.gemspec/)
10
+ end
11
+
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Kyle Welsby
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,47 @@
1
+ # Squeegee [![CI Build Status](https://secure.travis-ci.org/BritRuby/Squeegee.png?branch=master)][travis] [![Dependency Status](https://gemnasium.com/BritRuby/Squeegee.png?travis)][gemnasium]
2
+
3
+ [travis]:http://travis-ci.org/BritRuby/Squeegee
4
+ [gemnasium]:https://gemnasium.com/BritRuby/Squeegee
5
+
6
+ Squeegee is a collection of login strategies to gather bill dates and amounts
7
+ from customer accounts.
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ gem 'squeegee'
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install squeegee
22
+
23
+ ## Usage
24
+
25
+ Get the next bill statement for British Gas
26
+
27
+ user_credentials = {
28
+ :username => "JoeBlogs",
29
+ :password => "superduper",
30
+ :customer_number => "8500xxxxxxx"
31
+ }
32
+
33
+ s = Squeegee::BritishGas.new(user_credentials)
34
+
35
+ s.accounts.first #=> { :due_at => "2012-03-23",
36
+ :amount => 10000,
37
+ }
38
+
39
+
40
+
41
+ ## Contributing
42
+
43
+ 1. Fork it
44
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
45
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
46
+ 4. Push to the branch (`git push origin my-new-feature`)
47
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
@@ -0,0 +1,24 @@
1
+ module Squeegee
2
+ class Base
3
+ attr_writer :keys, :agent, :amount, :paid, :due_at
4
+ def params(args)
5
+ missing_keys = []
6
+ return unless defined? @keys
7
+ @keys.each do |key|
8
+ missing_keys << key unless args.has_key?(key.to_sym)
9
+ end
10
+ if missing_keys.any?
11
+ raise Error::InvalidParams,
12
+ "missing parameters #{missing_keys.map {|key| "`#{key}` "}.join}"
13
+ end
14
+ end
15
+
16
+ def get(url)
17
+ @agent ||= Mechanize.new
18
+ @agent.log = Logger.new 'squeegee.log'
19
+ @agent.user_agent = "Mozilla/5.0 (Squeegee)"
20
+ @agent.default_encoding = "utf8"
21
+ @agent.get(url)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,88 @@
1
+ module Squeegee
2
+
3
+ # BritishGas - Energy Supplier
4
+ # * can have more than one account.
5
+ #
6
+ class BritishGas < Base
7
+ HOST = "https://www.britishgas.co.uk"
8
+ LOGIN_URL = "#{HOST}/Your_Account/Account_Details/"
9
+ ACCOUNTS_URL = "#{HOST}/Account_History/Transactions_Account_List/"
10
+ ACCOUNT_URL = "#{HOST}/Your_Account/Account_Transaction/"
11
+
12
+ # British Gas Account information Extration
13
+ # Example:
14
+ # BritishGas::Account("8500", Mecanize.new)
15
+ #
16
+ class Account < BritishGas
17
+ attr_accessor :paid, :due_at, :amount
18
+
19
+ def initialize(id, agent)
20
+ @agent = agent
21
+ url = "#{Squeegee::BritishGas::ACCOUNT_URL}?accountnumber=#{id}"
22
+ page = get(url)
23
+ table = page.search("div#divHistoryTable table tbody")
24
+ rows = table.search("tr").map do |row|
25
+ tds = row.search("td")
26
+ _row = {
27
+ date: Date.parse(
28
+ tds.first.inner_text.match(/\d{2}\s\w{3}\s\d{4}/)[0]
29
+ ),
30
+ type: tds[1].inner_text.match(/[A-Za-z]{2,}\s?[A-Za-z]?{2,}/)[0],
31
+ debit: tds[2].inner_text.to_f,
32
+ credit: tds[3].inner_text.to_f,
33
+ balance: tds[4].inner_text.to_f
34
+ }
35
+ _row
36
+ end
37
+ @paid = !!(rows[0][:balance] = 0)
38
+ rows.each do |row|
39
+ if row[:debit] > 0
40
+ @amount = row[:debit].to_s.gsub(/\.|,/,'').to_i
41
+ @due_at = row[:date]
42
+ break
43
+ end
44
+ end
45
+ end
46
+ end
47
+
48
+ attr_accessor :accounts
49
+
50
+ FIELD = {
51
+ email: "userName",
52
+ password: "password"
53
+ }
54
+
55
+ def initialize(args = {})
56
+ @keys = %w(email password)
57
+
58
+ params(args)
59
+ @email = args.delete(:email)
60
+ @password = args.delete(:password)
61
+
62
+ authenticate!
63
+ get_statements
64
+ end
65
+
66
+ private
67
+
68
+ def authenticate!
69
+ page = get(LOGIN_URL)
70
+ form = page.form_with(action: '/Online_User/Account_Summary/')
71
+
72
+ form[FIELD[:email]] = @email
73
+ form[FIELD[:password]] = @password
74
+
75
+ page = @agent.submit(form, form.buttons.first)
76
+ #raise Error::Unauthorized, "Account details could be wrong" if page.at('.error')
77
+ end
78
+
79
+ def get_statements
80
+ page = get(ACCOUNTS_URL)
81
+ account_ids = page.search("table#tableSelectAccount td > strong").collect {|row| row.content.to_i}
82
+ @accounts = account_ids.map do |account_id|
83
+ Account.new(account_id, @agent)
84
+ end
85
+ end
86
+
87
+ end
88
+ end
@@ -0,0 +1,61 @@
1
+ module Squeegee
2
+
3
+ # British Sky Broadcasting (BSkyB) - Premium Television
4
+ #
5
+ class BSkyB < Base
6
+ LOGIN_URL = "https://skyid.sky.com/signin/accountmanagement"
7
+ ACCOUNT_URL = "https://myaccount.sky.com/?action=viewbills"
8
+
9
+ FIELD = {
10
+ username: 'username',
11
+ password: 'password'
12
+ }
13
+
14
+ attr_accessor :due_at, :amount, :paid
15
+
16
+ def initialize(args = {})
17
+ @keys = %w(username password)
18
+
19
+ params(args)
20
+ @username = args.delete(:username)
21
+ @password = args.delete(:password)
22
+
23
+ authenticate!
24
+ get_statement
25
+ end
26
+
27
+ private
28
+
29
+ def authenticate!
30
+ page = get(LOGIN_URL)
31
+ form = page.form_with(name: 'signinform')
32
+
33
+ form[FIELD[:username]] = @username
34
+ form[FIELD[:password]] = @password
35
+
36
+ @agent.submit(form, form.buttons.first)
37
+ end
38
+
39
+ def get_statement
40
+ page = get(ACCOUNT_URL)
41
+ amount = page.search(
42
+ "#outstanding_balance_total span.money-left"
43
+ ).inner_text.gsub(/\.|,/,'').match(/\d{1,}/)
44
+
45
+ @amount = amount[0].to_i if amount
46
+
47
+ due_at = page.search(
48
+ "#outstanding_balance_box_label h5 span"
49
+ ).inner_text.match(/(\d{2})\/(\d{2})\/(\d{2})/)
50
+
51
+ @due_at = Date.parse("20#{due_at[3]}-#{due_at[2]}-#{due_at[1]}")if due_at
52
+
53
+ @paid = page.search(
54
+ "#payments .bill .desc"
55
+ ).inner_text.downcase.include?("received")
56
+
57
+ rescue NoMethodError => e
58
+ raise Error::PageMissingContent, "Can't find something on the page"
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,10 @@
1
+ module Squeegee
2
+ module Error
3
+ # Invalid Parameters - raised when ever a parameter is listed as missing
4
+ class InvalidParams < ArgumentError;end
5
+ # Page Missing Content - raised when the page expectations have changed
6
+ class PageMissingContent < NoMethodError;end
7
+ # Unauthenticated - raised when authentication fails.
8
+ class Unauthenticated < NoMethodError;end
9
+ end
10
+ end
@@ -0,0 +1,55 @@
1
+ module Squeegee
2
+
3
+ # OrangeUK - Mobile network
4
+ #
5
+ class OrangeUK < Base
6
+ LOGIN_URL = "https://web.orange.co.uk/r/login/"
7
+ BILLS_URL = "https://www.youraccount.orange.co.uk/sss/jfn?mfunc=63&jfnRC=1"
8
+ LOGIN_POST_URL = "https://web.orange.co.uk/id/signin.php?rm=StandardSubmit"
9
+
10
+ FIELD = {
11
+ username: 'LOGIN',
12
+ password: 'PASSWORD'
13
+ }
14
+
15
+ attr_accessor :paid, :amount, :due_at
16
+
17
+ def initialize(args = {})
18
+ @keys = %w(username password)
19
+
20
+ params(args)
21
+ @username = args.delete(:username)
22
+ @password = args.delete(:password)
23
+
24
+ authenticate!
25
+ get_statement
26
+ end
27
+
28
+ private
29
+
30
+ def authenticate!
31
+ page = get(LOGIN_URL)
32
+ form = page.form_with(action: '/id/signin.php?rm=StandardSubmit')
33
+
34
+ form[FIELD[:username]] = @username
35
+ form[FIELD[:password]] = @password
36
+ page = @agent.submit(form, form.buttons.first)
37
+
38
+ if page.uri == LOGIN_POST_URL && !page.search('.error').nil?
39
+ raise Squeegee::Error::Unauthenticated
40
+ end
41
+ end
42
+
43
+ def get_statement
44
+ page = get(BILLS_URL)
45
+
46
+ last_bill = page.search("#eBillMainContent .eBillStandardTable").first
47
+
48
+ balance = page.search("#paymBalanceIncVAT").inner_text.gsub(/\.|,/,'').match(/\d{1,}/)
49
+
50
+ @due_at = Date.parse(last_bill.search("td")[0].inner_text)
51
+ @amount = last_bill.search('td')[2].inner_text.gsub(/\.|,/,'').match(/\d{1,}/)[0].to_i
52
+ #@paid = balance || balance[0].to_i >= 0
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,3 @@
1
+ module Squeegee
2
+ VERSION = "0.0.1"
3
+ end
data/lib/squeegee.rb ADDED
@@ -0,0 +1,9 @@
1
+ require 'mechanize'
2
+ require 'logger'
3
+ require 'squeegee/version'
4
+ require 'squeegee/error'
5
+ require 'squeegee/base'
6
+
7
+ require 'squeegee/british_gas'
8
+ require 'squeegee/bskyb'
9
+ require 'squeegee/orange_uk'
@@ -0,0 +1,23 @@
1
+ unless ENV['travis']
2
+ require 'simplecov'
3
+ SimpleCov.start do
4
+ add_filter "/spec"
5
+ end
6
+ end
7
+
8
+ require 'rspec'
9
+ require 'webmock'
10
+ require 'squeegee'
11
+
12
+ CONFIGS = YAML::load(File.open('spec/support/configs.yml'))
13
+
14
+ RSpec.configure do |config|
15
+ config.order = :rand
16
+ config.color_enabled = true
17
+
18
+ config.filter_run :focus => true
19
+ config.run_all_when_everything_filtered = true
20
+
21
+ config.before(:each) do
22
+ end
23
+ end
@@ -0,0 +1,38 @@
1
+ require 'spec_helper'
2
+
3
+ describe Squeegee::Base do
4
+ describe "#params" do
5
+ it "should raise InvalidParams if any parameters are missing" do
6
+ expect{
7
+ subject.keys = ['test']
8
+ subject.params({})
9
+ }.to raise_error(Squeegee::Error::InvalidParams, "missing parameters `test` ")
10
+ end
11
+
12
+ it "should raise error if no arguments given" do
13
+ expect{
14
+ subject.params
15
+ }.to raise_error(ArgumentError, "wrong number of arguments (0 for 1)")
16
+ end
17
+
18
+ it "should return nil if keys are not defined" do
19
+ subject.params({}).should be_nil
20
+ end
21
+ end
22
+
23
+ describe "#get" do
24
+ let(:mechanize) {mock('mechanize')}
25
+ let(:logger) {mock('logger')}
26
+ it "should assign agent with a new instance of Mechanize" do
27
+ Mechanize.should_receive(:new).and_return(mechanize)
28
+
29
+ mechanize.should_receive(:user_agent=).with("Mozilla/5.0 (Squeegee)")
30
+ mechanize.should_receive(:default_encoding=).with("utf8")
31
+ Logger.should_receive(:new).with('squeegee.log').and_return(logger)
32
+ mechanize.should_receive(:log=).with(logger)
33
+ mechanize.should_receive(:get).with("http://google.com")
34
+
35
+ subject.get("http://google.com")
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,155 @@
1
+ require 'spec_helper'
2
+
3
+ describe Squeegee::BritishGas do
4
+ let(:mechanize) {mock('mechanize') }
5
+ let(:node) {mock('node')}
6
+ let(:form) {mock('form')}
7
+ let(:button) {mock('button')}
8
+
9
+ subject {Squeegee::BritishGas}
10
+
11
+ context "classes" do
12
+ before do
13
+ Mechanize.any_instance.stub(get: mechanize)
14
+ end
15
+ subject {Squeegee::BritishGas::Account.new(1, mechanize.as_null_object)}
16
+ it "should get single account" do
17
+ pending
18
+ Mechanize.any_instance.should_receive(:get).at_least(:once).with(
19
+ "https://www.britishgas.co.uk/Your_Account/Account_Transaction/?accountnumber=1"
20
+ ).and_return(mechanize.as_null_object)
21
+
22
+ subject
23
+ end
24
+ it "should build array of data" do
25
+ mechanize.should_receive(:search).with(
26
+ "div#divHistoryTable table tbody"
27
+ ).and_return(node)
28
+
29
+ node.should_receive(:search).with("tr").and_return([node])
30
+ node.should_receive(:search).with("td").and_return(
31
+ [
32
+ stub(inner_text: "12 Feb 2012"),
33
+ stub(inner_text: "Payment Received"),
34
+ stub(inner_text: ""),
35
+ stub(inner_text: "30.50"),
36
+ stub(inner_text: "0.00")
37
+ ]
38
+ )
39
+
40
+ subject.paid.should be_true
41
+ end
42
+ end
43
+
44
+ context "Parameters" do
45
+ before do
46
+ subject.any_instance.stub(:authenticate!)
47
+ subject.any_instance.stub(:get_statements)
48
+ end
49
+ it "raises InvalidParams when email is missing" do
50
+ expect{
51
+ subject.new
52
+ }.to raise_error(Squeegee::Error::InvalidParams,
53
+ "missing parameters `email` `password` ")
54
+ end
55
+
56
+ it "raises InvalidParams when password is missing" do
57
+ expect{
58
+ subject.new(email: "test@test.com")
59
+ }.to raise_error(Squeegee::Error::InvalidParams,
60
+ "missing parameters `password` ")
61
+ end
62
+
63
+ it "accepts email" do
64
+ expect{
65
+ subject.new(email: "test@test.com", password: "superduper")
66
+ }.to_not raise_error
67
+ end
68
+ end
69
+ context "calls" do
70
+ before do
71
+ subject.any_instance.stub(:params)
72
+ subject.any_instance.stub(:authenticate!)
73
+ subject.any_instance.stub(:get_statements)
74
+ end
75
+
76
+ it "calls authenticate!" do
77
+ subject.any_instance.should_receive(:authenticate!)
78
+ subject.new
79
+ end
80
+
81
+ it "calls get_accounts" do
82
+ subject.any_instance.should_receive(:get_statements)
83
+ subject.new
84
+ end
85
+
86
+ it "calls params with arguments" do
87
+ args = {:a => 'b'}
88
+ subject.any_instance.should_receive(:params).with(args)
89
+ subject.new(args)
90
+ end
91
+ end
92
+
93
+ context "Authentication" do
94
+ before do
95
+ subject.any_instance.stub(:params)
96
+ #subject.any_instance.stub(get: mechanize.as_null_object)
97
+ #Mechanize.any_instance.stub(get: mechanize.as_null_object)
98
+ Mechanize.should_receive(:new).and_return(mechanize.as_null_object)
99
+ mechanize.stub(form_with: form.as_null_object)
100
+ end
101
+ it "navigates to login URL" do
102
+ mechanize.should_receive(:get).with(
103
+ "https://www.britishgas.co.uk/Your_Account/Account_Details/"
104
+ ).and_return(mechanize)
105
+
106
+ subject.new({})
107
+ end
108
+
109
+ it "finds by form action" do
110
+ mechanize.should_receive(:form_with).with(
111
+ action: '/Online_User/Account_Summary/'
112
+ ).and_return(form.as_null_object)
113
+
114
+ subject.new
115
+ end
116
+
117
+ it "fills in form inputs" do
118
+ form.should_receive(:[]=).with('userName', 'test@test.com')
119
+ form.should_receive(:[]=).with('password', 'superduper')
120
+
121
+ subject.new(email: "test@test.com", password: "superduper")
122
+ end
123
+
124
+ it "submits with first button" do
125
+ form.should_receive(:buttons).and_return([button])
126
+ mechanize.should_receive(:submit).with(form, button)
127
+
128
+ subject.new(email: "test")
129
+ end
130
+ end
131
+
132
+ context "get_accounts" do
133
+ before do
134
+ subject.any_instance.stub(:params)
135
+ Mechanize.any_instance.stub(get: mechanize)
136
+ subject.any_instance.stub(:authenticate!)
137
+ mechanize.stub(search: [stub(content: " 1 ")])
138
+ subject::Account.stub(:new, mechanize)
139
+ end
140
+
141
+ it "navigates to account list URL" do
142
+ Mechanize.any_instance.should_receive(:get).with(
143
+ "https://www.britishgas.co.uk/Account_History/Transactions_Account_List/"
144
+ ).and_return(mechanize)
145
+ subject.new
146
+ end
147
+
148
+ it "initialises a new account" do
149
+ pending
150
+ #mechanize.stub(search: [stub(content: " 1 ")])
151
+ #subject::Account.should_receive(:new).with(1, Mechanize.new())
152
+ #subject.new
153
+ end
154
+ end
155
+ end