kaspay 0.0.5 → 0.1.2

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
2
  SHA1:
3
- metadata.gz: 43081c96cd556ee5cc3f775ef57e9b4b2f7139e9
4
- data.tar.gz: be34341f29d037b715d41cb0fb2f4366c6975690
3
+ metadata.gz: 1105d0ccb68652d0db5be5c66b6700156941019f
4
+ data.tar.gz: 3ff3ba33823323e647a0f0c61c78fac65379807b
5
5
  SHA512:
6
- metadata.gz: 2a2fc30b97487116b4761f12110d435d046da79e6d293aabde5f49ac014c821ecdecb17ef3c0964705c223b8ffc8f41ce39bab261eb375639869d8b3519df676
7
- data.tar.gz: a81f7ade37886a8b8b9e0c5ca87b33532acdd96aaa41aca62ff88e9978b70445958af9362077f5d28d6375e1e6be5b1c0bcada06d77f7e3aec72f806f0bc5217
6
+ metadata.gz: 56525bb0c6852c73eeaa59b12f20f1c7aaf38e420c6f7b1d4e18cb7ff1a6dabaa657d5fc4ca89d3000ea9adee0f4199de50255edd28b1b79affed3d9c270ecd5
7
+ data.tar.gz: dbf60d5327aab90aa84d4c8692803de8dc004545d9f4713f5d2d370ebf8ae7eedb364a3f688614a6ce10c6d64963200b808e60091886e9cb3c580ae87fac5bfc
@@ -6,7 +6,7 @@ require 'kaspay/version'
6
6
  Gem::Specification.new do |spec|
7
7
  spec.name = "kaspay"
8
8
  spec.version = KasPay::VERSION
9
- spec.date = "2015-08-25"
9
+ spec.date = Time.now.strftime("%Y-%m-%d")
10
10
 
11
11
  spec.authors = ["Adrian Setyadi"]
12
12
  spec.email = ["a.styd@yahoo.com"]
@@ -6,19 +6,21 @@ require 'pstore'
6
6
  require 'watir'
7
7
  require 'headless'
8
8
  require_relative 'meta_stuff'
9
- require_relative 'kernel_patch'
9
+ require_relative 'kaspay/money'
10
+ require_relative 'kaspay/transaction'
10
11
 
11
12
  class KasPay
12
- # Assign methods from MetaStuff module as KasPay class methods
13
+ # Assigns methods from MetaStuff module as KasPay class methods
13
14
  extend MetaStuff
14
15
 
15
- # A bunch of constants belong to KasPay.
16
+ # A bunch of KasPay constants.
16
17
  BASE_URL = "https://www.kaspay.com"
17
18
  LOGIN_URL = BASE_URL + "/login"
18
- THINGS_TO_GET = %w(name balance acc_num).map(&:to_sym)
19
- THE_GET_METHODS = add__get__to(THINGS_TO_GET)
19
+ TRANSACTION_URL = BASE_URL + "/account/transactiondetails/"
20
+ TRANSACTION_HISTORY_URL = BASE_URL + "/account/history/"
20
21
  DATA_DIR = ENV['HOME'] + "/.kaspay"
21
- DATA_PATH = DATA_DIR + "/kaspay.dat"
22
+ LOGIN_PATH = DATA_DIR + "/login.dat"
23
+ COOKIE_PATH = DATA_DIR + "/cookie.dat"
22
24
 
23
25
  # Opening KasPay class singleton scope
24
26
  class << self
@@ -27,7 +29,7 @@ class KasPay
27
29
  # the information.
28
30
  static_pages = %w(about term) # more will come
29
31
 
30
- # Iterate through static pages list to make methods named the
32
+ # Iterates through static pages list to make methods named the
31
33
  # the same as the member of the list. These methods will be
32
34
  # the class methods of KasPay.
33
35
  # Example usage:
@@ -57,6 +59,65 @@ class KasPay
57
59
  alias_method :login, :new
58
60
  # Hidden to force the use of `login` as the class method
59
61
  # for instantiation.
