import gleam/dict.{type Dict} import gleam/dynamic/decode.{type Decoder} import gleam/json import gleam/list import gleam/option.{type Option} import gleam/time/calendar.{type Date} import gleam/time/timestamp.{type Timestamp} import gleam/uri.{type Uri} import spacetraders_models/account.{type Account} import spacetraders_models/agent.{type Agent} import spacetraders_models/agent_event.{type AgentEvent} import spacetraders_models/agent_symbol.{type AgentSymbol} import spacetraders_models/chart.{type Chart} import spacetraders_models/chart_transaction.{type ChartTransaction} import spacetraders_models/construction.{type Construction} import spacetraders_models/contract.{type Contract} import spacetraders_models/contract_id.{type ContractId} import spacetraders_models/cooldown.{type Cooldown} import spacetraders_models/extraction.{type Extraction} import spacetraders_models/faction.{type Faction} import spacetraders_models/faction_symbol.{type FactionSymbol} import spacetraders_models/jump_gate.{type JumpGate} import spacetraders_models/market.{type Market} import spacetraders_models/market_transaction.{type MarketTransaction} import spacetraders_models/module_symbol.{type ModuleSymbol} import spacetraders_models/mount_symbol.{type MountSymbol} import spacetraders_models/public_agent.{type PublicAgent} import spacetraders_models/refinement_produce.{type RefinementProduce} import spacetraders_models/refinement_yield.{type RefinementYield} import spacetraders_models/repair_transaction.{type RepairTransaction} import spacetraders_models/scanned_ship.{type ScannedShip} import spacetraders_models/scanned_system.{type ScannedSystem} import spacetraders_models/scanned_waypoint.{type ScannedWaypoint} import spacetraders_models/scrap_transaction.{type ScrapTransaction} import spacetraders_models/ship.{type Ship} import spacetraders_models/ship_cargo.{type ShipCargo} import spacetraders_models/ship_condition_event.{type ShipConditionEvent} import spacetraders_models/ship_fuel.{type ShipFuel} import spacetraders_models/ship_modification_transaction.{ type ShipModificationTransaction, } import spacetraders_models/ship_module.{type ShipModule} import spacetraders_models/ship_mount.{type ShipMount} import spacetraders_models/ship_nav.{type ShipNav} import spacetraders_models/ship_nav_flight_mode.{type ShipNavFlightMode} import spacetraders_models/ship_symbol.{type ShipSymbol} import spacetraders_models/ship_type.{type ShipType} import spacetraders_models/shipyard.{type Shipyard} import spacetraders_models/shipyard_transaction.{type ShipyardTransaction} import spacetraders_models/siphon.{type Siphon} import spacetraders_models/survey.{type Survey} import spacetraders_models/system.{type System} import spacetraders_models/system_symbol.{type SystemSymbol} import spacetraders_models/trade_symbol.{type TradeSymbol} import spacetraders_models/waypoint.{type Waypoint} import spacetraders_models/waypoint_modifier.{type WaypointModifier} import spacetraders_models/waypoint_symbol.{type WaypointSymbol} import spacetraders_models/waypoint_trait_symbol.{type WaypointTraitSymbol} import spacetraders_models/waypoint_type.{type WaypointType} import spacetraders_sdk.{ type AccountToken, type AgentToken, type ApiResponse, type PagedData, AccountAuth, AgentAuth, NoAuth, } import spacetraders_sdk/internal/api import spacetraders_sdk/internal/time pub fn get_account(token: AgentToken) -> ApiResponse(Account) { let request = api.get(AgentAuth(token), "/my/account") use response <- api.try_send(request) case response.status { 200 -> api.parse_data_response( response, decode.field("account", account.decoder(), decode.success), ) _ -> api.parse_error_response(response) } } pub type AgentRegistered { AgentRegistered( token: AgentToken, agent: Agent, faction: Faction, contract: Contract, ships: List(Ship), ) } pub fn register_new_agent( token: AccountToken, agent_symbol: AgentSymbol, faction_symbol: FactionSymbol, ) -> ApiResponse(AgentRegistered) { let request = api.post_json( AccountAuth(token), "/register", json.object([ #("symbol", agent_symbol.encode(agent_symbol)), #("faction", faction_symbol.encode(faction_symbol)), ]), ) use response <- api.try_send(request) case response.status { 201 -> api.parse_data_response(response, { use token <- decode.field( "token", spacetraders_sdk.agent_token_decoder(), ) use agent <- decode.field("agent", agent.decoder()) use faction <- decode.field("faction", faction.decoder()) use contract <- decode.field("contract", contract.decoder()) use ships <- decode.field("ships", decode.list(ship.decoder())) decode.success(AgentRegistered( token:, agent:, faction:, contract:, ships:, )) }) _ -> api.parse_error_response(response) } } pub fn list_public_agents( page: Option(Int), limit: Option(Int), ) -> ApiResponse(PagedData(List(PublicAgent))) { let request = api.get_page(NoAuth, "/agents", page, limit) use response <- api.try_send(request) case response.status { 200 -> api.parse_paged_data_response( response, decode.list(public_agent.decoder()), ) _ -> api.parse_error_response(response) } } pub fn get_public_agent(agent_symbol: AgentSymbol) -> ApiResponse(PublicAgent) { let request = api.get(NoAuth, "/agents/" <> agent_symbol.to_string(agent_symbol)) use response <- api.try_send(request) case response.status { 200 -> api.parse_data_response(response, public_agent.decoder()) _ -> api.parse_error_response(response) } } pub fn get_agent(token: AgentToken) -> ApiResponse(Agent) { let request = api.get(AgentAuth(token), "/my/agent") use response <- api.try_send(request) case response.status { 200 -> api.parse_data_response(response, agent.decoder()) _ -> api.parse_error_response(response) } } pub fn get_agent_events(token: AgentToken) -> ApiResponse(List(AgentEvent)) { let request = api.get(AgentAuth(token), "/my/agent/events") use response <- api.try_send(request) case response.status { 200 -> api.parse_data_response(response, decode.list(agent_event.decoder())) _ -> api.parse_error_response(response) } } pub fn list_contracts( token: AgentToken, page: Option(Int), limit: Option(Int), ) -> ApiResponse(PagedData(List(Contract))) { let request = api.get_page(AgentAuth(token), "/my/contracts", page, limit) use response <- api.try_send(request) case response.status { 200 -> api.parse_paged_data_response(response, decode.list(contract.decoder())) _ -> api.parse_error_response(response) } } pub fn get_contract( token: AgentToken, contract_id: ContractId, ) -> ApiResponse(Contract) { let request = api.get( AgentAuth(token), "/my/contracts/" <> contract_id.to_string(contract_id), ) use response <- api.try_send(request) case response.status { 200 -> api.parse_data_response(response, contract.decoder()) _ -> api.parse_error_response(response) } } pub type ContractAccepted { ContractAccepted(contract: Contract, agent: Agent) } pub fn accept_contract( token: AgentToken, contract_id: ContractId, ) -> ApiResponse(ContractAccepted) { let request = api.post( AgentAuth(token), "/my/contracts/" <> contract_id.to_string(contract_id) <> "/accept", ) use response <- api.try_send(request) case response.status { 200 -> api.parse_data_response(response, { use contract <- decode.field("contract", contract.decoder()) use agent <- decode.field("agent", agent.decoder()) decode.success(ContractAccepted(contract:, agent:)) }) _ -> api.parse_error_response(response) } } pub type ContractFulfilled { ContractFulfilled(contract: Contract, agent: Agent) } pub fn fulfill_contract( token: AgentToken, contract_id: ContractId, ) -> ApiResponse(ContractFulfilled) { let request = api.post( AgentAuth(token), "/my/contracts/" <> contract_id.to_string(contract_id) <> "/fulfill", ) use response <- api.try_send(request) case response.status { 200 -> api.parse_data_response(response, { use contract <- decode.field("contract", contract.decoder()) use agent <- decode.field("agent", agent.decoder()) decode.success(ContractFulfilled(contract:, agent:)) }) _ -> api.parse_error_response(response) } } pub type ContractCargoDelivered { ContractCargoDelivered(contract: Contract, cargo: ShipCargo) } pub fn deliver_contract_cargo( token: AgentToken, contract_id: ContractId, ship_symbol: ShipSymbol, trade_symbol: TradeSymbol, units: Int, ) -> ApiResponse(ContractCargoDelivered) { let request = api.post_json( AgentAuth(token), "/my/contracts/" <> contract_id.to_string(contract_id) <> "/deliver", json.object([ #("shipSymbol", ship_symbol.encode(ship_symbol)), #("tradeSymbol", trade_symbol.encode(trade_symbol)), #("units", json.int(units)), ]), ) use response <- api.try_send(request) case response.status { 200 -> api.parse_data_response(response, { use contract <- decode.field("contract", contract.decoder()) use cargo <- decode.field("cargo", ship_cargo.decoder()) decode.success(ContractCargoDelivered(contract:, cargo:)) }) _ -> api.parse_error_response(response) } } pub type ExportToImportMap = Dict(TradeSymbol, List(TradeSymbol)) pub fn get_supply_chain() -> ApiResponse(ExportToImportMap) { let request = api.get(NoAuth, "/market/supply-chain") use response <- api.try_send(request) case response.status { 200 -> api.parse_data_response( response, decode.field( "exportToImportMap", decode.dict( trade_symbol.decoder(), decode.list(trade_symbol.decoder()), ), decode.success, ), ) _ -> api.parse_error_response(response) } } pub fn list_factions( page: Option(Int), limit: Option(Int), ) -> ApiResponse(PagedData(List(Faction))) { let request = api.get_page(NoAuth, "/factions", page, limit) use response <- api.try_send(request) case response.status { 200 -> api.parse_paged_data_response(response, decode.list(faction.decoder())) _ -> api.parse_error_response(response) } } pub fn get_faction(symbol: FactionSymbol) -> ApiResponse(Faction) { let request = api.get(NoAuth, "/factions/" <> faction_symbol.to_string(symbol)) use response <- api.try_send(request) case response.status { 200 -> api.parse_data_response(response, faction.decoder()) _ -> api.parse_error_response(response) } } pub type FactionReputation { FactionReputation(symbol: FactionSymbol, reputation: Int) } pub fn get_my_factions( token: AgentToken, page: Option(Int), limit: Option(Int), ) -> ApiResponse(PagedData(List(FactionReputation))) { let request = api.get_page(AgentAuth(token), "/my/factions", page, limit) use response <- api.try_send(request) case response.status { 200 -> api.parse_paged_data_response( response, decode.list({ use symbol <- decode.field("symbol", faction_symbol.decoder()) use reputation <- decode.field("reputation", decode.int) decode.success(FactionReputation(symbol:, reputation:)) }), ) _ -> api.parse_error_response(response) } } pub fn list_ships( token: AgentToken, page: Option(Int), limit: Option(Int), ) -> ApiResponse(PagedData(List(Ship))) { let request = api.get_page(AgentAuth(token), "/my/ships", page, limit) use response <- api.try_send(request) case response.status { 200 -> api.parse_paged_data_response(response, decode.list(ship.decoder())) _ -> api.parse_error_response(response) } } pub type ShipPurchased { ShipPurchased(ship: Ship, agent: Agent, transaction: ShipyardTransaction) } pub fn purchase_ship( token: AgentToken, ship_type: ShipType, waypoint_symbol: WaypointSymbol, ) -> ApiResponse(ShipPurchased) { let request = api.post_json( AgentAuth(token), "/my/ships", json.object([ #("shipType", ship_type.encode(ship_type)), #("waypointSymbol", waypoint_symbol.encode(waypoint_symbol)), ]), ) use response <- api.try_send(request) case response.status { 201 -> api.parse_data_response(response, { use ship <- decode.field("ship", ship.decoder()) use agent <- decode.field("agent", agent.decoder()) use transaction <- decode.field( "transaction", shipyard_transaction.decoder(), ) decode.success(ShipPurchased(ship:, agent:, transaction:)) }) _ -> api.parse_error_response(response) } } pub fn get_ship(token: AgentToken, ship_symbol: ShipSymbol) -> ApiResponse(Ship) { let request = api.get( AgentAuth(token), "/my/ships/" <> ship_symbol.to_string(ship_symbol), ) use response <- api.try_send(request) case response.status { 200 -> api.parse_data_response(response, ship.decoder()) _ -> api.parse_error_response(response) } } pub type ChartCreated { ChartCreated( chart: Chart, waypoint: Waypoint, transaction: ChartTransaction, agent: Agent, ) } pub fn create_chart( token: AgentToken, ship_symbol: ShipSymbol, ) -> ApiResponse(ChartCreated) { let request = api.post( AgentAuth(token), "/my/ships/" <> ship_symbol.to_string(ship_symbol) <> "/chart", ) use response <- api.try_send(request) case response.status { 201 -> api.parse_data_response(response, { use chart <- decode.field("chart", chart.decoder()) use waypoint <- decode.field("waypoint", waypoint.decoder()) use transaction <- decode.field( "transaction", chart_transaction.decoder(), ) use agent <- decode.field("agent", agent.decoder()) decode.success(ChartCreated(chart:, waypoint:, transaction:, agent:)) }) _ -> api.parse_error_response(response) } } pub fn negotiate_contract( token: AgentToken, ship_symbol: String, ) -> ApiResponse(Contract) { let request = api.post( AgentAuth(token), "/my/ships/" <> ship_symbol <> "/negotiate/contract", ) use response <- api.try_send(request) case response.status { 201 -> api.parse_data_response( response, decode.field("contract", contract.decoder(), decode.success), ) _ -> api.parse_error_response(response) } } pub fn get_ship_cooldown( token: AgentToken, ship_symbol: ShipSymbol, ) -> ApiResponse(Option(Cooldown)) { let request = api.get( AgentAuth(token), "/my/ships/" <> ship_symbol.to_string(ship_symbol) <> "/cooldown", ) use response <- api.try_send(request) case response.status { 200 -> api.parse_data_response( response, decode.field("cooldown", cooldown.decoder(), fn(cooldown) { decode.success(option.Some(cooldown)) }), ) 204 -> Ok(option.None) _ -> api.parse_error_response(response) } } pub fn dock_ship( token: AgentToken, ship_symbol: ShipSymbol, ) -> ApiResponse(ShipNav) { let request = api.post( AgentAuth(token), "/my/ships/" <> ship_symbol.to_string(ship_symbol) <> "/dock", ) use response <- api.try_send(request) case response.status { 200 -> api.parse_data_response( response, decode.field("nav", ship_nav.decoder(), decode.success), ) _ -> api.parse_error_response(response) } } pub type ResourcesExtracted { ResourcesExtracted( extraction: Extraction, cooldown: Cooldown, cargo: ShipCargo, modifiers: List(WaypointModifier), events: List(ShipConditionEvent), ) } fn resources_extracted_decoder() -> Decoder(ResourcesExtracted) { use extraction <- decode.field("extraction", extraction.decoder()) use cooldown <- decode.field("cooldown", cooldown.decoder()) use cargo <- decode.field("cargo", ship_cargo.decoder()) use modifiers <- decode.field( "modifiers", decode.list(waypoint_modifier.decoder()), ) use events <- decode.field( "events", decode.list(ship_condition_event.decoder()), ) decode.success(ResourcesExtracted( extraction:, cooldown:, cargo:, modifiers:, events:, )) } pub fn extract_resources( token: AgentToken, ship_symbol: ShipSymbol, ) -> ApiResponse(ResourcesExtracted) { let request = api.post( AgentAuth(token), "/my/ships/" <> ship_symbol.to_string(ship_symbol) <> "/extract", ) use response <- api.try_send(request) case response.status { 201 -> api.parse_data_response(response, resources_extracted_decoder()) _ -> api.parse_error_response(response) } } pub fn extract_resources_with_survey( token: AgentToken, ship_symbol: ShipSymbol, survey: Survey, ) -> ApiResponse(ResourcesExtracted) { let request = api.post_json( AgentAuth(token), "/my/ships/" <> ship_symbol.to_string(ship_symbol) <> "/extract/survey", survey.encode(survey), ) use response <- api.try_send(request) case response.status { 201 -> api.parse_data_response(response, resources_extracted_decoder()) _ -> api.parse_error_response(response) } } pub fn jettison_cargo( token: AgentToken, ship_symbol: ShipSymbol, trade_symbol: TradeSymbol, units: Int, ) -> ApiResponse(ShipCargo) { let request = api.post_json( AgentAuth(token), "/my/ships/" <> ship_symbol.to_string(ship_symbol) <> "/jettison", json.object([ #("symbol", trade_symbol.encode(trade_symbol)), #("units", json.int(units)), ]), ) use response <- api.try_send(request) case response.status { 200 -> api.parse_data_response( response, decode.field("cargo", ship_cargo.decoder(), decode.success), ) _ -> api.parse_error_response(response) } } pub type ShipJumped { ShipJumped( nav: ShipNav, cooldown: Cooldown, transaction: MarketTransaction, agent: Agent, ) } pub fn jump_ship( token: AgentToken, ship_symbol: ShipSymbol, waypoint_symbol: WaypointSymbol, ) -> ApiResponse(ShipJumped) { let request = api.post_json( AgentAuth(token), "/my/ships/" <> ship_symbol.to_string(ship_symbol) <> "/jump", json.object([#("waypointSymbol", waypoint_symbol.encode(waypoint_symbol))]), ) use response <- api.try_send(request) case response.status { 200 -> api.parse_data_response(response, { use nav <- decode.field("nav", ship_nav.decoder()) use cooldown <- decode.field("cooldown", cooldown.decoder()) use transaction <- decode.field( "transaction", market_transaction.decoder(), ) use agent <- decode.field("agent", agent.decoder()) decode.success(ShipJumped(nav:, cooldown:, transaction:, agent:)) }) _ -> api.parse_error_response(response) } } pub type Scan(data) { Scan(cooldown: Cooldown, data: List(data)) } fn scan_decoder(field: String, decoder: Decoder(data)) -> Decoder(Scan(data)) { use cooldown <- decode.field("cooldown", cooldown.decoder()) use data <- decode.field(field, decode.list(decoder)) decode.success(Scan(cooldown:, data:)) } pub fn scan_systems( token: AgentToken, ship_symbol: ShipSymbol, ) -> ApiResponse(Scan(ScannedSystem)) { let request = api.post( AgentAuth(token), "/my/ships/" <> ship_symbol.to_string(ship_symbol) <> "/scan/systems", ) use response <- api.try_send(request) case response.status { 201 -> api.parse_data_response( response, scan_decoder("systems", scanned_system.decoder()), ) _ -> api.parse_error_response(response) } } pub fn scan_waypoints( token: AgentToken, ship_symbol: ShipSymbol, ) -> ApiResponse(Scan(ScannedWaypoint)) { let request = api.post( AgentAuth(token), "/my/ships/" <> ship_symbol.to_string(ship_symbol) <> "/scan/waypoints", ) use response <- api.try_send(request) case response.status { 201 -> api.parse_data_response( response, scan_decoder("waypoints", scanned_waypoint.decoder()), ) _ -> api.parse_error_response(response) } } pub fn scan_ships( token: AgentToken, ship_symbol: ShipSymbol, ) -> ApiResponse(Scan(ScannedShip)) { let request = api.post( AgentAuth(token), "/my/ships/" <> ship_symbol.to_string(ship_symbol) <> "/scan/ships", ) use response <- api.try_send(request) case response.status { 201 -> api.parse_data_response( response, scan_decoder("ships", scanned_ship.decoder()), ) _ -> api.parse_error_response(response) } } pub type ShipScrapped { ShipScrapped(agent: Agent, transaction: ScrapTransaction) } pub fn scrap_ship( token: AgentToken, ship_symbol: ShipSymbol, ) -> ApiResponse(ShipScrapped) { let request = api.post( AgentAuth(token), "/my/ships/" <> ship_symbol.to_string(ship_symbol) <> "/scrap", ) use response <- api.try_send(request) case response.status { 200 -> api.parse_data_response(response, { use agent <- decode.field("agent", agent.decoder()) use transaction <- decode.field( "transaction", scrap_transaction.decoder(), ) decode.success(ShipScrapped(agent:, transaction:)) }) _ -> api.parse_error_response(response) } } pub fn get_scrap_ship( token: AgentToken, ship_symbol: ShipSymbol, ) -> ApiResponse(ScrapTransaction) { let request = api.get( AgentAuth(token), "/my/ships/" <> ship_symbol.to_string(ship_symbol) <> "/scrap", ) use response <- api.try_send(request) case response.status { 200 -> api.parse_data_response( response, decode.field("transaction", scrap_transaction.decoder(), decode.success), ) _ -> api.parse_error_response(response) } } pub type ShipNavigated { ShipNavigated(nav: ShipNav, fuel: ShipFuel, events: List(ShipConditionEvent)) } pub fn navigate_ship( token: AgentToken, ship_symbol: ShipSymbol, waypoint_symbol: WaypointSymbol, ) -> ApiResponse(ShipNavigated) { let request = api.post_json( AgentAuth(token), "/my/ships/" <> ship_symbol.to_string(ship_symbol) <> "/navigate", json.object([#("waypointSymbol", waypoint_symbol.encode(waypoint_symbol))]), ) use response <- api.try_send(request) case response.status { 200 -> api.parse_data_response(response, { use nav <- decode.field("nav", ship_nav.decoder()) use fuel <- decode.field("fuel", ship_fuel.decoder()) use events <- decode.field( "events", decode.list(ship_condition_event.decoder()), ) decode.success(ShipNavigated(nav:, fuel:, events:)) }) _ -> api.parse_error_response(response) } } pub type ShipWarped { ShipWarped(nav: ShipNav, fuel: ShipFuel, events: List(ShipConditionEvent)) } pub fn warp_ship( token: AgentToken, ship_symbol: ShipSymbol, waypoint_symbol: WaypointSymbol, ) -> ApiResponse(ShipWarped) { let request = api.post_json( AgentAuth(token), "/my/ships/" <> ship_symbol.to_string(ship_symbol) <> "/warp", json.object([#("waypointSymbol", waypoint_symbol.encode(waypoint_symbol))]), ) use response <- api.try_send(request) case response.status { 200 -> api.parse_data_response(response, { use nav <- decode.field("nav", ship_nav.decoder()) use fuel <- decode.field("fuel", ship_fuel.decoder()) use events <- decode.field( "events", decode.list(ship_condition_event.decoder()), ) decode.success(ShipWarped(nav:, fuel:, events:)) }) _ -> api.parse_error_response(response) } } pub fn orbit_ship( token: AgentToken, ship_symbol: ShipSymbol, ) -> ApiResponse(ShipNav) { let request = api.post( AgentAuth(token), "/my/ships/" <> ship_symbol.to_string(ship_symbol) <> "/orbit", ) use response <- api.try_send(request) case response.status { 200 -> api.parse_data_response( response, decode.field("nav", ship_nav.decoder(), decode.success), ) _ -> api.parse_error_response(response) } } pub type CargoPurchased { CargoPurchased(cargo: ShipCargo, transaction: MarketTransaction, agent: Agent) } pub fn purchase_cargo( token: AgentToken, ship_symbol: ShipSymbol, trade_symbol: TradeSymbol, ) -> ApiResponse(CargoPurchased) { let request = api.post_json( AgentAuth(token), "/my/ships/" <> ship_symbol.to_string(ship_symbol) <> "/purchase", json.object([#("symbol", trade_symbol.encode(trade_symbol))]), ) use response <- api.try_send(request) case response.status { 201 -> api.parse_data_response(response, { use cargo <- decode.field("cargo", ship_cargo.decoder()) use transaction <- decode.field( "transaction", market_transaction.decoder(), ) use agent <- decode.field("agent", agent.decoder()) decode.success(CargoPurchased(cargo:, transaction:, agent:)) }) _ -> api.parse_error_response(response) } } pub type ShipRefined { ShipRefined( cargo: ShipCargo, cooldown: Cooldown, produced: List(RefinementYield), consumed: List(RefinementYield), ) } pub fn refine_ship( token: AgentToken, ship_symbol: ShipSymbol, produce: RefinementProduce, ) -> ApiResponse(ShipRefined) { let request = api.post_json( AgentAuth(token), "/my/ships/" <> ship_symbol.to_string(ship_symbol) <> "/refine", json.object([#("produce", refinement_produce.encode(produce))]), ) use response <- api.try_send(request) case response.status { 201 -> api.parse_data_response(response, { use cargo <- decode.field("cargo", ship_cargo.decoder()) use cooldown <- decode.field("cooldown", cooldown.decoder()) use produced <- decode.field( "produced", decode.list(refinement_yield.decoder()), ) use consumed <- decode.field( "consumed", decode.list(refinement_yield.decoder()), ) decode.success(ShipRefined(cargo:, cooldown:, produced:, consumed:)) }) _ -> api.parse_error_response(response) } } pub type ShipRefueled { ShipRefueled( agent: Agent, fuel: ShipFuel, cargo: ShipCargo, transaction: MarketTransaction, ) } pub fn refuel_ship( token: AgentToken, ship_symbol: ShipSymbol, units: Option(Int), from_cargo: Option(Bool), ) -> ApiResponse(ShipRefueled) { let request = api.post_json( AgentAuth(token), "/my/ships/" <> ship_symbol.to_string(ship_symbol) <> "/refuel", json.object([ #("units", json.nullable(units, json.int)), #("fromCargo", json.nullable(from_cargo, json.bool)), ]), ) use response <- api.try_send(request) case response.status { 200 -> api.parse_data_response(response, { use agent <- decode.field("agent", agent.decoder()) use fuel <- decode.field("fuel", ship_fuel.decoder()) use cargo <- decode.field("cargo", ship_cargo.decoder()) use transaction <- decode.field( "transaction", market_transaction.decoder(), ) decode.success(ShipRefueled(agent:, fuel:, cargo:, transaction:)) }) _ -> api.parse_error_response(response) } } pub type ShipRepaired { ShipRepaired(agent: Agent, ship: Ship, transaction: RepairTransaction) } pub fn repair_ship( token: AgentToken, ship_symbol: ShipSymbol, ) -> ApiResponse(ShipRepaired) { let request = api.post( AgentAuth(token), "/my/ships/" <> ship_symbol.to_string(ship_symbol) <> "/repair", ) use response <- api.try_send(request) case response.status { 200 -> api.parse_data_response(response, { use agent <- decode.field("agent", agent.decoder()) use ship <- decode.field("ship", ship.decoder()) use transaction <- decode.field( "transaction", repair_transaction.decoder(), ) decode.success(ShipRepaired(agent:, ship:, transaction:)) }) _ -> api.parse_error_response(response) } } pub fn get_repair_ship( token: AgentToken, ship_symbol: ShipSymbol, ) -> ApiResponse(RepairTransaction) { let request = api.get( AgentAuth(token), "/my/ships/" <> ship_symbol.to_string(ship_symbol) <> "/repair", ) use response <- api.try_send(request) case response.status { 200 -> api.parse_data_response( response, decode.field( "transaction", repair_transaction.decoder(), decode.success, ), ) _ -> api.parse_error_response(response) } } pub type CargoSold { CargoSold(cargo: ShipCargo, transaction: MarketTransaction, agent: Agent) } pub fn sell_cargo( token: AgentToken, ship_symbol: ShipSymbol, trade_symbol: TradeSymbol, units: Int, ) -> ApiResponse(CargoSold) { let request = api.post_json( AgentAuth(token), "/my/ships/" <> ship_symbol.to_string(ship_symbol) <> "/sell", json.object([ #("symbol", trade_symbol.encode(trade_symbol)), #("units", json.int(units)), ]), ) use response <- api.try_send(request) case response.status { 201 -> api.parse_data_response(response, { use cargo <- decode.field("cargo", ship_cargo.decoder()) use transaction <- decode.field( "transaction", market_transaction.decoder(), ) use agent <- decode.field("agent", agent.decoder()) decode.success(CargoSold(cargo:, transaction:, agent:)) }) _ -> api.parse_error_response(response) } } pub type ResourcesSiphoned { ResourcesSiphoned( siphon: Siphon, cooldown: Cooldown, cargo: ShipCargo, events: List(ShipConditionEvent), ) } pub fn siphon_resources( token: AgentToken, ship_symbol: ShipSymbol, ) -> ApiResponse(ResourcesSiphoned) { let request = api.post( AgentAuth(token), "/my/ships/" <> ship_symbol.to_string(ship_symbol) <> "/siphon", ) use response <- api.try_send(request) case response.status { 201 -> api.parse_data_response(response, { use siphon <- decode.field("siphon", siphon.decoder()) use cooldown <- decode.field("cooldown", cooldown.decoder()) use cargo <- decode.field("cargo", ship_cargo.decoder()) use events <- decode.field( "events", decode.list(ship_condition_event.decoder()), ) decode.success(ResourcesSiphoned(siphon:, cooldown:, cargo:, events:)) }) _ -> api.parse_error_response(response) } } pub type SurveyCreated { SurveyCreated(cooldown: Cooldown, surveys: List(Survey)) } pub fn create_survey( token: AgentToken, ship_symbol: ShipSymbol, ) -> ApiResponse(SurveyCreated) { let request = api.post( AgentAuth(token), "/my/ships/" <> ship_symbol.to_string(ship_symbol) <> "/survey", ) use response <- api.try_send(request) case response.status { 201 -> api.parse_data_response(response, { use cooldown <- decode.field("cooldown", cooldown.decoder()) use surveys <- decode.field("surveys", decode.list(survey.decoder())) decode.success(SurveyCreated(cooldown:, surveys:)) }) _ -> api.parse_error_response(response) } } pub type CargoTransferred { CargoTransferred(cargo: ShipCargo, target_cargo: ShipCargo) } pub fn transfer_cargo( token: AgentToken, ship_symbol: ShipSymbol, trade_symbol: TradeSymbol, units: Int, target_ship_symbol: ShipSymbol, ) -> ApiResponse(CargoTransferred) { let request = api.post_json( AgentAuth(token), "/my/ships/" <> ship_symbol.to_string(ship_symbol) <> "/transfer", json.object([ #("tradeSymbol", trade_symbol.encode(trade_symbol)), #("units", json.int(units)), #("shipSymbol", ship_symbol.encode(target_ship_symbol)), ]), ) use response <- api.try_send(request) case response.status { 200 -> api.parse_data_response(response, { use cargo <- decode.field("cargo", ship_cargo.decoder()) use target_cargo <- decode.field("targetCargo", ship_cargo.decoder()) decode.success(CargoTransferred(cargo:, target_cargo:)) }) _ -> api.parse_error_response(response) } } pub fn get_ship_cargo( token: AgentToken, ship_symbol: ShipSymbol, ) -> ApiResponse(ShipCargo) { let request = api.get( AgentAuth(token), "/my/ships/" <> ship_symbol.to_string(ship_symbol) <> "/cargo", ) use response <- api.try_send(request) case response.status { 200 -> api.parse_data_response(response, ship_cargo.decoder()) _ -> api.parse_error_response(response) } } pub fn get_ship_modules( token: AgentToken, ship_symbol: ShipSymbol, ) -> ApiResponse(List(ShipModule)) { let request = api.get( AgentAuth(token), "/my/ships/" <> ship_symbol.to_string(ship_symbol) <> "/modules", ) use response <- api.try_send(request) case response.status { 200 -> api.parse_data_response(response, decode.list(ship_module.decoder())) _ -> api.parse_error_response(response) } } pub type ShipModuleInstalled { ShipModuleInstalled( agent: Agent, modules: List(ShipModule), cargo: ShipCargo, transaction: ShipModificationTransaction, ) } pub fn install_ship_module( token: AgentToken, ship_symbol: ShipSymbol, module_symbol: ModuleSymbol, ) -> ApiResponse(ShipModuleInstalled) { let request = api.post_json( AgentAuth(token), "/my/ships/" <> ship_symbol.to_string(ship_symbol) <> "/modules/install", json.object([#("symbol", module_symbol.encode(module_symbol))]), ) use response <- api.try_send(request) case response.status { 201 -> api.parse_data_response(response, { use agent <- decode.field("agent", agent.decoder()) use modules <- decode.field( "modules", decode.list(ship_module.decoder()), ) use cargo <- decode.field("cargo", ship_cargo.decoder()) use transaction <- decode.field( "transaction", ship_modification_transaction.decoder(), ) decode.success(ShipModuleInstalled( agent:, modules:, cargo:, transaction:, )) }) _ -> api.parse_error_response(response) } } pub type ShipModuleRemoved { ShipModuleRemoved( agent: Agent, modules: List(ShipModule), cargo: ShipCargo, transaction: ShipModificationTransaction, ) } pub fn remove_ship_module( token: AgentToken, ship_symbol: ShipSymbol, module_symbol: ModuleSymbol, ) -> ApiResponse(ShipModuleRemoved) { let request = api.post_json( AgentAuth(token), "/my/ships/" <> ship_symbol.to_string(ship_symbol) <> "/modules/remove", json.object([#("symbol", module_symbol.encode(module_symbol))]), ) use response <- api.try_send(request) case response.status { 201 -> api.parse_data_response(response, { use agent <- decode.field("agent", agent.decoder()) use modules <- decode.field( "modules", decode.list(ship_module.decoder()), ) use cargo <- decode.field("cargo", ship_cargo.decoder()) use transaction <- decode.field( "transaction", ship_modification_transaction.decoder(), ) decode.success(ShipModuleRemoved(agent:, modules:, cargo:, transaction:)) }) _ -> api.parse_error_response(response) } } pub fn get_ship_mounts( token: AgentToken, ship_symbol: ShipSymbol, ) -> ApiResponse(List(ShipMount)) { let request = api.get( AgentAuth(token), "/my/ships/" <> ship_symbol.to_string(ship_symbol) <> "/mounts", ) use response <- api.try_send(request) case response.status { 200 -> api.parse_data_response(response, decode.list(ship_mount.decoder())) _ -> api.parse_error_response(response) } } pub type ShipMountInstalled { ShipMountInstalled( agent: Agent, mounts: List(ShipMount), cargo: ShipCargo, transaction: ShipModificationTransaction, ) } pub fn install_ship_mount( token: AgentToken, ship_symbol: ShipSymbol, mount_symbol: MountSymbol, ) -> ApiResponse(ShipMountInstalled) { let request = api.post_json( AgentAuth(token), "/my/ships/" <> ship_symbol.to_string(ship_symbol) <> "/mounts/install", json.object([#("symbol", mount_symbol.encode(mount_symbol))]), ) use response <- api.try_send(request) case response.status { 201 -> api.parse_data_response(response, { use agent <- decode.field("agent", agent.decoder()) use mounts <- decode.field("mounts", decode.list(ship_mount.decoder())) use cargo <- decode.field("cargo", ship_cargo.decoder()) use transaction <- decode.field( "transaction", ship_modification_transaction.decoder(), ) decode.success(ShipMountInstalled(agent:, mounts:, cargo:, transaction:)) }) _ -> api.parse_error_response(response) } } pub type ShipMountRemoved { ShipMountRemoved( agent: Agent, mounts: List(ShipMount), cargo: ShipCargo, transaction: ShipModificationTransaction, ) } pub fn remove_ship_mount( token: AgentToken, ship_symbol: ShipSymbol, mount_symbol: MountSymbol, ) -> ApiResponse(ShipMountRemoved) { let request = api.post_json( AgentAuth(token), "/my/ships/" <> ship_symbol.to_string(ship_symbol) <> "/mounts/remove", json.object([#("symbol", mount_symbol.encode(mount_symbol))]), ) use response <- api.try_send(request) case response.status { 201 -> api.parse_data_response(response, { use agent <- decode.field("agent", agent.decoder()) use mounts <- decode.field("mounts", decode.list(ship_mount.decoder())) use cargo <- decode.field("cargo", ship_cargo.decoder()) use transaction <- decode.field( "transaction", ship_modification_transaction.decoder(), ) decode.success(ShipMountRemoved(agent:, mounts:, cargo:, transaction:)) }) _ -> api.parse_error_response(response) } } pub fn get_ship_nav( token: AgentToken, ship_symbol: ShipSymbol, ) -> ApiResponse(ShipNav) { let request = api.get( AgentAuth(token), "/my/ships/" <> ship_symbol.to_string(ship_symbol) <> "/nav", ) use response <- api.try_send(request) case response.status { 200 -> api.parse_data_response(response, ship_nav.decoder()) _ -> api.parse_error_response(response) } } pub type ShipNavPatched { ShipNavPatched(nav: ShipNav, fuel: ShipFuel, events: List(ShipConditionEvent)) } pub fn patch_ship_nav( token: AgentToken, ship_symbol: ShipSymbol, flight_mode: ShipNavFlightMode, ) -> ApiResponse(ShipNavPatched) { let request = api.patch_json( AgentAuth(token), "/my/ships/" <> ship_symbol.to_string(ship_symbol) <> "/nav", json.object([#("flightMode", ship_nav_flight_mode.encode(flight_mode))]), ) use response <- api.try_send(request) case response.status { 200 -> api.parse_data_response(response, { use nav <- decode.field("nav", ship_nav.decoder()) use fuel <- decode.field("fuel", ship_fuel.decoder()) use events <- decode.field( "events", decode.list(ship_condition_event.decoder()), ) decode.success(ShipNavPatched(nav:, fuel:, events:)) }) _ -> api.parse_error_response(response) } } pub type Stats { Stats( accounts: Option(Int), agents: Int, ships: Int, systems: Int, waypoints: Int, ) } fn stats_decoder() -> Decoder(Stats) { use accounts <- decode.optional_field( "accounts", option.None, decode.optional(decode.int), ) use agents <- decode.field("agents", decode.int) use ships <- decode.field("ships", decode.int) use systems <- decode.field("systems", decode.int) use waypoints <- decode.field("waypoints", decode.int) decode.success(Stats(accounts:, agents:, ships:, systems:, waypoints:)) } pub type Health { Health(last_market_update: Option(Timestamp)) } fn health_decoder() -> Decoder(Health) { use last_market_update <- decode.optional_field( "lastMarketUpdate", option.None, decode.optional(time.rfc3339_timestamp_decoder()), ) decode.success(Health(last_market_update:)) } pub type CreditLeaderboardEntry { CreditLeaderboardEntry(agent_symbol: AgentSymbol, credits: Int) } fn credit_leaderboard_entry_decoder() -> Decoder(CreditLeaderboardEntry) { use agent_symbol <- decode.field("agentSymbol", agent_symbol.decoder()) use credits <- decode.field("credits", decode.int) decode.success(CreditLeaderboardEntry(agent_symbol:, credits:)) } pub type ChartLeaderboardEntry { ChartLeaderboardEntry(agent_symbol: AgentSymbol, chart_count: Int) } fn chart_leaderboard_entry_decoder() -> Decoder(ChartLeaderboardEntry) { use agent_symbol <- decode.field("agentSymbol", agent_symbol.decoder()) use chart_count <- decode.field("chartCount", decode.int) decode.success(ChartLeaderboardEntry(agent_symbol:, chart_count:)) } pub type Leaderboards { Leaderboards( most_credits: List(CreditLeaderboardEntry), most_submitted_charts: List(ChartLeaderboardEntry), ) } fn leaderboards_decoder() -> Decoder(Leaderboards) { use most_credits <- decode.field( "mostCredits", decode.list(credit_leaderboard_entry_decoder()), ) use most_submitted_charts <- decode.field( "mostSubmittedCharts", decode.list(chart_leaderboard_entry_decoder()), ) decode.success(Leaderboards(most_credits:, most_submitted_charts:)) } pub type ServerResets { ServerResets(next: Timestamp, frequency: String) } fn server_resets_decoder() -> Decoder(ServerResets) { use next <- decode.field("next", time.rfc3339_timestamp_decoder()) use frequency <- decode.field("frequency", decode.string) decode.success(ServerResets(next:, frequency:)) } pub type Announcement { Announcement(title: String, body: String) } fn announcement_decoder() -> Decoder(Announcement) { use title <- decode.field("title", decode.string) use body <- decode.field("body", decode.string) decode.success(Announcement(title:, body:)) } pub type Link { Link(name: String, url: Uri) } fn link_decoder() -> Decoder(Link) { use name <- decode.field("name", decode.string) use url <- decode.field("url", { use value <- decode.then(decode.string) case uri.parse(value) { Ok(time) -> decode.success(time) Error(Nil) -> decode.failure(uri.empty, "Uri") } }) decode.success(Link(name:, url:)) } pub type ServerStatus { ServerStatus( status: String, version: String, reset_date: Date, description: String, stats: Stats, health: Health, leaderboards: Leaderboards, server_resets: ServerResets, announcements: List(Announcement), links: List(Link), ) } fn server_status_decoder() -> Decoder(ServerStatus) { use status <- decode.field("status", decode.string) use version <- decode.field("version", decode.string) use reset_date <- decode.field("resetDate", time.rfc3339_date_decoder()) use description <- decode.field("description", decode.string) use stats <- decode.field("stats", stats_decoder()) use health <- decode.field("health", health_decoder()) use leaderboards <- decode.field("leaderboards", leaderboards_decoder()) use server_resets <- decode.field("serverResets", server_resets_decoder()) use announcements <- decode.field( "announcements", decode.list(announcement_decoder()), ) use links <- decode.field("links", decode.list(link_decoder())) decode.success(ServerStatus( status:, version:, reset_date:, description:, stats:, health:, leaderboards:, server_resets:, announcements:, links:, )) } pub fn get_server_status() -> ApiResponse(ServerStatus) { let request = api.get(NoAuth, "/") use response <- api.try_send(request) case response.status { 200 -> api.parse_response(response, server_status_decoder()) _ -> api.parse_error_response(response) } } pub type ErrorCode { ErrorCode(code: Int, name: String) } fn error_code_decoder() -> Decoder(ErrorCode) { use code <- decode.field("code", decode.int) use name <- decode.field("name", decode.string) decode.success(ErrorCode(code:, name:)) } pub fn list_error_codes() -> ApiResponse(List(ErrorCode)) { let request = api.get(NoAuth, "/error-codes") use response <- api.try_send(request) case response.status { 200 -> api.parse_response( response, decode.field( "errorCodes", decode.list(error_code_decoder()), decode.success, ), ) _ -> api.parse_error_response(response) } } pub fn list_systems( page: Option(Int), limit: Option(Int), ) -> ApiResponse(PagedData(List(System))) { let request = api.get_page(NoAuth, "/systems", page, limit) use response <- api.try_send(request) case response.status { 200 -> api.parse_paged_data_response(response, decode.list(system.decoder())) _ -> api.parse_error_response(response) } } pub fn get_system(system_symbol: SystemSymbol) -> ApiResponse(System) { let request = api.get(NoAuth, "/systems/" <> system_symbol.to_string(system_symbol)) use response <- api.try_send(request) case response.status { 200 -> api.parse_data_response(response, system.decoder()) _ -> api.parse_error_response(response) } } pub fn list_system_waypoints( system_symbol: SystemSymbol, page: Option(Int), limit: Option(Int), type_: Option(WaypointType), traits: List(WaypointTraitSymbol), ) -> ApiResponse(PagedData(List(Waypoint))) { let query = list.map(traits, fn(trait) { #("traits", waypoint_trait_symbol.to_string(trait)) }) let query = case type_ { option.Some(type_) -> [#("type", waypoint_type.to_string(type_)), ..query] option.None -> query } let request = api.get_page_with_query( NoAuth, "/systems/" <> system_symbol.to_string(system_symbol) <> "/waypoints", page, limit, query, ) use response <- api.try_send(request) case response.status { 200 -> api.parse_paged_data_response(response, decode.list(waypoint.decoder())) _ -> api.parse_error_response(response) } } pub fn get_waypoint( system_symbol: SystemSymbol, waypoint_symbol: WaypointSymbol, ) -> ApiResponse(Waypoint) { let request = api.get( NoAuth, "/systems/" <> system_symbol.to_string(system_symbol) <> "/waypoints/" <> waypoint_symbol.to_string(waypoint_symbol), ) use response <- api.try_send(request) case response.status { 200 -> api.parse_data_response(response, waypoint.decoder()) _ -> api.parse_error_response(response) } } pub fn get_construction_site( system_symbol: SystemSymbol, waypoint_symbol: WaypointSymbol, ) -> ApiResponse(Construction) { let request = api.get( NoAuth, "/systems/" <> system_symbol.to_string(system_symbol) <> "/waypoints/" <> waypoint_symbol.to_string(waypoint_symbol) <> "/construction", ) use response <- api.try_send(request) case response.status { 200 -> api.parse_data_response(response, construction.decoder()) _ -> api.parse_error_response(response) } } pub type ConstructionSiteSupplied { ConstructionSiteSupplied(construction: Construction, cargo: ShipCargo) } pub fn supply_construction_site( token: AgentToken, system_symbol: SystemSymbol, waypoint_symbol: WaypointSymbol, ship_symbol: ShipSymbol, trade_symbol: TradeSymbol, units: Int, ) -> ApiResponse(ConstructionSiteSupplied) { let request = api.post_json( AgentAuth(token), "/systems/" <> system_symbol.to_string(system_symbol) <> "/waypoints/" <> waypoint_symbol.to_string(waypoint_symbol) <> "/construction/supply", json.object([ #("shipSymbol", ship_symbol.encode(ship_symbol)), #("tradeSymbol", trade_symbol.encode(trade_symbol)), #("units", json.int(units)), ]), ) use response <- api.try_send(request) case response.status { 201 -> api.parse_data_response(response, { use construction <- decode.field("construction", construction.decoder()) use cargo <- decode.field("cargo", ship_cargo.decoder()) decode.success(ConstructionSiteSupplied(construction:, cargo:)) }) _ -> api.parse_error_response(response) } } pub fn get_market( system_symbol: SystemSymbol, waypoint_symbol: WaypointSymbol, ) -> ApiResponse(Market) { let request = api.get( NoAuth, "/systems/" <> system_symbol.to_string(system_symbol) <> "/waypoints/" <> waypoint_symbol.to_string(waypoint_symbol) <> "/market", ) use response <- api.try_send(request) case response.status { 200 -> api.parse_data_response(response, market.decoder()) _ -> api.parse_error_response(response) } } pub fn get_jump_gate( system_symbol: SystemSymbol, waypoint_symbol: WaypointSymbol, ) -> ApiResponse(JumpGate) { let request = api.get( NoAuth, "/systems/" <> system_symbol.to_string(system_symbol) <> "/waypoints/" <> waypoint_symbol.to_string(waypoint_symbol) <> "/jump-gate", ) use response <- api.try_send(request) case response.status { 200 -> api.parse_data_response(response, jump_gate.decoder()) _ -> api.parse_error_response(response) } } pub fn get_shipyard( system_symbol: SystemSymbol, waypoint_symbol: WaypointSymbol, ) -> ApiResponse(Shipyard) { let request = api.get( NoAuth, "/systems/" <> system_symbol.to_string(system_symbol) <> "/waypoints/" <> waypoint_symbol.to_string(waypoint_symbol) <> "/shipyard", ) use response <- api.try_send(request) case response.status { 200 -> api.parse_data_response(response, shipyard.decoder()) _ -> api.parse_error_response(response) } }