606 lines
18 KiB
PHP
606 lines
18 KiB
PHP
<?php
|
|
/**
|
|
* Spacetraders API Client Library
|
|
*
|
|
* This library provides a simple interface to interact with the Spacetraders API,
|
|
* allowing you to manage your agents, ships, and other resources in the game.
|
|
* It includes methods for authentication, making API requests, and handling responses,
|
|
* making it easier to integrate Spacetraders into your applications or scripts.
|
|
*
|
|
* @category API
|
|
* @package SpacetradersAPI
|
|
* @author Keith Solomon <keith@keithsolomon.net>
|
|
* @license MIT License
|
|
* @version GIT: <git_id>
|
|
* @link https://git.keithsolomon.net/keith/Spacetraders
|
|
*/
|
|
|
|
require_once __DIR__ . '/spacetraders-api-exception.php';
|
|
require_once __DIR__ . '/spacetraders-storage.php';
|
|
|
|
/**
|
|
* Spacetraders API Client
|
|
*
|
|
* Main client class for interacting with the Spacetraders API. Provides methods
|
|
* for authentication, managing agents, ships, contracts, systems, and performing
|
|
* various game actions like navigation, trading, and resource extraction.
|
|
*
|
|
* @package SpacetradersAPI
|
|
*/
|
|
class SpacetradersApi {
|
|
/**
|
|
* The base URL for the Spacetraders API.
|
|
*
|
|
* @var string
|
|
*/
|
|
private string $baseUrl;
|
|
|
|
/**
|
|
* The authentication token for API requests.
|
|
*
|
|
* @var string|null
|
|
*/
|
|
private ?string $token;
|
|
|
|
/**
|
|
* The timeout for HTTP requests in seconds.
|
|
*
|
|
* @var int
|
|
*/
|
|
private int $timeout;
|
|
/**
|
|
* Optional SQLite storage for settings and cache.
|
|
*
|
|
* @var SpacetradersStorage|null
|
|
*/
|
|
private ?SpacetradersStorage $storage;
|
|
|
|
/**
|
|
* Default cache lifetime in seconds.
|
|
*
|
|
* @var int
|
|
*/
|
|
private int $cacheTtl;
|
|
|
|
/**
|
|
* Constructor for SpacetradersApi.
|
|
*
|
|
* @param string|null $token The authentication token for API requests ( default: null ).
|
|
* @param string $baseUrl The base URL for the Spacetraders API ( default: 'https://api.spacetraders.io/v2' ).
|
|
* @param int $timeout The timeout for HTTP requests in seconds ( default: 30 ).
|
|
* @param SpacetradersStorage|null $storage Optional SQLite storage object for settings and caching.
|
|
* @param int $cacheTtl API cache lifetime in seconds (default: 600).
|
|
*/
|
|
public function __construct(
|
|
?string $token = null,
|
|
string $baseUrl = 'https://api.spacetraders.io/v2',
|
|
int $timeout = 30,
|
|
?SpacetradersStorage $storage = null,
|
|
int $cacheTtl = 600
|
|
) {
|
|
$this->baseUrl = rtrim( $baseUrl, '/' );
|
|
$this->token = $token;
|
|
$this->timeout = $timeout;
|
|
$this->storage = $storage;
|
|
$this->cacheTtl = max( 1, $cacheTtl );
|
|
}
|
|
|
|
/**
|
|
* Set the authentication token for API requests.
|
|
*
|
|
* @param string $token The authentication token to set.
|
|
*
|
|
* @return void
|
|
*/
|
|
public function setToken( string $token ): void {
|
|
$this->token = trim( $token );
|
|
}
|
|
|
|
/**
|
|
* Get the current authentication token.
|
|
*
|
|
* @return string|null The authentication token or null if not set.
|
|
*/
|
|
public function getToken(): ?string {
|
|
return $this->token;
|
|
}
|
|
|
|
/**
|
|
* Attach storage for API caching and persisted settings.
|
|
*
|
|
* @param SpacetradersStorage|null $storage Storage handler.
|
|
*
|
|
* @return void
|
|
*/
|
|
public function setStorage( ?SpacetradersStorage $storage ): void {
|
|
$this->storage = $storage;
|
|
}
|
|
|
|
/**
|
|
* Set cache lifetime for cached API calls in seconds.
|
|
*
|
|
* @param int $cacheTtl Cache lifetime in seconds.
|
|
*
|
|
* @return void
|
|
*/
|
|
public function setCacheTtl( int $cacheTtl ): void {
|
|
$this->cacheTtl = max( 1, $cacheTtl );
|
|
}
|
|
|
|
/**
|
|
* Make an HTTP request to the Spacetraders API.
|
|
*
|
|
* @param string $method The HTTP method (GET, POST, etc.).
|
|
* @param string $endpoint The API endpoint to call.
|
|
* @param array<string,mixed> $payload The request payload data.
|
|
* @param array<string,mixed> $query The query parameters.
|
|
*
|
|
* @return array<string,mixed>
|
|
* @throws SpacetradersApiException When cURL fails to initialize, JSON encoding fails, network errors occur, or API returns an error response.
|
|
*/
|
|
public function request(
|
|
string $method,
|
|
string $endpoint,
|
|
array $payload = array(),
|
|
array $query = array()
|
|
): array {
|
|
$url = $this->_buildUrl( $endpoint, $query );
|
|
$method = strtoupper( $method );
|
|
$cacheKey = $this->_buildCacheKey( $method, $url, $payload );
|
|
|
|
if ($method === 'GET' && $this->storage !== null ) {
|
|
$cachedResponse = $this->storage->getCache( $cacheKey );
|
|
if (is_array( $cachedResponse ) ) {
|
|
return $cachedResponse;
|
|
}
|
|
}
|
|
|
|
$ch = curl_init( $url );
|
|
|
|
if ($ch === false ) {
|
|
throw new SpacetradersApiException( 'Unable to initialize cURL.' );
|
|
}
|
|
|
|
$headers = array( 'Accept: application/json' );
|
|
$jsonPayload = null;
|
|
|
|
if (! empty( $this->token ) ) {
|
|
$headers[] = 'Authorization: Bearer ' . $this->token;
|
|
}
|
|
|
|
if (! empty( $payload ) ) {
|
|
$jsonPayload = json_encode( $payload );
|
|
if ($jsonPayload === false ) {
|
|
throw new SpacetradersApiException( 'Unable to encode request payload as JSON.' );
|
|
}
|
|
|
|
$headers[] = 'Content-Type: application/json';
|
|
}
|
|
|
|
curl_setopt_array(
|
|
$ch,
|
|
array(
|
|
CURLOPT_RETURNTRANSFER => true,
|
|
CURLOPT_CUSTOMREQUEST => $method,
|
|
CURLOPT_HTTPHEADER => $headers,
|
|
CURLOPT_TIMEOUT => $this->timeout,
|
|
)
|
|
);
|
|
|
|
if ($jsonPayload !== null ) {
|
|
curl_setopt( $ch, CURLOPT_POSTFIELDS, $jsonPayload );
|
|
}
|
|
|
|
$rawResponse = curl_exec( $ch );
|
|
|
|
if ($rawResponse === false ) {
|
|
$error = curl_error( $ch );
|
|
curl_close( $ch );
|
|
throw new SpacetradersApiException( 'Network error: ' . $error );
|
|
}
|
|
|
|
$statusCode = (int) curl_getinfo( $ch, CURLINFO_HTTP_CODE );
|
|
curl_close( $ch );
|
|
|
|
$decoded = json_decode( $rawResponse, true );
|
|
if (! is_array( $decoded ) ) {
|
|
throw new SpacetradersApiException(
|
|
'API returned an invalid JSON response.',
|
|
$statusCode
|
|
);
|
|
}
|
|
|
|
if ($statusCode >= 400 ) {
|
|
$message = $decoded['error']['message'] ?? 'Unknown API error.';
|
|
$errorCode = (int) ( $decoded['error']['code'] ?? $statusCode );
|
|
throw new SpacetradersApiException( $message, $errorCode, $decoded );
|
|
}
|
|
|
|
if ($method === 'GET' && $this->storage !== null ) {
|
|
$this->storage->setCache( $cacheKey, $decoded, $this->cacheTtl );
|
|
$this->storage->purgeExpiredCache();
|
|
}
|
|
|
|
return $decoded;
|
|
}
|
|
|
|
/**
|
|
* Register a new agent and returns the API response.
|
|
*
|
|
* @param array<string,mixed> $options Optional registration options: `symbol`, `faction`, and `email`.
|
|
*
|
|
* @return array<string,mixed>
|
|
*/
|
|
public function registerAgent( array $options ): array {
|
|
return $this->request( 'POST', '/register', $options );
|
|
}
|
|
|
|
/**
|
|
* Get the current agent's information.
|
|
*
|
|
* @return array<string,mixed>
|
|
*/
|
|
public function getMyAgent(): array {
|
|
return $this->request( 'GET', '/my/agent' );
|
|
}
|
|
|
|
/**
|
|
* Get a list of all ships owned by the current agent.
|
|
*
|
|
* @param array<string,mixed> $query
|
|
*
|
|
* @return array<string,mixed>
|
|
*/
|
|
public function listMyShips( array $query = array() ): array {
|
|
return $this->request( 'GET', '/my/ships', array(), $query );
|
|
}
|
|
|
|
/**
|
|
* Get information about a specific ship owned by the current agent.
|
|
*
|
|
* @param string $shipSymbol The symbol of the ship to retrieve.
|
|
*
|
|
* @return array<string,mixed>
|
|
*/
|
|
public function getShip( string $shipSymbol ): array {
|
|
return $this->request( 'GET', '/my/ships/' . rawurlencode( $shipSymbol ) );
|
|
}
|
|
|
|
/**
|
|
* Get a list of all contracts for the current agent.
|
|
*
|
|
* @param array<string,mixed> $query
|
|
*
|
|
* @return array<string,mixed>
|
|
*/
|
|
public function listMyContracts( array $query = array() ): array {
|
|
return $this->request( 'GET', '/my/contracts', array(), $query );
|
|
}
|
|
|
|
/**
|
|
* Accept a contract for the current agent.
|
|
*
|
|
* @param string $contractId The ID of the contract to accept.
|
|
*
|
|
* @return array<string,mixed>
|
|
*/
|
|
public function acceptContract( string $contractId ): array {
|
|
return $this->request(
|
|
'POST',
|
|
'/my/contracts/' . rawurlencode( $contractId ) . '/accept'
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Get a list of all systems in the universe.
|
|
*
|
|
* @param array<string,mixed> $query
|
|
*
|
|
* @return array<string,mixed>
|
|
*/
|
|
public function listSystems( array $query = array() ): array {
|
|
return $this->request( 'GET', '/systems', array(), $query );
|
|
}
|
|
|
|
/**
|
|
* Get information about a specific system.
|
|
*
|
|
* @param string $systemSymbol The symbol of the system to retrieve.
|
|
*
|
|
* @return array<string,mixed>
|
|
*/
|
|
public function getSystem( string $systemSymbol ): array {
|
|
return $this->request( 'GET', '/systems/' . rawurlencode( $systemSymbol ) );
|
|
}
|
|
|
|
/**
|
|
* Get a list of waypoints in a specific system.
|
|
*
|
|
* @param string $systemSymbol The symbol of the system to retrieve waypoints from.
|
|
* @param array<string,mixed> $query
|
|
*
|
|
* @return array<string,mixed>
|
|
*/
|
|
public function listWaypoints( string $systemSymbol, array $query = array() ): array {
|
|
return $this->request(
|
|
'GET',
|
|
'/systems/' . rawurlencode( $systemSymbol ) . '/waypoints',
|
|
array(),
|
|
$query
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Get information about a single waypoint.
|
|
*
|
|
* @param string $systemSymbol The system symbol.
|
|
* @param string $waypointSymbol The waypoint symbol.
|
|
*
|
|
* @return array<string,mixed>
|
|
*/
|
|
public function getWaypoint( string $systemSymbol, string $waypointSymbol ): array {
|
|
return $this->request(
|
|
'GET',
|
|
'/systems/' . rawurlencode( $systemSymbol ) . '/waypoints/' . rawurlencode( $waypointSymbol )
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Get market data for a waypoint with a marketplace.
|
|
*
|
|
* @param string $systemSymbol The system symbol.
|
|
* @param string $waypointSymbol The waypoint symbol.
|
|
*
|
|
* @return array<string,mixed>
|
|
*/
|
|
public function getWaypointMarket( string $systemSymbol, string $waypointSymbol ): array {
|
|
return $this->request(
|
|
'GET',
|
|
'/systems/' . rawurlencode( $systemSymbol ) . '/waypoints/' . rawurlencode( $waypointSymbol ) . '/market'
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Get shipyard data for a waypoint with a shipyard.
|
|
*
|
|
* @param string $systemSymbol The system symbol.
|
|
* @param string $waypointSymbol The waypoint symbol.
|
|
*
|
|
* @return array<string,mixed>
|
|
*/
|
|
public function getWaypointShipyard( string $systemSymbol, string $waypointSymbol ): array {
|
|
return $this->request(
|
|
'GET',
|
|
'/systems/' . rawurlencode( $systemSymbol ) . '/waypoints/' . rawurlencode( $waypointSymbol ) . '/shipyard'
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Navigate a ship to a specific waypoint.
|
|
*
|
|
* @param string $shipSymbol The symbol of the ship to navigate.
|
|
* @param string $waypointSymbol The symbol of the waypoint to navigate to.
|
|
*
|
|
* @return array<string,mixed>
|
|
*/
|
|
public function navigateShip( string $shipSymbol, string $waypointSymbol ): array {
|
|
return $this->request(
|
|
'POST',
|
|
'/my/ships/' . rawurlencode( $shipSymbol ) . '/navigate',
|
|
array( 'waypointSymbol' => $waypointSymbol )
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Put a ship into orbit around its current waypoint.
|
|
*
|
|
* @param string $shipSymbol The symbol of the ship to put into orbit.
|
|
*
|
|
* @return array<string,mixed>
|
|
*/
|
|
public function orbitShip( string $shipSymbol ): array {
|
|
return $this->request(
|
|
'POST',
|
|
'/my/ships/' . rawurlencode( $shipSymbol ) . '/orbit'
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Dock a ship at its current waypoint.
|
|
*
|
|
* @param string $shipSymbol The symbol of the ship to dock.
|
|
*
|
|
* @return array<string,mixed>
|
|
*/
|
|
public function dockShip( string $shipSymbol ): array {
|
|
return $this->request(
|
|
'POST',
|
|
'/my/ships/' . rawurlencode( $shipSymbol ) . '/dock'
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Extract resources from the current waypoint using the specified ship.
|
|
*
|
|
* @param string $shipSymbol The symbol of the ship to use for resource extraction.
|
|
*
|
|
* @return array<string,mixed>
|
|
*/
|
|
public function extractResources( string $shipSymbol ): array {
|
|
return $this->request(
|
|
'POST',
|
|
'/my/ships/' . rawurlencode( $shipSymbol ) . '/extract'
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Create a survey at the ship's current waypoint.
|
|
*
|
|
* @param string $shipSymbol The symbol of the ship creating the survey.
|
|
*
|
|
* @return array<string,mixed>
|
|
*/
|
|
public function surveyWaypoint( string $shipSymbol ): array {
|
|
return $this->request(
|
|
'POST',
|
|
'/my/ships/' . rawurlencode( $shipSymbol ) . '/survey'
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Siphon resources at the ship's current waypoint.
|
|
*
|
|
* @param string $shipSymbol The symbol of the ship performing siphon.
|
|
*
|
|
* @return array<string,mixed>
|
|
*/
|
|
public function siphonResources( string $shipSymbol ): array {
|
|
return $this->request(
|
|
'POST',
|
|
'/my/ships/' . rawurlencode( $shipSymbol ) . '/siphon'
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Refuel a ship.
|
|
*
|
|
* @param string $shipSymbol The symbol of the ship to refuel.
|
|
* @param bool $fromCargo Whether to refuel from cargo instead of market.
|
|
* @param int|null $units Optional number of fuel units to buy.
|
|
*
|
|
* @return array<string,mixed>
|
|
*/
|
|
public function refuelShip( string $shipSymbol, bool $fromCargo = false, ?int $units = null ): array {
|
|
$payload = array();
|
|
|
|
if ($fromCargo ) {
|
|
$payload['fromCargo'] = true;
|
|
}
|
|
|
|
if ($units !== null && $units > 0 ) {
|
|
$payload['units'] = $units;
|
|
}
|
|
|
|
return $this->request(
|
|
'POST',
|
|
'/my/ships/' . rawurlencode( $shipSymbol ) . '/refuel',
|
|
$payload
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Purchase cargo for a specific ship at its current waypoint.
|
|
*
|
|
* @param string $shipSymbol The symbol of the ship to purchase cargo for.
|
|
* @param string $tradeSymbol The symbol of the trade good to purchase.
|
|
* @param int $units The number of units to purchase.
|
|
*
|
|
* @return array<string,mixed>
|
|
*/
|
|
public function purchaseCargo( string $shipSymbol, string $tradeSymbol, int $units ): array {
|
|
return $this->request(
|
|
'POST',
|
|
'/my/ships/' . rawurlencode( $shipSymbol ) . '/purchase',
|
|
array(
|
|
'symbol' => $tradeSymbol,
|
|
'units' => $units,
|
|
)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Sell cargo from a specific ship at its current waypoint.
|
|
*
|
|
* @param string $shipSymbol The symbol of the ship to sell cargo from.
|
|
* @param string $tradeSymbol The symbol of the trade good to sell.
|
|
* @param int $units The number of units to sell.
|
|
*
|
|
* @return array<string,mixed>
|
|
*/
|
|
public function sellCargo( string $shipSymbol, string $tradeSymbol, int $units ): array {
|
|
return $this->request(
|
|
'POST',
|
|
'/my/ships/' . rawurlencode( $shipSymbol ) . '/sell',
|
|
array(
|
|
'symbol' => $tradeSymbol,
|
|
'units' => $units,
|
|
)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Jettison cargo from a ship.
|
|
*
|
|
* @param string $shipSymbol The symbol of the ship.
|
|
* @param string $tradeSymbol The cargo symbol to jettison.
|
|
* @param int $units Number of units to jettison.
|
|
*
|
|
* @return array<string,mixed>
|
|
*/
|
|
public function jettisonCargo( string $shipSymbol, string $tradeSymbol, int $units ): array {
|
|
return $this->request(
|
|
'POST',
|
|
'/my/ships/' . rawurlencode( $shipSymbol ) . '/jettison',
|
|
array(
|
|
'symbol' => $tradeSymbol,
|
|
'units' => $units,
|
|
)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Purchase a new ship from a shipyard.
|
|
*
|
|
* @param string $shipType The ship type to purchase.
|
|
* @param string $waypointSymbol The shipyard waypoint symbol.
|
|
*
|
|
* @return array<string,mixed>
|
|
*/
|
|
public function purchaseShip( string $shipType, string $waypointSymbol ): array {
|
|
return $this->request(
|
|
'POST',
|
|
'/my/ships',
|
|
array(
|
|
'shipType' => $shipType,
|
|
'waypointSymbol' => $waypointSymbol,
|
|
)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Build the full URL for an API endpoint with optional query parameters.
|
|
*
|
|
* @param string $endpoint The API endpoint path.
|
|
* @param array<string,mixed> $query The query parameters to append.
|
|
*
|
|
* @return string The complete URL.
|
|
*/
|
|
private function _buildUrl( string $endpoint, array $query = array() ): string { // phpcs:ignore
|
|
$url = $this->baseUrl . '/' . ltrim( $endpoint, '/' );
|
|
|
|
if (! empty( $query ) ) {
|
|
$url .= '?' . http_build_query( $query );
|
|
}
|
|
|
|
return $url;
|
|
}
|
|
|
|
/**
|
|
* Build a deterministic cache key for an API request.
|
|
*
|
|
* @param string $method HTTP method.
|
|
* @param string $url Full URL.
|
|
* @param array<string,mixed> $payload Request payload.
|
|
*
|
|
* @return string
|
|
*/
|
|
private function _buildCacheKey( string $method, string $url, array $payload ): string { // phpcs:ignore
|
|
$payloadJson = json_encode( $payload );
|
|
if ($payloadJson === false ) {
|
|
$payloadJson = '';
|
|
}
|
|
|
|
return hash( 'sha256', $method . '|' . $url . '|' . $payloadJson );
|
|
}
|
|
}
|