62
+
63
+ def all_get_methods
64
+ instance_methods.grep /^get_/
65
+ end
66
+
67
+ def things_to_get
68
+ all_get_methods.map{|m| m.id2name.sub("get_","")}
69
+ end
70
+
71
+ def data_scope(path, &block)
72
+ kasdb = PStore.new(path)
73
+ kasdb.transaction do
74
+ yield(kasdb)
75
+ end
76
+ end
77
+
78
+ def login_scope(&block)
79
+ data_scope(LOGIN_PATH, &block)
80
+ end
81
+
82
+ def login_data_exists? login_name
83
+ data = nil
84
+ begin
85
+ login_scope {|login| data = login.roots}
86
+ return data.include? login_name
87
+ rescue NameError
88
+ return false
89
+ end
90
+ end
91
+
92
+ def clear_login login_name = nil
93
+ login_scope do |login|
94
+ if !login_name.nil?
95
+ login.delete login_name
96
+ elsif login_name.nil?
97
+ login.roots.each do |name|
98
+ login.delete name
99
+ end
100
+ end
101
+ end
102
+ end
103
+
104
+ def load_login login_name
105
+ raise LoadLoginError,
106
+ "login data \"#{login_name}\" cannot be found" \
107
+ unless login_data_exists? login_name
108
+ email, password = nil
109
+ login_scope do |login|
110
+ email = login[login_name][:email]
111
+ password = login[login_name][:password]
112
+ end
113
+ login email: email, password: password
114
+ end
115
+
116
+ def cookies_scope(&block)
117
+ data_scope(COOKIE_PATH, &block)
118
+ end
119
+
120
+
60
121
  private :new
61
122
  end
62
123
 
@@ -69,6 +130,8 @@ class KasPay
69
130
  attr_reader :password
70
131
 
71
132
  # Some methods need to be inaccessible
133
+ private :browser
134
+ private :browser=
72
135
  private :headless
73
136
  private :headless=
74
137
  private :password
@@ -83,18 +146,34 @@ class KasPay
83
146
  end
84
147
 
85
148
  # Actually starts the login process after email and password
86
- # provided. This is actualy different from the class method
87
- # `login` that only creates KasPay object and compounding the
88
- # data for login.
149
+ # provided. This is different from the class method `login`
150
+ # that only creates KasPay object and compounding the data
151
+ # for login.
89
152
  def login
90
153
  headless = Headless.new
91
154
  headless.start
92
155
 
93
156
  @browser = Watir::Browser.start LOGIN_URL
94
157
 
95
- browser.text_field(id: 'username').set email
96
- browser.text_field(id: 'password').set password
97
- browser.button(name: 'button').click
158
+ # Delete all expired cookies
159
+ delete_cookies_if_expire
160
+ # Use unexpired cookies that match the given email and password
161
+ the_cookies = use_cookies
162
+
163
+ # Fresh login
164
+ if the_cookies.nil?
165
+ browser.text_field(id: 'username').set email
166
+ browser.text_field(id: 'password').set password
167
+ browser.button(name: 'button').click
168
+ save_cookies
169
+ # Cookies still exist
170
+ else
171
+ browser.cookies.clear
172
+ the_cookies.each do |cookies|
173
+ browser.cookies.add(cookies[:name], cookies[:value])
174
+ end
175
+ browser.goto BASE_URL
176
+ end
98
177
  end
99
178
 
100
179
  # Sets email input
@@ -107,6 +186,7 @@ class KasPay
107
186
  def password= pass
108
187
  @password = pass
109
188
  login if user_data_complete?
189
+ return nil
110
190
  end
111
191
 
112
192
  def current_url
@@ -117,26 +197,117 @@ class KasPay
117
197
  url = (path[0] == "/" ? (BASE_URL + path) : path)
118
198
  browser.goto url
119
199
  end
120
-
200
+
121
201
  def get_name
122
202
  browser.span(class: "user-name").text
123
203
  end
124
204
 
125
205
  def get_balance
