200 lines
6.1 KiB
Go
200 lines
6.1 KiB
Go
// Package esi provides EVE Online ESI API implementations for planetary interaction data.
|
|
package esi
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"os"
|
|
"time"
|
|
|
|
"go-eve-pi/options"
|
|
|
|
logger "git.site.quack-lab.dev/dave/cylogger"
|
|
)
|
|
|
|
const (
|
|
ESIBaseURL = "https://esi.evetech.net"
|
|
)
|
|
|
|
type Planet struct {
|
|
PlanetID int `json:"planet_id"`
|
|
PlanetType string `json:"planet_type"`
|
|
SolarSystemID int `json:"solar_system_id"`
|
|
UpgradeLevel int `json:"upgrade_level"`
|
|
NumPins int `json:"num_pins"`
|
|
LastUpdate string `json:"last_update"`
|
|
OwnerID int `json:"owner_id"`
|
|
PlanetName string `json:"planet_name"`
|
|
PlanetTypeID int `json:"planet_type_id"`
|
|
Position struct {
|
|
X float64 `json:"x"`
|
|
Y float64 `json:"y"`
|
|
Z float64 `json:"z"`
|
|
} `json:"position"`
|
|
}
|
|
|
|
type PlanetDetail struct {
|
|
Links []Link `json:"links"`
|
|
Pins []Pin `json:"pins"`
|
|
Routes []Route `json:"routes"`
|
|
LastUpdate string `json:"last_update"`
|
|
}
|
|
|
|
type Link struct {
|
|
DestinationPinID int `json:"destination_pin_id"`
|
|
LinkLevel int `json:"link_level"`
|
|
SourcePinID int `json:"source_pin_id"`
|
|
}
|
|
|
|
type Pin struct {
|
|
Contents []StorageContent `json:"contents"`
|
|
ExpiryTime *string `json:"expiry_time"`
|
|
ExtractorDetails *ExtractorDetails `json:"extractor_details"`
|
|
FactoryDetails *FactoryDetails `json:"factory_details"`
|
|
InstallTime *string `json:"install_time"`
|
|
LastCycleStart *string `json:"last_cycle_start"`
|
|
Latitude float64 `json:"latitude"`
|
|
Longitude float64 `json:"longitude"`
|
|
PinID int `json:"pin_id"`
|
|
SchematicID *int `json:"schematic_id"`
|
|
TypeID int `json:"type_id"`
|
|
}
|
|
|
|
type StorageContent struct {
|
|
Amount int `json:"amount"`
|
|
TypeID int `json:"type_id"`
|
|
}
|
|
|
|
type ExtractorDetails struct {
|
|
CycleTime *int `json:"cycle_time"`
|
|
HeadRadius *float64 `json:"head_radius"`
|
|
Heads []Head `json:"heads"`
|
|
ProductTypeID *int `json:"product_type_id"`
|
|
QtyPerCycle *int `json:"qty_per_cycle"`
|
|
}
|
|
|
|
type Head struct {
|
|
HeadID int `json:"head_id"`
|
|
Latitude float64 `json:"latitude"`
|
|
Longitude float64 `json:"longitude"`
|
|
}
|
|
|
|
type FactoryDetails struct {
|
|
SchematicID int `json:"schematic_id"`
|
|
}
|
|
|
|
type Route struct {
|
|
ContentTypeID int `json:"content_type_id"`
|
|
DestinationPinID int `json:"destination_pin_id"`
|
|
Quantity float64 `json:"quantity"`
|
|
RouteID int `json:"route_id"`
|
|
SourcePinID int `json:"source_pin_id"`
|
|
Waypoints []int `json:"waypoints"`
|
|
}
|
|
|
|
// ESIInterface defines the contract for ESI API interactions
|
|
type ESIInterface interface {
|
|
GetCharacterPlanets(ctx context.Context, characterID int, accessToken string) ([]Planet, error)
|
|
GetPlanetDetails(ctx context.Context, characterID, planetID int, accessToken string) (*PlanetDetail, error)
|
|
}
|
|
|
|
// DirectESI implements ESIInterface with direct API calls
|
|
type DirectESI struct {
|
|
httpClient *http.Client
|
|
}
|
|
|
|
// NewDirectESI creates a new DirectESI instance
|
|
func NewDirectESI() *DirectESI {
|
|
// Parse HTTP timeout ONCE at initialization
|
|
httpTimeout, err := time.ParseDuration(options.GlobalOptions.HTTPTimeout)
|
|
if err != nil {
|
|
logger.Error("Invalid HTTP timeout duration %s: %v", options.GlobalOptions.HTTPTimeout, err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
return &DirectESI{
|
|
httpClient: &http.Client{
|
|
Timeout: httpTimeout,
|
|
},
|
|
}
|
|
}
|
|
|
|
// GetCharacterPlanets retrieves a list of planets for a character
|
|
func (d *DirectESI) GetCharacterPlanets(ctx context.Context, characterID int, accessToken string) ([]Planet, error) {
|
|
logger.Debug("Fetching planets for character ID %d", characterID)
|
|
|
|
url := fmt.Sprintf("%s/v1/characters/%d/planets/", ESIBaseURL, characterID)
|
|
|
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
|
if err != nil {
|
|
logger.Error("Failed to create request for character planets: %v", err)
|
|
return nil, err
|
|
}
|
|
|
|
req.Header.Set("Authorization", "Bearer "+accessToken)
|
|
req.Header.Set("Accept", "application/json")
|
|
|
|
resp, err := d.httpClient.Do(req)
|
|
if err != nil {
|
|
logger.Error("Failed to fetch character planets: %v", err)
|
|
return nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
body, _ := io.ReadAll(resp.Body)
|
|
logger.Error("Character planets API returned status %d: %s", resp.StatusCode, string(body))
|
|
return nil, fmt.Errorf("API request failed with status %d: %s", resp.StatusCode, string(body))
|
|
}
|
|
|
|
var planets []Planet
|
|
if err := json.NewDecoder(resp.Body).Decode(&planets); err != nil {
|
|
logger.Error("Failed to decode character planets response: %v", err)
|
|
return nil, err
|
|
}
|
|
|
|
logger.Info("Successfully fetched %d planets for character ID %d", len(planets), characterID)
|
|
return planets, nil
|
|
}
|
|
|
|
// GetPlanetDetails retrieves detailed information about a specific planet
|
|
func (d *DirectESI) GetPlanetDetails(ctx context.Context, characterID, planetID int, accessToken string) (*PlanetDetail, error) {
|
|
logger.Debug("Fetching planet details for character ID %d, planet ID %d", characterID, planetID)
|
|
|
|
url := fmt.Sprintf("%s/v3/characters/%d/planets/%d/", ESIBaseURL, characterID, planetID)
|
|
|
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
|
if err != nil {
|
|
logger.Error("Failed to create request for planet details: %v", err)
|
|
return nil, err
|
|
}
|
|
|
|
req.Header.Set("Authorization", "Bearer "+accessToken)
|
|
req.Header.Set("Accept", "application/json")
|
|
|
|
resp, err := d.httpClient.Do(req)
|
|
if err != nil {
|
|
logger.Error("Failed to fetch planet details: %v", err)
|
|
return nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
body, _ := io.ReadAll(resp.Body)
|
|
logger.Error("Planet details API returned status %d: %s", resp.StatusCode, string(body))
|
|
return nil, fmt.Errorf("API request failed with status %d: %s", resp.StatusCode, string(body))
|
|
}
|
|
|
|
var planetDetail PlanetDetail
|
|
if err := json.NewDecoder(resp.Body).Decode(&planetDetail); err != nil {
|
|
logger.Error("Failed to decode planet details response: %v", err)
|
|
return nil, err
|
|
}
|
|
|
|
logger.Info("Successfully fetched planet details for character ID %d, planet ID %d", characterID, planetID)
|
|
return &planetDetail, nil
|
|
}
|