kaspay 0.0.5 → 0.1.2

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