126
- Money.new browser.div(class: "kaspay-balance") \
127
- .span.text.sub("Rp ","").sub(".","").to_i
206
+ Money.new browser.div(class: "kaspay-balance").span.text
128
207
  end
129
208
 
130
209
  def get_acc_num
131
210
  browser.span(class: "kaspay-id").text.sub("KasPay Account: ", "").to_i
132
211
  end
133
-
212
+
213
+ def get_cookies_expire_time
214
+ current_cookies_scope do |cookies, cookies_name|
215
+ cookies[cookies_name][:cookies].each do |el|
216
+ if el[:name] == "kaspay_csrf_cookie"
217
+ return DateTime.parse(el[:expires].to_s)
218
+ end
219
+ end
220
+ end
221
+ end
222
+
223
+ def trx_type_to_num val_in_sym
224
+ num = case val_in_sym
225
+ when :all_trx then 0
226
+ when :top_up then 1
227
+ when :payment_to then 2
228
+ when :payment_from then 3
229
+ when :redeem then 4
230
+ when :refund then 5
231
+ when :trx_fee then 6
232
+ else 0
233
+ end
234
+ return num
235
+ end
236
+
237
+ def get_transaction options = {}
238
+ default_options = {
239
+ latest: 5,
240
+ type: :all_trx,
241
+ oldest_only: false
242
+ }
243
+ options = default_options.merge(options)
244
+ unless options[:oldest_only]
245
+ no_of_trx = options[:latest]
246
+ get_latest_transactions no_of_trx, trx_type_to_num(options[:type])
247
+ else
248
+ trx_no = options[:latest] - 1
249
+ get_transaction_number trx_no, trx_type_to_num(options[:type])
250
+ end
251
+ end
252
+
253
+ def get_latest_transactions no_of_trx, type = 0
254
+ trx_ids = []
255
+ while no_of_trx - trx_ids.length > 0
256
+ browser.goto(TRANSACTION_HISTORY_URL \
257
+ + trx_ids.length.to_s \
258
+ + "?f=01/01/2009&t=" \
259
+ + Time.now.strftime("%d/%m/%Y") \
260
+ + "&transactiontype=" \
261
+ + type.to_s)
262
+
263
+ browser.tds(class: "trxid").each do |td|
264
+ trx_ids << td.link.href.sub(/.*\/(.*)$/, '\1')
265
+ break if trx_ids.length == no_of_trx
266
+ end
267
+ break if trx_ids.length == 0 \
268
+ || trx_ids.length >= no_of_trx
269
+ end
270
+ trx_ids.each_with_index do |trx_id, i|
271
+ trx_ids[i] = Transaction.new trx_id, browser
272
+ end
273
+ browser.goto(BASE_URL)
274
+ return trx_ids
275
+ end
276
+
277
+ def get_transaction_number trx_no, type = 0
278
+ row_number = trx_no % 5
279
+ browser.goto(TRANSACTION_HISTORY_URL \
280
+ + (trx_no - row_number).to_s \
281
+ + "?f=01/01/2009&t=" \
282
+ + Time.now.strftime("%d/%m/%Y") \
283
+ + "&transactiontype=" \
284
+ + type.to_s)
285
+
286
+ trx_id = Transaction.new(browser.tds(class: "trxid")[row_number] \
287
+ .link.href.sub(/.*\/(.*)$/, '\1'), browser)
288
+ browser.goto(BASE_URL)
289
+ return trx_id
290
+ end
291
+
292
+ def links
293
+ links = []
294
+ browser.tds(class: "trxid").each{|td| links << td.link.href.to_s}
295
+ links
296
+ end
297
+
134
298
  def home
135
299
  goto "/"
136
300
  end
137
-
301
+
138
302
  def logout!
