easycard 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: de65d5a1fa9046e9b85a468ce23da1c259b968cf
4
+ data.tar.gz: c0c60ef9ea374f6957fa657dd4092dc7ee70565b
5
+ SHA512:
6
+ metadata.gz: 602fdb29058772249ab6eb56b19bf43a21025f8b1ee6d8608ddb72f8ab89e0d1ec168fb6e0cbd742e8d9fec4e544f9089b45f393e71a2b079060bbc258cc7e54
7
+ data.tar.gz: 5257b248199a25b517710f2421c5fd86f2e0bf1352457aa2c26556da2529fd3f218097d8ab5a6095b5d40cda859c10a75ea296642b1039a1ef80a6566f8f25fb
@@ -0,0 +1,57 @@
1
+ #!/usr/bin/env ruby
2
+ require 'optparse'
3
+ require 'optparse/time'
4
+ require 'easycard'
5
+
6
+ options = {format: :table, from: Date.today << 1, to: Date.today}
7
+ parser = OptionParser.new do |opts|
8
+ opts.banner = 'Usage: easycard CARD_ID [options]'
9
+
10
+ opts.on('-mNUMBER', '--months=NUMBER', Float, '最近 n 月') do |n|
11
+ today = Date.today
12
+ options[:from] = today << n
13
+ options[:to] = today
14
+ end
15
+
16
+ opts.on('-dNUMBER', '--days=NUMBER', Float, '最近 n 天') do |n|
17
+ today = Date.today
18
+ options[:from] = today - n
19
+ options[:to] = today
20
+ end
21
+
22
+ opts.on('-j', '--json', '以 JSON 格式顯示') do
23
+ options[:format] = :json
24
+ end
25
+
26
+ opts.on('-y', '--yaml', '以 YAML 格式顯示') do
27
+ options[:format] = :yaml
28
+ end
29
+
30
+ opts.on('-f', '--from=YYYY/MM/DD', Time, '起始時間') do |time|
31
+ options[:from] = time
32
+ end
33
+
34
+ opts.on('-t', '--to=YYYY/MM/DD', Time, '結束時間') do |time|
35
+ options[:to] = time
36
+ end
37
+
38
+ opts.on('-h', '--help', '顯示此訊息') do |v|
39
+ puts opts
40
+ exit 0
41
+ end
42
+ end
43
+
44
+ parser.parse!
45
+
46
+ if ARGV.empty?
47
+ puts parser.help
48
+ exit 0
49
+ end
50
+
51
+
52
+ begin
53
+ response = EasyCard.query(ARGV.first, from: options[:from], to: options[:to])
54
+ puts response.as(options[:format])
55
+ rescue EasyCard::Error => e
56
+ $stderr.puts e.message
57
+ end
@@ -0,0 +1,45 @@
1
+ require 'easycard/response'
2
+ require 'openssl'
3
+ require 'cgi'
4
+ require 'base64'
5
+ require 'net/http'
6
+
7
+ module EasyCard
8
+ KEY = 'EasyCardToKingay23456789'
9
+ IV = '01234567'
10
+ SALT = 'L0CalKing'
11
+ CONST = 8544
12
+
13
+ class Error < RuntimeError; end
14
+ class NotFound < Error; end
15
+
16
+ module_function
17
+
18
+ def query cart_number, from: Date.today-30, to: Date.today, format: nil
19
+ query_hash = {
20
+ cardID: card_id(cart_number),
21
+ begin: from.strftime('%Y-%m-%d'),
22
+ end: to.strftime('%Y-%m-%d'),
23
+ verify: verify,
24
+ ev: 1
25
+ }
26
+ uri = URI('https://wallet.easycard.com.tw/EasyWallet/QueryManager/V3/GetTXNThinDataInfo')
27
+ uri.query = URI.encode_www_form(query_hash)
28
+ Response.new(Net::HTTP.get(uri))
29
+ end
30
+
31
+ def card_id card_number
32
+ remainder = card_number.size % 16
33
+ data = card_number + "\x06"*(16 - remainder) if remainder != 0
34
+ cipher = OpenSSL::Cipher.new('DES3')
35
+ cipher.encrypt
36
+ cipher.key = KEY
37
+ cipher.iv = IV
38
+ Base64.encode64(cipher.update(data)).chop!
39
+ end
40
+
41
+ def verify time = Time.now
42
+ seed = time.month + time.day + time.hour
43
+ Digest::MD5.hexdigest("#{seed * CONST}#{SALT}").upcase!
44
+ end
45
+ end
@@ -0,0 +1,10 @@
1
+ module EasyCard
2
+ module ColorString
3
+ refine String do
4
+ def bold; "\033[1m#{self}\033[22m" end
5
+ def red; "\033[31m#{self}\033[0m" end
6
+ def green; "\033[32m#{self}\033[0m" end
7
+ def yellow; "\033[33m#{self}\033[0m" end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,66 @@
1
+ require 'easycard/color_string'
2
+ require 'json'
3
+ require 'yaml'
4
+ require 'forwardable'
5
+
6
+ module EasyCard
7
+ class Response
8
+ extend Forwardable
9
+
10
+ attr_reader :data, :raw_data, :parsed_data, :balance
11
+ def_delegators :@data, :to_json, :to_yaml, :to_a
12
+
13
+ using ColorString
14
+
15
+ def self.normalize_record record
16
+ type = case record[?T]
17
+ when ?D then :withdrawal
18
+ when ?U then :deposit
19
+ when '罰款' then :fine
20
+ when '查無交易資料' then raise EasyCard::NotFound, record[?T]
21
+ else raise EasyCard::Error, "未知類型: #{record[?T]}"
22
+ end
23
+ record[?L].gsub!('<BR>', ?-)
24
+ {type: type, datetime: record[?D], location: record[?L], balance: record[?A], amount: record[?Q]}
25
+ end
26
+
27
+ def self.type_text type
28
+ case type
29
+ when :withdrawal then '扣款'.red.bold
30
+ when :deposit then '儲值'.green.bold
31
+ when :fine then '罰款'.yellow.bold
32
+ end
33
+ end
34
+
35
+ def initialize raw_data
36
+ @raw_data = raw_data
37
+ @parsed_data = JSON.parse(@raw_data)
38
+ @balance = @parsed_data.pop[?B].to_i if @parsed_data.last[?B]
39
+ @data = @parsed_data.map do |record|
40
+ self.class.normalize_record(record)
41
+ end
42
+ end
43
+
44
+ def as format = nil
45
+ case format
46
+ when :json then to_json
47
+ when :yaml then to_yaml
48
+ when :table then to_s
49
+ else data
50
+ end
51
+ end
52
+
53
+ def to_s
54
+ ret = "%3s | %-17s | %s | %-3s | %-3s | %s\n" % %w[# 時間 種類 金額 餘額 地點]
55
+ ret << "#{?-*3} | #{?-*19} | #{?-*4} | #{?-*5} | #{?-*5} | #{?-*20}\n"
56
+ @data.each_with_index.map{|record, i| ret << line(i+1, record) << $/}
57
+ ret
58
+ end
59
+
60
+ def line id, record
61
+ type = self.class.type_text(record[:type])
62
+ '%3d | %19s | %s | %5s | %5s | %s' % [id, record[:datetime], type, record[:amount], record[:balance], record[:location]]
63
+ end
64
+
65
+ end
66
+ end
@@ -0,0 +1,3 @@
1
+ module EasyCard
2
+ VERSION = '1.0.0'
3
+ end
metadata ADDED
@@ -0,0 +1,65 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: easycard
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Jian Weihang
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-05-06 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ description: |
28
+ 台灣悠遊卡交易紀錄查詢工具,支援指令模式。
29
+ A search tool for Taiwan EasyCard, supporting CLI mode.
30
+ email: tonytonyjan@gmail.com
31
+ executables:
32
+ - easycard
33
+ extensions: []
34
+ extra_rdoc_files: []
35
+ files:
36
+ - bin/easycard
37
+ - lib/easycard.rb
38
+ - lib/easycard/color_string.rb
39
+ - lib/easycard/response.rb
40
+ - lib/easycard/version.rb
41
+ homepage: https://github.com/tonytonyjan/easycard
42
+ licenses:
43
+ - MIT
44
+ metadata: {}
45
+ post_install_message:
46
+ rdoc_options: []
47
+ require_paths:
48
+ - lib
49
+ required_ruby_version: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ required_rubygems_version: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: '0'
59
+ requirements: []
60
+ rubyforge_project:
61
+ rubygems_version: 2.4.6
62
+ signing_key:
63
+ specification_version: 4
64
+ summary: "台灣悠遊卡交易紀錄查詢工具/A search tool for Taiwan EasyCard"
65
+ test_files: []