From b28355d182493620cc90415300ef2ffdbc1a6a4e Mon Sep 17 00:00:00 2001 From: Jonathan Harris Date: Sun, 17 Dec 2017 18:56:25 +0000 Subject: [PATCH] Merge EDSM-NET/master Squashed commit of the following: commit a13a5c431396a131aad21927f2b4cc43b948d417 Author: AnthorNet Date: Sun Dec 17 16:51:58 2017 +0100 Ready for testing commit 3e5915075b20e824580e5c3d879bce467dcb8b8d Author: AnthorNet Date: Sat Dec 16 21:47:22 2017 +0100 Fix message json encoding commit 868030578422f9c8568d5ba21d6f3aa132c01232 Author: AnthorNet Date: Sat Dec 16 17:54:10 2017 +0100 Fix double Dict conversion commit 7693031f6f09c7119c51f91abbed7241e5ad6a1c Author: AnthorNet Date: Sat Dec 16 17:31:19 2017 +0100 Fix transient state format commit 54c96e31774ba5bfa2d19ac45b07d355dbefe46b Author: AnthorNet Date: Sat Dec 16 17:25:02 2017 +0100 Fix calling discarded event callback commit 24d006947f3bbb6607f893042d783c9232402c28 Author: AnthorNet Date: Fri Dec 15 15:11:28 2017 +0100 Retry Discarded Event list if empty commit 4defedba965489d2d0c009189a193f9ecabc84fd Author: AnthorNet Date: Fri Dec 15 14:43:28 2017 +0100 Prepare API migration --- plugins/edsm.py | 244 ++++++++++++++---------------------------------- 1 file changed, 70 insertions(+), 174 deletions(-) diff --git a/plugins/edsm.py b/plugins/edsm.py index 4306b382..b7799fba 100644 --- a/plugins/edsm.py +++ b/plugins/edsm.py @@ -25,20 +25,19 @@ import plug if __debug__: from traceback import print_exc -EDSM_POLL = 0.1 -_TIMEOUT = 20 -FAKE = ['CQC', 'Training', 'Destination'] # Fake systems that shouldn't be sent to EDSM +EDSM_POLL = 0.1 +_TIMEOUT = 20 +FAKE = ['CQC', 'Training', 'Destination'] # Fake systems that shouldn't be sent to EDSM -this = sys.modules[__name__] # For holding module globals -this.session = requests.Session() -this.queue = Queue() # Items to be sent to EDSM by worker thread -this.lastship = None # Description of last ship that we sent to EDSM -this.lastlookup = False # whether the last lookup succeeded +this = sys.modules[__name__] # For holding module globals +this.session = requests.Session() +this.queue = Queue() # Items to be sent to EDSM by worker thread +this.discardedEvents = [] # List discarded events from EDSM +this.lastlookup = False # whether the last lookup succeeded # Game state -this.multicrew = False # don't send captain's ship info to EDSM while on a crew - +this.multicrew = False # don't send captain's ship info to EDSM while on a crew def plugin_start(): # Can't be earlier since can only call PhotoImage after window is created @@ -67,6 +66,10 @@ def plugin_start(): this.thread = Thread(target = worker, name = 'EDSM worker') this.thread.daemon = True this.thread.start() + + # Get the last Discarded events from EDSM + if __debug__: print('Calling Discarded Events API endpoint') + this.queue.put(('https://www.edsm.net/api-journal-v1/discard', {}, discarded_callback)) return 'EDSM' @@ -193,56 +196,24 @@ def journal_entry(cmdr, is_beta, system, station, entry, state): this.system.update_idletasks() this.multicrew = bool(state['Role']) + + # If Discarded events still emtpy, retry + if not this.discardedEvents: + this.queue.put(('https://www.edsm.net/api-journal-v1/discard', {}, discarded_callback)) # Send interesting events to EDSM if config.getint('edsm_out') and not is_beta and not this.multicrew and credentials(cmdr): try: - # Send credits to EDSM on new game (but not on startup - data might be old) - if entry['event'] == 'LoadGame': - setcredits(cmdr, state['Credits'], state['Loan']) - - # Send rank info to EDSM on startup or change - if entry['event'] in ['StartUp', 'Progress', 'Promotion'] and state['Rank']: - setranks(cmdr, state['Rank']) - - # Send ship info to EDSM on startup or change - if entry['event'] in ['StartUp', 'Loadout', 'LoadGame', 'SetUserShipName'] and cmdr and state['ShipID'] is not None: - setshipid(cmdr, state['ShipID']) - props = [] - if state['ShipIdent'] is not None: - props.append(('shipIdent', state['ShipIdent'])) - if state['ShipName'] is not None: - props.append(('shipName', state['ShipName'])) - if state['PaintJob'] is not None: - props.append(('paintJob', state['PaintJob'])) - updateship(cmdr, state['ShipID'], state['ShipType'], props) - elif entry['event'] in ['ShipyardBuy', 'ShipyardSell', 'SellShipOnRebuy', 'ShipyardSwap']: - sellship(cmdr, entry.get('SellShipID')) - - # Send cargo to EDSM on startup or change - if entry['event'] in (['StartUp', 'LoadGame', 'CollectCargo', 'EjectCargo', 'MarketBuy', 'MarketSell', - 'MiningRefined', 'EngineerContribution'] or - (entry['event'] == 'MissionCompleted' and entry.get('CommodityReward'))): - setcargo(cmdr, state['Cargo']) - - # Send materials info to EDSM on startup or change - if entry['event'] in ['StartUp', 'LoadGame', 'MaterialCollected', 'MaterialDiscarded', 'ScientificResearch', 'EngineerCraft', 'Synthesis']: - setmaterials(cmdr, state['Raw'], state['Manufactured'], state['Encoded']) - - # Send paintjob info to EDSM on change - if entry['event'] in ['ModuleBuy', 'ModuleSell'] and entry['Slot'] == 'PaintJob': - updateship(cmdr, state['ShipID'], state['ShipType'], [('paintJob', state['PaintJob'])]) - - # Write EDSM log on startup and change - if system and entry['event'] in ['Location', 'FSDJump']: - this.lastlookup = False - writelog(cmdr, timegm(time.strptime(entry['timestamp'], '%Y-%m-%dT%H:%M:%SZ')), system, 'StarPos' in entry and tuple(entry['StarPos']), state['ShipID']) + # Send the journal entry if not in the discarded events + if entry['event'] not in this.discardedEvents: + sendEntry(cmdr, system, station, entry, state) except Exception as e: if __debug__: print_exc() return unicode(e) +# Update system data def cmdr_data(data, is_beta): system = data['lastSystem']['name'] @@ -258,46 +229,6 @@ def cmdr_data(data, is_beta): this.lastlookup = False this.system.update_idletasks() - if config.getint('edsm_out') and not is_beta and not this.multicrew and credentials(data['commander']['name']): - # Send flightlog to EDSM if FSDJump failed to do so - if not this.lastlookup: - try: - this.writelog(data['commander']['name'], int(time.time()), system, None, data['ship']['id']) - except Exception as e: - if __debug__: print_exc() - return unicode(e) - - # Update credits and ship info and send to EDSM - try: - if data['commander'].get('credits') is not None: - setcredits(data['commander']['name'], data['commander']['credits'], data['commander'].get('debt', 0)) - ship = companion.ship(data) - if ship != this.lastship: - cargo = 0 - fuel = 0 - for v in data['ship']['modules'].itervalues(): - module = outfitting.lookup(v['module'], companion.ship_map) - if not module: - pass - elif 'Fuel Tank'in module['name']: - fuel += 2**int(module['class']) - elif 'Cargo Rack' in module['name']: - cargo += 2**int(module['class']) - - updateship(data['commander']['name'], - data['ship']['id'], - data['ship']['name'].lower(), - { - 'cargoCapacity': cargo, - 'fuelMainCapacity': fuel, - 'linkToCoriolis': coriolis.url(data, is_beta), - 'linkToEDShipyard': edshipyard.url(data, is_beta), - }) - this.lastship = ship - except Exception as e: - # Not particularly important so silent on failure - if __debug__: print_exc() - # Worker thread def worker(): @@ -311,16 +242,21 @@ def worker(): retrying = 0 while retrying < 3: try: + #if __debug__: print(data) r = this.session.post(url, data=data, timeout=_TIMEOUT) r.raise_for_status() reply = r.json() - (msgnum, msg) = reply['msgnum'], reply['msg'] + if callback: callback(reply) - elif msgnum // 100 != 1: # 1xx = OK, 2xx = fatal error - plug.show_error(_('Error: EDSM {MSG}').format(MSG=msg)) + else: + (msgnum, msg) = reply['msgnum'], reply['msg'] + if msgnum // 100 not in (1,4): + if __debug__: print(_('Error: EDSM {MSG}').format(MSG=msg)) + plug.show_error(_('Error: EDSM {MSG}').format(MSG=msg)) break except: + if __debug__: print_exc() retrying += 1 else: if callback: @@ -329,37 +265,46 @@ def worker(): plug.show_error(_("Error: Can't connect to EDSM")) -# Queue a call to an EDSM endpoint with args (which should be quoted) -def call(cmdr, endpoint, args, callback=None): - (username, apikey) = credentials(cmdr) - args = dict(args) - args['commanderName'] = username - args['apiKey'] = apikey - args['fromSoftware'] = applongname - args['fromSoftwareVersion'] =appversion - this.queue.put(('https://www.edsm.net/%s' % endpoint, args, callback)) +# Queue a call to the EDSM journal API with args (which should be quoted) +def call(cmdr, args, callback=None): + (username, apikey) = credentials(cmdr) + + args = dict(args) + args['commanderName'] = username + args['apiKey'] = apikey + args['fromSoftware'] = applongname + args['fromSoftwareVersion'] = appversion + + this.queue.put(('https://www.edsm.net/api-journal-v1', args, callback)) -# Send flight log and also do lookup -def writelog(cmdr, timestamp, system_name, coordinates, shipid = None): - - if system_name in FAKE: +# Send journal entry +def sendEntry(cmdr, system, station, entry, state): + if entry['event'] in this.discardedEvents: return + + if entry['event'] in ['Location', 'FSDJump'] and entry['StarSystem'] in FAKE: + return + + # Update callback on needed events + if entry['event'] in ['Location', 'FSDJump']: + eventCallback = writelog_callback + else: + eventCallback = null_callback + + # Introduce transient states into the event + entry['_systemName'] = system; + #entry['_systemCoordinates'] = gameStatus.coordinates; #TODO: Track system coordinates + entry['_stationName'] = station; + entry['_shipId'] = state['ShipID']; + + # Make the API call + call(cmdr, { 'message': json.dumps(entry) }, eventCallback) - args = { - 'systemName': system_name, - 'dateVisited': time.strftime('%Y-%m-%d %H:%M:%S', time.gmtime(timestamp)), - } - if coordinates: - args['x'] = '%.3f' % coordinates[0] - args['y'] = '%.3f' % coordinates[1] - args['z'] = '%.3f' % coordinates[2] - if shipid is not None: - args['shipId'] = '%d' % shipid - call(cmdr, 'api-logs-v1/set-log', args, writelog_callback) def writelog_callback(reply): - this.lastlookup = reply + #if __debug__: print(reply) + this.lastlookup = reply['events'][0] # Get first response while we send events one by one this.system.event_generate('<>', when="tail") # calls update_status in main thread def update_status(event=None): @@ -380,60 +325,11 @@ def update_status(event=None): # When we don't care about return msgnum from EDSM def null_callback(reply): if not reply: - plug.show_error(_("Error: Can't connect to EDSM")) + plug.show_error(_("Error: Can't connect to EDSM"))# When we don't care about return msgnum from EDSM - -def setranks(cmdr, ranks): - args = {} - if ranks: - for k,v in ranks.iteritems(): - if v is not None: - args[k] = '%d;%d' % v - if args: - call(cmdr, 'api-commander-v1/set-ranks', args) - -def setcredits(cmdr, balance, loan): - if balance is not None: - args = { - 'balance': '%d' % balance, - 'loan': '%d' % loan, - } - call(cmdr, 'api-commander-v1/set-credits', args) - -def setcargo(cmdr, cargo): - args = { - 'type': 'cargo', - 'values': json.dumps(cargo, separators = (',', ':')), - } - call(cmdr, 'api-commander-v1/set-materials', args) - -def setmaterials(cmdr, raw, manufactured, encoded): - args = { - 'type': 'data', - 'values': json.dumps(encoded, separators = (',', ':')), - } - call(cmdr, 'api-commander-v1/set-materials', args) - - materials = {} - materials.update(raw) - materials.update(manufactured) - args = { - 'type': 'materials', - 'values': json.dumps(materials, separators = (',', ':')), - } - call(cmdr, 'api-commander-v1/set-materials', args) - -def setshipid(cmdr, shipid): - if shipid is not None: - call(cmdr, 'api-commander-v1/set-ship-id', { 'shipId': '%d' % shipid }) - -def updateship(cmdr, shipid, shiptype, args={}): - if shipid is not None and shiptype: - args = dict(args) - args['shipId'] = '%d' % shipid - args['type'] = shiptype - call(cmdr, 'api-commander-v1/update-ship', args) - -def sellship(cmdr, shipid): - if shipid is not None: - call(cmdr, 'api-commander-v1/sell-ship', { 'shipId': '%d' % shipid }, null_callback) +# Grab the discarded list +def discarded_callback(reply): + if not reply: + plug.show_error(_("Error: Can't get EDSM DIscarded Events")) + else: + this.discardedEvents = reply \ No newline at end of file