squeegee 0.0.1

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.
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