303
+ calling_method = caller[0][/(?<=`).*(?=')/]
139
304
  logout_link.click
305
+ delete_cookies unless calling_method == "delete_cookies"
306
+ end
307
+
308
+ # Silently logs out
309
+ def logout
310
+ logout! if logged_in?
140
311
  end
141
312
 
142
313
  def logged_in?
@@ -154,96 +325,90 @@ class KasPay
154
325
 
155
326
  def save_login login_name
156
327
  Dir.mkdir(DATA_DIR) unless Dir.exists?(DATA_DIR)
157
- kasdb = PStore.new(DATA_PATH)
158
- kasdb.transaction do
159
- kasdb[login_name] = {email: email, password: password}
160
- end
161
- end
162
-
163
- def load_login login_name
164
- kasdb = PStore.new(DATA_PATH)
165
- kasdb.transaction do
166
- @email = kasdb[login_name][:email]
167
- @password = kasdb[login_name][:password]
168
- return kasdb[login_name]
328
+ KasPay.login_scope do |login|
329
+ login[login_name] = {email: email, password: password}
169
330
  end
331
+ return nil
170
332
  end
171
-
172
- def inspect
173
- "#<#{self.class}:0x#{(object_id << 1).to_s(16)} logged_in=#{logged_in?}>"
174
- end
175
-
176
- def method_missing(m, *args, &block)
177
- if THINGS_TO_GET.include? m
178
- send("get_#{m}")
179
- else
180
- super
181
- end
182
- end
183
333
 
184
334
  def check_login
185
335
  raise LoginError, "you are not logged in" unless logged_in?
186
336
  end
337
+
338
+ def inspect
339
+ "#<#{self.class}:0x#{(object_id << 1).to_s(16)} logged_in=#{logged_in?}>"
340
+ end
187
341
 
188
- before( THE_GET_METHODS + [:logout!] ){ :check_login }
189
-
190
- alias_method :logout, :logout!
342
+ before( all_get_methods + [:logout!] ){ :check_login }
191
343
  alias_method :url, :current_url
192
-
193
- private
194
-
195
- def login_data_exists?
196
- data = nil
197
- begin
198
- PStore.new("kaspay.dat").tap{|x| x.transaction{ data = x.roots}}
199
- return (data != [])
200
- rescue NameError
201
- return false
202
- end
344
+ KasPay.things_to_get.each do |m|
345
+ alias_method m, "get_#{m}"
203
346
  end
204
-
205
- def logout_link
206
- logout_link = browser.a(href: "https://www.kaspay.com/account/logout")
347
+
348
+ private
349
+
350
+ def get_cookies
351
+ browser.cookies.to_a
207
352
  end
208
-
209
- # inner class Money
210
- class Money
211
- attr_accessor :value
212
-
213
- fixnum_methods_to_discard = %w(inspect -@ abs magnitude to_s dclone ~ & | ^ [] << >> size bit_length to_f).map(&:to_sym)
214
- new_methods = Fixnum.instance_methods(false) \
215
- .delete_if{|m| fixnum_methods_to_discard.include? m}
216
-
217
- new_methods.each do |m|
218
- define_method(m) do |arg, &block|
219
- arg = arg.to_i # to convert string or Money object to Integer
220
- self.value = value.send(m, arg)
221
- return self
353
+
354
+ def current_cookies_scope
355
+ KasPay.cookies_scope do |cookies|
356
+ cookies.roots.reverse.each do |cookies_name|
357
+ if cookies[cookies_name][:email] == email \
358
+ && cookies[cookies_name][:password] == password
359
+ yield(cookies, cookies_name)
360
+ end
222
361
  end
223
362
  end
224
-
225
- def initialize value = 0
226
- @value = Integer(value)
363
+ end
364
+
365
+ def delete_cookies_if_expire
366
+ current_cookies_scope do |cookies, cookies_name|
367
+ cookies[cookies_name][:cookies].each do |el|
368
+ if el[:name] == "kaspay_csrf_cookie" \
369
+ && DateTime.parse(el[:expires].to_s) < DateTime.now
370
+ cookies.delete(cookies_name)
371
+ end
372
+ end
227
373
  end
228
-
229
- def to_f
230
- value * 1.0
374
+ end
375
+
376
+ def delete_cookies
377
+ current_cookies_scope do |cookies, cookies_name|
378
+ cookies.delete(cookies_name)
231
379
  end
232
-
233
- def to_s
234
- "Rp " + value.to_s.reverse.gsub(/(\d{3})(?=\d)/, '\\1,').reverse + ".00"
380
+ logout! if logged_in?
381
+ return nil
382
+ end
383
+
384
+ def save_cookies
385
+ time = Time.now
386
+ cookies_name = time.strftime("%y%m%d%H%M%S").to_i.to_s(36)
387
+ Dir.mkdir(DATA_DIR) unless Dir.exists?(DATA_DIR)
388
+ KasPay.cookies_scope do |cookies|
389
+ cookies[cookies_name] = {email: email, password: password, cookies: get_cookies}
235
390
  end
391
+ end
236
392
 
237
- def inspect
238
- "#<#{self.class}:0x#{(object_id << 1).to_s(16)} value=#{value}>"
393
+ def use_cookies
394
+ the_cookie = nil
395
+ current_cookies_scope do |cookies, cookies_name|
396
+ the_cookie = cookies[cookies_name][:cookies]
239
397
  end
240
-
241
- alias_method :to_i, :value
398
+ return the_cookie
399
+ end
400
+
401
+ def logout_link
402
+ logout_link = browser.a(href: "https://www.kaspay.com/account/logout")
242
403
  end
243
404
  end
244
405
 
406
+ # Aliases for 'KasPay' class
407
+ Kaspay = KasPay
408
+ KASPAY = KasPay
409
+
245
410
  class LoginError < StandardError
246
411
  end
247
412
 
248
- class UserDataError < StandardError
413
+ class LoadLoginError < StandardError
249
414
  end
@@ -0,0 +1,37 @@
1
+ class KasPay
2
+ # Inner class Money
3
+ class Money
4
+ attr_accessor :value
5
+
6
+ fixnum_methods_to_discard = %w(inspect -@ abs magnitude to_s dclone ~ & | ^ [] << >> size bit_length to_f).map(&:to_sym)
7
+ new_methods = Fixnum.instance_methods(false) \
8
+ .delete_if{|m| fixnum_methods_to_discard.include? m}
9
+
10
+ # Inheriting some methods from Fixnum
11
+ new_methods.each do |m|
12
+ define_method(m) do |arg, &block|
13
+ arg = arg.to_i # to convert string or Money object to Integer
14
+ self.value = value.send(m, arg)
15
+ return self
16
+ end
17
+ end
18
+
19
+ def initialize value = 0
20
+ @value = value.gsub(/\s/, '').sub(/[\.\,]00$/, "").sub(/^Rp\s*(.*)$/, '\1').gsub(/[\.\,]/, '').to_i
21
+ end
22
+
23
+ def to_f
24
+ value * 1.0
25
+ end
26
+
27
+ def to_s
28
+ "Rp " + value.to_s.reverse.gsub(/(\d{3})(?=\d)/, '\\1,').reverse + ".00"
29
+ end
30
+
31
+ def inspect
32
+ "#<#{self.class}:0x#{(object_id << 1).to_s(16)} value=#{value}>"
33
+ end
34
+
35
+ alias_method :to_i, :value
36
+ end
37
+ end
@@ -0,0 +1,87 @@
1
+ class KasPay
2
+ # Inner class Transaction
3
+ class Transaction
4
+ def initialize trx_id, browser
5
+ @trx_id = trx_id
6
+ @browser = browser
7
+ end
8
+
9
+ def data_available?
10
+ respond_to? :amount
11
+ end
12
+
13
+ # To be called only when needed
14
+ def data_build
15
+ @browser.goto(KasPay::TRANSACTION_URL + @trx_id)
16
+ data = []
17
+ @browser.tds.each_slice(3){|a, b, c| data << c.text}
18
+ @date = DateTime.parse(data[0] + "T" \
19
+ + data[1] + "+07:00")
20
+ @trx_id = data[2]
21
+ @trx_type = data[3]
22
+ @remark = data[4]
23
+ @status = data[5]
24
+ m1_in_sym = []
25
+ (instance_variables - [:@browser]).each do |v|
26
+ m1_in_sym << v.to_s.sub(/@/, '').to_sym
27
+ end
28
+ m1_in_sym.each do |m|
29
+ define_singleton_method m do
30
+ eval "@#{m.to_s}"
31
+ end
32
+ end
33
+
34
+ non_payment_type = ["Transaction correction", "Topup"]
35
+ unless non_payment_type.include? @trx_type
36
+ @seller = data[6]
37
+ @merchant_trx_id = data[7]
38
+ @product_id = data[8]
39
+ @product_name = data[9]
40
+ @quantity = data[10].gsub(/[^0-9]/, '').to_i
41
+ @description = data[11]
42
+ m2_in_sym = []
43
+ (instance_variables - [:@browser]).each do |v|
44
+ m2_in_sym << v.to_s.sub(/@/, '').to_sym
45
+ end
46
+ m2_in_sym = m2_in_sym - m1_in_sym
47
+ m2_in_sym.each do |m|
48
+ define_singleton_method m do
49
+ eval "@#{m.to_s}"
50
+ end
51
+ end
52
+ @amount = Money.new data[12]
53
+ else
54
+ @amount = Money.new data[6]
55
+ end
56
+
57
+ define_singleton_method :amount do
58
+ @amount
59
+ end
60
+ end
61
+
62
+ def to_h
63
+ h = {}
64
+ instance_variables.each do |v|
65
+ m_in_sym = v.to_s.sub(/@/, '').to_sym
66
+ h[m_in_sym] = send m_in_sym
67
+ end
68
+ return h
69
+ end
70
+
71
+ def inspect
72
+ "#<#{self.class}:0x#{(object_id << 1).to_s(16)} id=#{@trx_id} browser=#{@browser}>"
73
+ end
74
+
75
+ def method_missing(name, *args, &block)
76
+ if data_available?
77
+ super
78
+ else
79
+ data_build
80
+ send(name, *args, &block)
81
+ end
82
+ end
83
+
84
+ alias_method :to_s, :to_h
85
+
86
+ end
87
+ end
@@ -0,0 +1,5 @@
1
+ class KasPay
2
+ # Inner class TransactionCollection
3
+ class TransactionCollection
4
+ end
5
+ end
@@ -1,3 +1,3 @@
1
1
  class KasPay
2
- VERSION = "0.0.5"
2
+ VERSION = "0.1.2"
3
3
  end
@@ -1,17 +1,13 @@
1
1
  module MetaStuff
2
- def method_missing(m, *args, &block)
3
- raise NoMethodError, "undefined method `#{m}' for #{self}:#{self.class}"
4
- end
5
-
6
- def before(names, &block)
7
- names.each do |name|
8
- m = instance_method(name)
9
- n = instance_method(yield)
10
- define_method(name) do
11
- n.bind(self).call
12
- m.bind(self).call
2
+ def before(names)
3
+ names.each do |name|
4
+ m = instance_method(name)
5
+ n = instance_method(yield)
6
+ define_method(name) do |*args, &block|
7
+ n.bind(self)
8
+ m.bind(self).(*args, &block)
9
+ end
13
10
  end
14
- end
15
- private yield
16
- end
11
+ private yield
12
+ end
17
13
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kaspay
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Adrian Setyadi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-08-25 00:00:00.000000000 Z
11
+ date: 2015-10-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: watir
@@ -62,8 +62,10 @@ files:
62
62
  - README.md
63
63
  - kaspay.gemspec
64
64
  - lib/kaspay.rb
65
+ - lib/kaspay/money.rb
66
+ - lib/kaspay/transaction.rb
67
+ - lib/kaspay/transaction_collection.rb
65
68
  - lib/kaspay/version.rb
66
- - lib/kernel_patch.rb
67
69
  - lib/meta_stuff.rb
68
70
  homepage: https://github.com/styd/kaspay
69
71
  licenses:
@@ -1,5 +0,0 @@
1
- module Kernel
2
- def add__get__to array
3
- array.map {|el| "get_#{el}".to_sym}
4
- end
5
- end