diff --git a/main.py b/main.py index 3e6f647..013be4b 100644 --- a/main.py +++ b/main.py @@ -2,7 +2,7 @@ import argparse from nullptr.commander import Commander def main(args): - c = Commander(args.store_dir, args.agent) + c = Commander(args.store_dir) c.run() # X1-AG74-41076A @@ -11,6 +11,5 @@ def main(args): if __name__ == '__main__': parser = argparse.ArgumentParser() parser.add_argument('-s', '--store-dir', default='data') - parser.add_argument('-a', '--agent', default=None) args = parser.parse_args() main(args) diff --git a/nullptr/api.py b/nullptr/api.py index 26a3320..6fc7d6e 100644 --- a/nullptr/api.py +++ b/nullptr/api.py @@ -3,6 +3,7 @@ from nullptr.models.system import System from nullptr.models.waypoint import Waypoint from nullptr.models.marketplace import Marketplace from nullptr.models.jumpgate import Jumpgate +from nullptr.models.ship import Ship from .util import * from time import sleep class ApiError(Exception): @@ -17,7 +18,9 @@ class Api: def __init__(self, store, agent): self.agent = agent self.store = store + self.requests_sent = 0 self.meta = None + self.last_result = None self.root = 'https://api.spacetraders.io/v2/' def token(self): @@ -37,6 +40,7 @@ class Api: headers = {} if need_token: headers['Authorization'] = 'Bearer ' + self.token() + self.requests_sent += 1 if method == 'get': params['limit'] = 20 r = requests.request(method, self.root+path, json=data, headers=headers, params=params) @@ -93,4 +97,8 @@ class Api: symbol = str(waypoint) return self.store.update(Jumpgate, symbol, data) + def list_ships(self): + data = self.request('get', 'my/ships') + return self.store.update_list(Ship, data) + diff --git a/nullptr/commander.py b/nullptr/commander.py index 87ab44b..9a30b54 100644 --- a/nullptr/commander.py +++ b/nullptr/commander.py @@ -14,11 +14,11 @@ from threading import Thread from nullptr.atlas_builder import AtlasBuilder class Commander(CommandLine): - def __init__(self, store_dir='data', agent=None): + def __init__(self, store_dir='data'): self.store_dir = store_dir self.store = Store(store_dir) self.store.load() - self.agent = self.select_agent(agent) + self.agent = self.select_agent() self.api = Api(self.store, self.agent) self.atlas_builder = AtlasBuilder(self.store, self.api) self.analyzer = Analyzer(self.store) @@ -35,16 +35,13 @@ class Commander(CommandLine): print('not found') return obj - def select_agent(self, agent_str): - if agent_str is not None: - return self.store.get(Agent, agent_str) - else: - agents = self.store.all(Agent) - agent = next(agents, None) - if agent is None: - symbol = input('agent name: ') - agent = self.store.get(Agent, symbol) - return agent + def select_agent(self): + agents = self.store.all(Agent) + agent = next(agents, None) + if agent is None: + symbol = input('agent name: ') + agent = self.store.get(Agent, symbol) + return agent def after_cmd(self): self.store.flush() @@ -94,3 +91,13 @@ class Commander(CommandLine): # dest = self.store.get(System, 'X1-DA90') path = self.analyzer.find_path(orig, dest) pprint(path) + + def do_ships(self, arg=''): + if arg.startswith('r'): + r = self.api.list_ships() + else: + r = list(self.store.all('Ship')) + pprint(r) + + def do_pp(self): + pprint(self.api.last_result) diff --git a/nullptr/models/ship.py b/nullptr/models/ship.py new file mode 100644 index 0000000..a91eb17 --- /dev/null +++ b/nullptr/models/ship.py @@ -0,0 +1,111 @@ +from .base import Base +from time import time +from nullptr.util import * +from dataclasses import dataclass + +@dataclass +class InventoryItem: + symbol: str + units: int + def __str__(self): + return self.symbol + ': ' + str(self.units) + +class Ship(Base): + cargo:dict = {} + mission_state:dict = {} + status:str = '' + cargo_capacity:int = 0 + cargo_units:int = 0 + location_str = '' + cooldown:int = 0 + arrival:int = 0 + fuel_current:int = 0 + fuel_capacity:int = 0 + mission:str = None + mission_status:str = 'init' + + @classmethod + def ext(self): + return 'shp' + + def location(self): + return self.store.get('Waypoint', self.location_str) + + def path(self): + agent = self.symbol.split('-')[0] + return f'{agent}/{self.symbol}.{self.ext()}' + + def update(self, d): + self.seta('status', d, 'nav.status') + self.seta('location_str', d, 'nav.waypointSymbol') + + self.seta('cargo_capacity', d, 'cargo.capacity') + self.seta('cargo_units', d, 'cargo.units') + self.seta('fuel_capacity', d, 'fuel.capacity') + self.seta('fuel_current', d,'fuel.current') + cargo = sg(d, 'cargo.inventory') + if cargo is not None: + self.load_cargo(cargo) + cooldown = sg(d, 'cooldown.expiration') + if cooldown: + self.cooldown = parse_timestamp(cooldown) + arrival = sg(d, 'nav.route.arrival') + if arrival: + self.arrival = parse_timestamp(arrival) + + def tick(self): + if self.status == 'IN_TRANSIT' and self.arrival < time(): + self.status = 'IN_ORBIT' + + def is_cooldown(self): + return self.cooldown > time() + + def is_travelling(self): + return self.status == 'IN_TRANSIT' + + def get_cargo(self, typ): + if typ not in self.cargo: + return 0 + return self.cargo[typ].units + + def load_cargo(self, cargo): + result = {} + for i in cargo: + symbol = must_get(i, 'symbol') + units = must_get(i, 'units') + result[symbol] = InventoryItem(symbol, units) + self.cargo = result + + def deliverable_cargo(self, contract): + result = [] + for d in contract.deliveries: + if self.get_cargo(d.trade_symbol) > 0: + result.append(d.trade_symbol) + return result + + def nondeliverable_cargo(self, contract): + cargo = [c.symbol for c in self.cargo.values()] + deliveries = [d.trade_symbol for d in contract.deliveries] + garbage = [c for c in cargo if c not in deliveries] + return garbage + + def update_timers(self): + if self.status == 'IN_TRANSIT' and self.arrival < time(): + self.status = 'IN_ORBIT' + + + def f(self, detail=1): + self.update_timers() + arrival = int(self.arrival - time()) + cooldown = int(self.cooldown - time()) + r = self.symbol + if detail > 1: + r += ' ' + self.status + r += f' [{self.fuel_current}/{self.fuel_capacity}]' + r += ' ' + str(self.location()) + if self.is_travelling(): + r += f' [A: {arrival}]' + if self.is_cooldown(): + r += f' [C: {cooldown}]' + return r + diff --git a/nullptr/store.py b/nullptr/store.py index 3ac660d..dc436b6 100644 --- a/nullptr/store.py +++ b/nullptr/store.py @@ -6,6 +6,7 @@ from nullptr.models.agent import Agent from nullptr.models.marketplace import Marketplace from nullptr.models.system_member import SystemMember from nullptr.models.jumpgate import Jumpgate +from nullptr.models.ship import Ship from os.path import isfile, dirname, isdir import os from os.path import basename @@ -24,6 +25,7 @@ class Store: def init_models(self): self.models = all_subclasses(Base) self.extensions = {c.ext(): c for c in self.models} + self.model_names = {c.__name__: c for c in self.models} def dirty(self, obj): self.dirty_objects.add(obj) @@ -78,6 +80,9 @@ class Store: return obj def get(self, typ, symbol, create=False): + if type(typ) == str and typ in self.model_names: + typ = self.model_names[typ] + if typ not in self.data: return None if symbol not in self.data[typ]: @@ -96,6 +101,9 @@ class Store: return [self.update(typ, mg(d, 'symbol'), d) for d in lst] def all(self, typ): + if type(typ) == str and typ in self.model_names: + typ = self.model_names[typ] + for m in self.data[typ].values(): yield m