Tcat

A Ruby gem for tracking T-Cat (Taiwan Pelican Express) shipment status. Provides a simple and easy-to-use API interface.

โœจ Two Ways to Use Tcat

1. Ruby Gem (This Repository)

Perfect for Ruby/Rails applications with direct integration.

2. Cloudflare Worker API

Looking for a serverless solution? Check out worker/README.md

Benefits of the Worker version:

  • ๐ŸŒ Global edge deployment for faster queries worldwide
  • ๐Ÿ”’ Secure secret storage in environment variables
  • ๐ŸŒ HTTP API accessible from any platform (JavaScript, Python, cURL, etc.)
  • ๐Ÿ’ฐ Cost-effective: 100K free requests/day
  • ๐Ÿ“ฑ Perfect for frontend apps, mobile apps, or microservices

Features

  • ๐Ÿ“ฆ Track shipment status
  • ๐Ÿ” Secure encrypted requests
  • ๐ŸŒ Simple API interface
  • โšก Non-blocking HTTP requests
  • ๐Ÿ›ก๏ธ Comprehensive error handling

Installation

Add this line to your application's Gemfile:

gem 'tcat'

And then execute:

$ bundle install

Or install it yourself as:

$ gem install tcat

Usage

There are two ways to use this gem:

  1. Direct API Access - Query T-Cat API directly from your Ruby application
  2. Cloudflare Worker - Query through a Cloudflare Worker proxy (recommended for production)

Option 1: Direct API Access

Configure the gem with your T-Cat API credentials:

Tcat.configure do |config|
  config.secret_string = 'your_secret_string'
  config.secret_key = 'your_secret_key'
end

# Two equivalent shapes โ€” pick whichever fits your code:

# A) tracking number bound at construction (one-shot)
query = Tcat::Query.new('your_tracking_number')
status = query.status_code

# B) tracking number per call (reuse one client for many lookups,
#    matches Tcat::WorkerClient's shape)
query = Tcat::Query.new
status = query.status_code('your_tracking_number')
# Returns one of the following:
# :done           - Successfully delivered
# :delivering     - Out for delivery
# :collected      - Package collected
# :in_transit     - In transit
# :returned       - Return completed
# :held           - Held at post office
# :rescheduled    - Delivery time rescheduled
# :forwarding     - Being forwarded
# :investigation  - Under investigation
# :rejected       - Delivery rejected
# :returning      - In return process
# :store_delivery - At convenience store for pickup
# :unknown        - Unknown status

# Get latest status details
latest = query.latest_status
if latest
  puts "Status: #{latest.status}"         # e.g. "Successfully delivered"
  puts "Status code: #{latest.status_code}" # e.g. :done
  puts "Time: #{latest.time}"           # Time object
  puts "Office: #{latest.office}"       # e.g. "Tainan office"
  puts "Last update: #{latest.last_update}" # Time object
end

# Get full shipment history
history = query.history
history.each do |item|
  puts "Status: #{item.status}"
  puts "Time: #{item.time}"
  puts "Office: #{item.office}"
  puts "---"
end

The Worker approach is recommended for production because:

  • โœ… Secrets are stored securely in Cloudflare, not in your application
  • โœ… Faster response times via Cloudflare's edge network
  • โœ… No need to manage encryption in your Ruby app
  • โœ… Can be used by any language/platform (not just Ruby)

First, deploy the Cloudflare Worker (see worker/README.md for deployment instructions).

Then use the WorkerClient in your Ruby application. You can pass the Worker URL (and optional Bearer token) directly, or configure them globally via Tcat.configure:

# Option A: explicit per-instance arguments
client = Tcat::WorkerClient.new(
  'https://your-worker.workers.dev',
  token: ENV['TCAT_WORKER_TOKEN']  # only needed when AUTH_TOKEN is set on the Worker
)

# Option B: global configuration, then construct without arguments
Tcat.configure do |config|
  config.worker_url   = 'https://your-worker.workers.dev'
  config.worker_token = ENV['TCAT_WORKER_TOKEN']
end
client = Tcat::WorkerClient.new

# Explicit args always override configuration:
override = Tcat::WorkerClient.new('https://other.workers.dev', token: 'other-token')

# Get shipment status (same API as Query)
status = client.status_code('your_tracking_number')
# => :delivering

# Get latest status details
latest = client.latest_status('your_tracking_number')
if latest
  puts "Status: #{latest.status}"         # e.g. "้…้€ไธญ"
  puts "Status code: #{latest.status_code}" # e.g. :delivering
  puts "Time: #{latest.time}"             # Time object
  puts "Office: #{latest.office}"         # e.g. "ๅฐๅŒ—็‡Ÿๆฅญๆ‰€"
end

# Get full shipment history
history = client.history('your_tracking_number')
history.each do |item|
  puts "Status: #{item.status}"
  puts "Time: #{item.time}"
  puts "Office: #{item.office}"
  puts "---"
end

# Check if Worker is healthy
if client.healthy?
  puts "Worker is operational"
end

Custom timeout:

# Default timeout is 30 seconds
client = Tcat::WorkerClient.new('https://your-worker.workers.dev', timeout: 60)

Error handling:

begin
  status = client.status_code('tracking_number')
rescue Tcat::WorkerClient::NetworkError => e
  puts "Network error: #{e.message}"
