tcat 0.1.4 → 0.1.5
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 +4 -4
- data/.rubocop.yml +13 -1
- data/.tool-versions +1 -1
- data/README.md +89 -12
- data/Rakefile +3 -3
- data/lib/tcat/configuration.rb +1 -0
- data/lib/tcat/encryption_service.rb +47 -0
- data/lib/tcat/http_client.rb +60 -0
- data/lib/tcat/query.rb +26 -31
- data/lib/tcat/version.rb +1 -1
- data/lib/tcat.rb +1 -0
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 66a34a4165a334cea5fc3e1297f49265cff8cff2099aa8ea6ea0912d953798a1
|
4
|
+
data.tar.gz: 72cd8a878a16a17829ffbb0dfbe6588c04828ed1e72de0799f3fe66460b9451b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 96960dd54febe02718c8fb068206b3d1b0d23ca45ddb65b4097920cdf58d87b5bba346e2fce0bcc3ef11109b67d78c6a00d6ad84d8d1899358ee094e4245f863
|
7
|
+
data.tar.gz: d5a1a0ee708bb6179d62b8596af30137ecd0965f146bb6f009a3ac9b834242fe61046e553f6620a457e215c99f13375306c13bddbb7b011bf217614b1683f961
|
data/.rubocop.yml
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
AllCops:
|
2
2
|
TargetRubyVersion: 2.7
|
3
|
+
Exclude:
|
4
|
+
- 'bin/*'
|
3
5
|
|
4
6
|
# Style/StringLiterals:
|
5
7
|
# Enabled: true
|
@@ -9,5 +11,15 @@ AllCops:
|
|
9
11
|
# Enabled: true
|
10
12
|
# EnforcedStyle: double_quotes
|
11
13
|
|
14
|
+
Metrics/AbcSize:
|
15
|
+
Exclude:
|
16
|
+
- 'lib/tcat/query.rb'
|
17
|
+
|
18
|
+
Metrics/MethodLength:
|
19
|
+
Exclude:
|
20
|
+
- 'lib/tcat/query.rb'
|
21
|
+
|
12
22
|
Layout/LineLength:
|
13
|
-
|
23
|
+
Exclude:
|
24
|
+
- 'lib/tcat/query.rb'
|
25
|
+
- tcat.gemspec
|
data/.tool-versions
CHANGED
@@ -1 +1 @@
|
|
1
|
-
ruby 3.3.
|
1
|
+
ruby 3.3.6
|
data/README.md
CHANGED
@@ -1,35 +1,112 @@
|
|
1
1
|
# Tcat
|
2
2
|
|
3
|
-
|
3
|
+
A Ruby gem for tracking T-Cat (Taiwan Pelican Express) shipment status. Provides a simple and easy-to-use API interface.
|
4
4
|
|
5
|
-
|
5
|
+
## Features
|
6
|
+
|
7
|
+
- Track shipment status
|
8
|
+
- Secure encrypted requests
|
9
|
+
- Simple API interface
|
10
|
+
- Non-blocking HTTP requests
|
11
|
+
- Comprehensive error handling
|
6
12
|
|
7
13
|
## Installation
|
8
14
|
|
9
|
-
|
15
|
+
Add this line to your application's Gemfile:
|
16
|
+
|
17
|
+
```ruby
|
18
|
+
gem 'tcat'
|
19
|
+
```
|
20
|
+
|
21
|
+
And then execute:
|
10
22
|
|
11
|
-
|
23
|
+
```bash
|
24
|
+
$ bundle install
|
25
|
+
```
|
12
26
|
|
13
|
-
|
27
|
+
Or install it yourself as:
|
14
28
|
|
15
|
-
|
29
|
+
```bash
|
30
|
+
$ gem install tcat
|
31
|
+
```
|
16
32
|
|
17
|
-
|
33
|
+
## Configuration
|
34
|
+
|
35
|
+
Configure the gem before use:
|
36
|
+
|
37
|
+
```ruby
|
38
|
+
Tcat.configure do |config|
|
39
|
+
config.secret_string = 'your_secret_string'
|
40
|
+
config.secret_key = 'your_secret_key'
|
41
|
+
end
|
42
|
+
```
|
18
43
|
|
19
44
|
## Usage
|
20
45
|
|
21
|
-
|
46
|
+
### Basic Usage
|
47
|
+
|
48
|
+
```ruby
|
49
|
+
# Create a query instance
|
50
|
+
query = Tcat::Query.new('your_tracking_number')
|
51
|
+
|
52
|
+
# Get shipment status
|
53
|
+
status = query.perform
|
54
|
+
|
55
|
+
# Status will be one of the following:
|
56
|
+
# :done - Successfully delivered
|
57
|
+
# :delivering - Out for delivery
|
58
|
+
# :collected - Package collected
|
59
|
+
# :in_transit - In transit
|
60
|
+
# :unknown - Unknown status
|
61
|
+
```
|
62
|
+
|
63
|
+
### Error Handling
|
64
|
+
|
65
|
+
```ruby
|
66
|
+
begin
|
67
|
+
status = query.perform
|
68
|
+
rescue Tcat::HttpClient::RequestError => e
|
69
|
+
puts "Network request error: #{e.message}"
|
70
|
+
rescue Tcat::EncryptionService::EncryptionError => e
|
71
|
+
puts "Encryption error: #{e.message}"
|
72
|
+
rescue StandardError => e
|
73
|
+
puts "Other error: #{e.message}"
|
74
|
+
end
|
75
|
+
```
|
22
76
|
|
23
77
|
## Development
|
24
78
|
|
25
|
-
|
79
|
+
1. Fork the project
|
80
|
+
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
|
81
|
+
3. Commit your changes (`git commit -am 'Add some amazing feature'`)
|
82
|
+
4. Push to the branch (`git push origin feature/amazing-feature`)
|
83
|
+
5. Create a Pull Request
|
84
|
+
|
85
|
+
### Running Tests
|
26
86
|
|
27
|
-
|
87
|
+
```bash
|
88
|
+
$ bundle exec rake spec
|
89
|
+
```
|
90
|
+
|
91
|
+
### Local Installation
|
92
|
+
|
93
|
+
```bash
|
94
|
+
$ bundle exec rake install
|
95
|
+
```
|
28
96
|
|
29
97
|
## Contributing
|
30
98
|
|
31
|
-
Bug reports and pull requests are welcome
|
99
|
+
Bug reports and pull requests are welcome.
|
32
100
|
|
33
101
|
## License
|
34
102
|
|
35
|
-
|
103
|
+
This gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
104
|
+
|
105
|
+
## Changelog
|
106
|
+
|
107
|
+
### 0.1.5 (2024-01-11)
|
108
|
+
|
109
|
+
- Refactored HTTP request handling with new HttpClient class
|
110
|
+
- Refactored encryption logic with new EncryptionService class
|
111
|
+
- Improved error handling
|
112
|
+
- Updated documentation
|
data/Rakefile
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require 'bundler/gem_tasks'
|
4
|
+
require 'rspec/core/rake_task'
|
5
5
|
|
6
6
|
RSpec::Core::RakeTask.new(:spec)
|
7
7
|
|
8
|
-
require
|
8
|
+
require 'rubocop/rake_task'
|
9
9
|
|
10
10
|
RuboCop::RakeTask.new
|
11
11
|
|
data/lib/tcat/configuration.rb
CHANGED
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'openssl'
|
4
|
+
require 'base64'
|
5
|
+
|
6
|
+
module Tcat
|
7
|
+
class EncryptionService
|
8
|
+
class EncryptionError < StandardError; end
|
9
|
+
|
10
|
+
CIPHER_ALGORITHM = 'AES-128-ECB'
|
11
|
+
|
12
|
+
def initialize(secret_key)
|
13
|
+
@secret_key = secret_key
|
14
|
+
validate_key!
|
15
|
+
end
|
16
|
+
|
17
|
+
def encrypt(message)
|
18
|
+
cipher = setup_cipher
|
19
|
+
padded_message = pad_message(message, cipher.block_size)
|
20
|
+
encrypted = cipher.update(padded_message)
|
21
|
+
Base64.strict_encode64(encrypted)
|
22
|
+
rescue OpenSSL::Cipher::CipherError => e
|
23
|
+
raise EncryptionError, "Encryption failed: #{e.message}"
|
24
|
+
rescue StandardError => e
|
25
|
+
raise EncryptionError, "Unexpected error during encryption: #{e.message}"
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def validate_key!
|
31
|
+
raise EncryptionError, 'Secret key cannot be nil or empty' if @secret_key.nil? || @secret_key.empty?
|
32
|
+
end
|
33
|
+
|
34
|
+
def setup_cipher
|
35
|
+
cipher = OpenSSL::Cipher.new(CIPHER_ALGORITHM)
|
36
|
+
cipher.encrypt
|
37
|
+
cipher.key = @secret_key
|
38
|
+
cipher
|
39
|
+
end
|
40
|
+
|
41
|
+
def pad_message(message, block_size)
|
42
|
+
padding_size = block_size - (message.length % block_size)
|
43
|
+
padding = padding_size.chr * padding_size
|
44
|
+
message + padding
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'net/http'
|
4
|
+
|
5
|
+
module Tcat
|
6
|
+
class HttpClient
|
7
|
+
DEFAULT_HEADERS = {
|
8
|
+
'User-Agent' => 'BlackCat/2.49 (iPhone; iOS 17.6.1; Scale/3.00)',
|
9
|
+
'Accept-Language' => 'zh-Hant-TW;q=1, en-TW;q=0.9',
|
10
|
+
'Cookie' => 'citrix_ns_id=AAE7go4rZDusCRcEAAAAADuVBLGLdqo1FolMO02r_jOEnjNmr4u9sIYwtllmmfsOOw==KJArZA==AQJLxyeLYOXPo3Uzfh_rWrr_NcU=; ASP.NET_SessionId=p4v1z4nijrynbortgf4iqml4'
|
11
|
+
}.freeze
|
12
|
+
|
13
|
+
API_ENDPOINT = 'https://www.t-cat.com.tw/iPhone/TCatApp.aspx'
|
14
|
+
API_VERSION = '24.26'
|
15
|
+
|
16
|
+
class RequestError < StandardError; end
|
17
|
+
|
18
|
+
def initialize(timeout: 30)
|
19
|
+
@timeout = timeout
|
20
|
+
end
|
21
|
+
|
22
|
+
def post(params)
|
23
|
+
uri = URI("#{API_ENDPOINT}?ver=#{API_VERSION}")
|
24
|
+
http = setup_http_client(uri)
|
25
|
+
|
26
|
+
request = Net::HTTP::Post.new(uri)
|
27
|
+
DEFAULT_HEADERS.each { |key, value| request[key] = value }
|
28
|
+
request.body = URI.encode_www_form(params)
|
29
|
+
|
30
|
+
handle_response(http.request(request))
|
31
|
+
rescue Net::ReadTimeout, Net::OpenTimeout => e
|
32
|
+
raise RequestError, "Request timeout: #{e.message}"
|
33
|
+
rescue SocketError, Net::HTTPError => e
|
34
|
+
raise RequestError, "Network error: #{e.message}"
|
35
|
+
rescue StandardError => e
|
36
|
+
raise RequestError, "Unexpected error: #{e.message}"
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def setup_http_client(uri)
|
42
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
43
|
+
http.use_ssl = true
|
44
|
+
http.read_timeout = @timeout
|
45
|
+
http.open_timeout = @timeout
|
46
|
+
http
|
47
|
+
end
|
48
|
+
|
49
|
+
def handle_response(response)
|
50
|
+
case response
|
51
|
+
when Net::HTTPSuccess
|
52
|
+
response.body
|
53
|
+
when Net::HTTPRedirection
|
54
|
+
raise RequestError, "Unexpected redirect to #{response['location']}"
|
55
|
+
else
|
56
|
+
raise RequestError, "HTTP #{response.code}: #{response.message}"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
data/lib/tcat/query.rb
CHANGED
@@ -3,8 +3,11 @@
|
|
3
3
|
require 'net/http'
|
4
4
|
require 'base64'
|
5
5
|
require 'ox'
|
6
|
+
require_relative 'http_client'
|
7
|
+
require_relative 'encryption_service'
|
6
8
|
|
7
9
|
module Tcat
|
10
|
+
# Query class handles making requests to the Tcat system
|
8
11
|
class Query
|
9
12
|
def initialize(tracking_number)
|
10
13
|
@secret_string = Tcat.configuration.secret_string
|
@@ -12,27 +15,23 @@ module Tcat
|
|
12
15
|
validate_secrets!
|
13
16
|
|
14
17
|
@tracking_number = tracking_number
|
18
|
+
@http_client = HttpClient.new
|
19
|
+
@encryption_service = EncryptionService.new(@secret_key)
|
15
20
|
end
|
16
21
|
|
17
22
|
def perform
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
http = Net::HTTP.new(uri.host, uri.port)
|
24
|
-
http.use_ssl = true
|
25
|
-
body = URI.encode_www_form(data)
|
26
|
-
response = Net::HTTP.post(uri, body, headers)
|
27
|
-
|
28
|
-
parse_response(response.body) if response.code == '200'
|
23
|
+
response_body = @http_client.post(data)
|
24
|
+
parse_response(response_body) if response_body
|
25
|
+
rescue HttpClient::RequestError => e
|
26
|
+
# Log error or handle it appropriately
|
27
|
+
nil
|
29
28
|
end
|
30
29
|
|
31
30
|
private
|
32
31
|
|
33
32
|
def validate_secrets!
|
34
|
-
raise ArgumentError,
|
35
|
-
raise ArgumentError,
|
33
|
+
raise ArgumentError, 'SECRET_STRING must be configured' if @secret_string.nil? || @secret_string.empty?
|
34
|
+
raise ArgumentError, 'SECRET_KEY must be configured' if @secret_key.nil? || @secret_key.empty?
|
36
35
|
end
|
37
36
|
|
38
37
|
def data
|
@@ -40,14 +39,13 @@ module Tcat
|
|
40
39
|
ConsignmentNo: @tracking_number,
|
41
40
|
f: 5,
|
42
41
|
isForeign: 'N',
|
43
|
-
secret:
|
42
|
+
secret: generate_secret,
|
44
43
|
SVC: 'TCATAPP'
|
45
44
|
}
|
46
45
|
end
|
47
46
|
|
48
47
|
def date_string
|
49
|
-
|
50
|
-
date.strftime('%Y%m%d%H%M%S')
|
48
|
+
Time.now.utc.strftime('%Y%m%d%H%M%S')
|
51
49
|
end
|
52
50
|
|
53
51
|
def random
|
@@ -58,18 +56,11 @@ module Tcat
|
|
58
56
|
random + @secret_string + date_string
|
59
57
|
end
|
60
58
|
|
61
|
-
def
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
block_size = cipher.block_size
|
67
|
-
padding_size = block_size - (source_string.length % block_size)
|
68
|
-
padding = padding_size.chr * padding_size
|
69
|
-
padded_message = source_string + padding
|
70
|
-
|
71
|
-
encrypted = cipher.update(padded_message)
|
72
|
-
Base64.strict_encode64(encrypted)
|
59
|
+
def generate_secret
|
60
|
+
@encryption_service.encrypt(source_string)
|
61
|
+
rescue EncryptionService::EncryptionError => e
|
62
|
+
# Log error or handle it appropriately
|
63
|
+
raise "Failed to generate secret: #{e.message}"
|
73
64
|
end
|
74
65
|
|
75
66
|
def parse_response(xml)
|
@@ -89,12 +80,16 @@ module Tcat
|
|
89
80
|
end
|
90
81
|
|
91
82
|
def parse_status_message(statuses)
|
92
|
-
if '順利送達'
|
83
|
+
if statuses.include?('順利送達')
|
93
84
|
:done
|
94
|
-
elsif '配送中'
|
85
|
+
elsif statuses.include?('配送中')
|
95
86
|
:delivering
|
96
|
-
elsif '已集貨'
|
87
|
+
elsif statuses.include?('已集貨')
|
97
88
|
:collected
|
89
|
+
elsif statuses.include?('轉運中')
|
90
|
+
:in_transit
|
91
|
+
else
|
92
|
+
:unknown
|
98
93
|
end
|
99
94
|
end
|
100
95
|
end
|
data/lib/tcat/version.rb
CHANGED
data/lib/tcat.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tcat
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Zac
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-11-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ox
|
@@ -55,6 +55,8 @@ files:
|
|
55
55
|
- Rakefile
|
56
56
|
- lib/tcat.rb
|
57
57
|
- lib/tcat/configuration.rb
|
58
|
+
- lib/tcat/encryption_service.rb
|
59
|
+
- lib/tcat/http_client.rb
|
58
60
|
- lib/tcat/query.rb
|
59
61
|
- lib/tcat/version.rb
|
60
62
|
- sig/tcat.rbs
|
@@ -78,7 +80,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
78
80
|
- !ruby/object:Gem::Version
|
79
81
|
version: '0'
|
80
82
|
requirements: []
|
81
|
-
rubygems_version: 3.5.
|
83
|
+
rubygems_version: 3.5.22
|
82
84
|
signing_key:
|
83
85
|
specification_version: 4
|
84
86
|
summary: A Ruby gem for tracking packages using the Tcat system.
|