easycard 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/easycard +57 -0
- data/lib/easycard.rb +45 -0
- data/lib/easycard/color_string.rb +10 -0
- data/lib/easycard/response.rb +66 -0
- data/lib/easycard/version.rb +3 -0
- metadata +65 -0
checksums.yaml
ADDED
@@ -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
|
data/bin/easycard
ADDED
@@ -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
|
data/lib/easycard.rb
ADDED
@@ -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,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
|
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: []
|