rescue Tcat::WorkerClient::APIError => e
  puts "API error: #{e.message}"
end

Status Code Explanation

  • :done - Successfully delivered
  • :delivering - Out for delivery
  • :collected - Package collected
  • :in_transit - In transit
  • :returned - Return completed
  • :held - Package is being held at post office
  • :rescheduled - Delivery time rescheduled
  • :forwarding - Package is being forwarded
  • :investigation - Package is under investigation (e.g., address change, rejection)
  • :rejected - Delivery was rejected
  • :returning - Package is in return process
  • :store_delivery - Handed over to a convenience store, awaiting recipient pickup
  • :unknown - Unknown status

Cloudflare Worker

This repository includes a Cloudflare Worker implementation that provides an HTTP API for T-Cat tracking.

Features

  • ๐ŸŒ Deploy globally on Cloudflare's edge network
  • ๐Ÿ”’ Securely store API credentials as Worker secrets
  • ๐Ÿš€ Fast response times from edge locations
  • ๐ŸŒ CORS enabled for frontend applications
  • ๐Ÿ“ฑ Works with any programming language

Quick Start

cd worker
npm install
npm run dev  # Start local development server

For full deployment instructions, see worker/README.md and worker/DEPLOYMENT.md.

Comparison: Direct API vs Worker

Feature Direct API (Query) Cloudflare Worker (WorkerClient)
Setup complexity Low (just gem install) Medium (requires Worker deployment)
Security Secrets in your app Secrets in Cloudflare
Performance Direct to T-Cat API Via Cloudflare edge
Multi-platform Ruby only Any language/platform
Cost Free Free tier available
Best for Simple scripts, internal tools Production apps, public APIs

Development

Running Tests

# Run all tests
$ bundle exec rake spec

# Run specific test file
$ bundle exec rspec spec/tcat/worker_client_spec.rb

Local Installation

$ bundle exec rake install

Contributing

Bug reports and pull requests are welcome.

  1. Fork the project
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -am 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Create a Pull Request

License

This gem is available as open source under the terms of the MIT License.

Changelog

0.4.0

  • Tcat::Query#status_code, #history, #latest_status now accept an optional tracking-number argument, mirroring Tcat::WorkerClient's shape
  • Tcat::Query.new may be called without a tracking number for stateless reuse: Tcat::Query.new.status_code('1234567890')
  • Existing one-shot construction (Tcat::Query.new(tn).status_code) is still supported

0.3.5

  • Added optional worker_url and worker_token to Tcat.configure; explicit Tcat::WorkerClient.new(url, token:) arguments still take precedence
  • Tcat::WorkerClient.new now accepts no arguments when worker_url is set in configuration

0.3.4

  • Bundled the 0.3.3 fixes for release on RubyGems

0.3.3

  • Worker now subtracts the Taiwan UTC+8 offset when emitting ISO timestamps (previously every event was reported 8 hours later than reality)
  • Worker forwards only the name=value portion of Set-Cookie to the upstream Cookie: header (RFC 6265)
  • Worker rejects /query with HTTP 401 when AUTH_TOKEN is not configured (fail-closed)
  • 5xx responses no longer leak error.message; details are written to console.error (visible via wrangler tail)

0.3.2

  • Added optional Bearer token auth to the Cloudflare Worker (AUTH_TOKEN secret)
  • Tcat::WorkerClient accepts a token: keyword arg that is sent as Authorization: Bearer <token>
  • Worker compares tokens in constant time; CORS allow-headers gain Authorization

0.3.1

  • Tightened tcat.gemspec so the published gem no longer bundles the worker/ subproject or dev-only configs

0.3.0

  • Added Tcat::WorkerClient to query a Cloudflare Worker proxy instead of T-Cat directly
  • Added new status mapping: :store_delivery for ่ฝ‰ไบค่ถ…ๅ•†้…้” (handed over to convenience store)
  • Added the worker/ Cloudflare Worker subproject (separate from the gem release)

0.2.2

  • Fixed status parsing to handle HTML tags in API responses

0.2.1

  • Updated base64 gem dependency to ~> 0.3
  • Code formatting improvements

0.2.0

  • Fixed SSL certificate verification issues with T-Cat API
  • Added session initialization to obtain cookies before queries
  • Added cookie management for maintaining session state
  • Added new status code mappings: :rescheduled (ๅฆ็ด„ๆ™‚้–“) and :held (ๆšซ็ฝฎ็‡Ÿๆฅญๆ‰€)
  • Removed Rails.logger dependency for better standalone gem compatibility
  • Added base64 gem dependency for Ruby 3.4+ compatibility
  • Updated HTTP headers to match latest BlackCat iOS app (version 3)
  • Improved error handling with better debug output

0.1.9

  • Updated User-Agent and API version

0.1.7

  • Improved method naming, removed get_ prefix
  • Added latest_status method to get latest status details
  • Added history method to get full shipment history
  • Used DeliveryItem struct to provide more complete shipment information
  • Improved error handling and logging
  • Supported UTF-8 encoding

0.1.6

  • Refactored query parsing method
  • Improved error handling
  • Added test coverage

0.1.5

  • Refactored HTTP request handling with new HttpClient class
  • Refactored encryption logic with new EncryptionService class
  • Improved error handling
  • Updated documentation