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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 649a0ce80d0bd52cde6c0cd171b869ab557d7b085467d2e0e950a652456a29a9
4
- data.tar.gz: 2519e582fd44fc108a05b1c69a0bbcc80dba5620f6acb5f8baf21a96c8289b38
3
+ metadata.gz: 66a34a4165a334cea5fc3e1297f49265cff8cff2099aa8ea6ea0912d953798a1
4
+ data.tar.gz: 72cd8a878a16a17829ffbb0dfbe6588c04828ed1e72de0799f3fe66460b9451b
5
5
  SHA512:
6
- metadata.gz: 5040e5ed15e2bef7e946a7b237f50464ecf52fbe0dd50fec8aba99c9ee78e9d1c43de2d78840a84921876c713b1af46cfbe90d2d2a75183619a5d8839466620b
7
- data.tar.gz: fdcacbc2c3bdd76754cf73083bd59dd8791501f22404966992bc827a78d90ddb2e3f8f8819b2bc1611175f451eb9c9e8ec8b55a6136826e8ddc7026f1461648a
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
- Max: 120
23
+ Exclude:
24
+ - 'lib/tcat/query.rb'
25
+ - tcat.gemspec
data/.tool-versions CHANGED
@@ -1 +1 @@
1
- ruby 3.3.3
1
+ ruby 3.3.6
data/README.md CHANGED
@@ -1,35 +1,112 @@
1
1
  # Tcat
2
2
 
3
- TODO: Delete this and the text below, and describe your gem
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
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/tcat`. To experiment with that code, run `bin/console` for an interactive prompt.
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
- TODO: Replace `UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG` with your gem name right after releasing it to RubyGems.org. Please do not do it earlier due to security reasons. Alternatively, replace this section with instructions to install your gem from git if you don't plan to release to RubyGems.org.
15
+ Add this line to your application's Gemfile:
16
+
17
+ ```ruby
18
+ gem 'tcat'
19
+ ```
20
+
21
+ And then execute:
10
22
 
11
- Install the gem and add to the application's Gemfile by executing:
23
+ ```bash
24
+ $ bundle install
25
+ ```
12
26
 
13
- $ bundle add UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG
27
+ Or install it yourself as:
14
28
 
15
- If bundler is not being used to manage dependencies, install the gem by executing:
29
+ ```bash
30
+ $ gem install tcat
31
+ ```
16
32
 
17
- $ gem install UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG
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
- TODO: Write usage instructions here
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
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
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
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
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 on GitHub at https://github.com/[USERNAME]/tcat.
99
+ Bug reports and pull requests are welcome.
32
100
 
33
101
  ## License
34
102
 
35
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
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 "bundler/gem_tasks"
4
- require "rspec/core/rake_task"
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
5
 
6
6
  RSpec::Core::RakeTask.new(:spec)
7
7
 
8
- require "rubocop/rake_task"
8
+ require 'rubocop/rake_task'
9
9
 
10
10
  RuboCop::RakeTask.new
11
11
 
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Tcat
4
+ # Configuration class handles settings for Tcat
4
5
  class Configuration
5
6
  attr_accessor :secret_string, :secret_key
6
7
 
@@ -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
- cookies = 'citrix_ns_id=AAE7go4rZDusCRcEAAAAADuVBLGLdqo1FolMO02r_jOEnjNmr4u9sIYwtllmmfsOOw==KJArZA==AQJLxyeLYOXPo3Uzfh_rWrr_NcU=; ASP.NET_SessionId=p4v1z4nijrynbortgf4iqml4'
19
- ua = 'BlackCat/2.49 (iPhone; iOS 17.6.1; Scale/3.00)'
20
- headers = { 'User-Agent': ua, 'Accept-Language': 'zh-Hant-TW;q=1, en-TW;q=0.9', Cookie: cookies }
21
- uri = URI('https://www.t-cat.com.tw/iPhone/TCatApp.aspx?ver=24.26')
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, "SECRET_STRING must be configured" if @secret_string.nil? || @secret_string.empty?
35
- raise ArgumentError, "SECRET_KEY must be configured" if @secret_key.nil? || @secret_key.empty?
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: secret,
42
+ secret: generate_secret,
44
43
  SVC: 'TCATAPP'
45
44
  }
46
45
  end
47
46
 
48
47
  def date_string
49
- date = Time.now.utc
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 secret
62
- cipher = OpenSSL::Cipher.new('AES-128-ECB')
63
- cipher.encrypt
64
- cipher.key = @secret_key
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 '順利送達'.in?(statuses)
83
+ if statuses.include?('順利送達')
93
84
  :done
94
- elsif '配送中'.in?(statuses)
85
+ elsif statuses.include?('配送中')
95
86
  :delivering
96
- elsif '已集貨'.in?(statuses)
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Tcat
4
- VERSION = '0.1.4'
4
+ VERSION = '0.1.5'
5
5
  end
data/lib/tcat.rb CHANGED
@@ -4,6 +4,7 @@ require 'tcat/configuration'
4
4
  require_relative 'tcat/version'
5
5
  require_relative 'tcat/query'
6
6
 
7
+ # Tcat module provides functionality for tracking packages
7
8
  module Tcat
8
9
  class << self
9
10
  attr_writer :configuration
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
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-09-02 00:00:00.000000000 Z
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.11
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.