1
0
mirror of https://github.com/EDCD/EDMarketConnector.git synced 2025-04-13 15:57:14 +03:00

Merge pull request #1747 from EDCD/enhancement/1728/capi-for-legacy

CAPI: Enable use of Legacy host when applicable.
This commit is contained in:
Athanasius 2022-12-14 10:42:05 +00:00 committed by GitHub
commit 605ad07aac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 121 additions and 42 deletions

View File

@ -2,4 +2,8 @@
follow_imports = skip
ignore_missing_imports = True
scripts_are_modules = True
; Without this bare `mypy <file>` may get warnings for e.g.
; `<var> = <value>`
; i.e. no typing info.
check-untyped-defs = True
; platform = darwin

View File

@ -16,7 +16,7 @@ from builtins import object, str
from os import chdir, environ
from os.path import dirname, join
from time import localtime, strftime, time
from typing import TYPE_CHECKING, Optional, Tuple, Union
from typing import TYPE_CHECKING, Literal, Optional, Tuple, Union
# Have this as early as possible for people running EDMarketConnector.exe
# from cmd.exe or a bat file or similar. Else they might not be in the correct
@ -1013,11 +1013,6 @@ class AppWindow(object):
self.status['text'] = _('CAPI query aborted: GameVersion unknown')
return
if not monitor.is_live_galaxy():
logger.warning("Dropping CAPI request because this is the Legacy galaxy, which is not yet supported")
self.status['text'] = 'CAPI for Legacy not yet supported'
return
if not monitor.system:
logger.trace_if('capi.worker', 'Aborting Query: Current star system unknown')
# LANG: CAPI queries aborted because current star system name unknown
@ -1210,8 +1205,7 @@ class AppWindow(object):
monitor.state['Loan'] = capi_response.capi_data['commander'].get('debt', 0)
# stuff we can do when not docked
# TODO: Use plug.notify_capi_legacy if Legacy host
err = plug.notify_newdata(capi_response.capi_data, monitor.is_beta)
err = plug.notify_capidata(capi_response.capi_data, monitor.is_beta)
self.status['text'] = err and err or ''
if err:
play_bad = True
@ -1357,7 +1351,7 @@ class AppWindow(object):
# Ensure the ship type/name text is clickable, if it should be.
if monitor.state['Modules']:
ship_state = True
ship_state: Literal['normal', 'disabled'] = tk.NORMAL
else:
ship_state = tk.DISABLED

View File

@ -98,6 +98,12 @@ liable to change without notice.
`from prefs import prefsVersion` - to allow for versioned preferences.
`from companion import CAPIData, SERVER_LIVE, SERVER_LEGACY, SERVER_BETA` -
`CAPIData` is the actual type of `data` as passed into `cmdr_data()` and
`cmdr_data_legacy()`.
See [Commander Data from Frontier CAPI](#commander-data-from-frontier-capi))
for further information.
`import edmc_data` (or specific 'from' imports) - This contains various static
data that used to be in other files. You should **not** now import anything
from the original files unless specified as allowed in this section.
@ -129,11 +135,15 @@ modules for plugin use:
- sqlite3
- zipfile
And, of course, anything in the [Python Standard Library](https://docs.python.org/3/library/)
will always be available, dependent on the version of Python we're using to
build Windows installed versions. Check the 'Startup' line in an application
[Debug Log File](https://github.com/EDCD/EDMarketConnector/wiki/Troubleshooting#debug-log-files)
for the version of Python being used.
Unfortunately we cannot promise to include every part of the
[Python Standard Library](https://docs.python.org/3/library/) due to issues
with correctly detecting all the modules, and if they're single file or a
package, and perhaps have sub-modules. For now, if you find something is
missing that you need for your plugin, ask us to add it in, and we'll do so on
a 'best efforts' basis.
See [#1327 - ModuleNotFound when creating a new plugin.](https://github.com/EDCD/EDMarketConnector/issues/1327)
for some discussion.
---
@ -899,8 +909,14 @@ constants.
---
### Commander Data from Frontier CAPI
If a plugin has a `cmdr_data()` function it gets called when the application
has just fetched fresh Cmdr and station data from Frontier's servers, **but not
for the Legacy galaxy**. See `cmdr_data_legacy()` below for Legacy data
handling.
```python
from companion import CAPIData, SERVER_LIVE, SERVER_LEGACY, SERVER_BETA
def cmdr_data(data, is_beta):
"""
We have new data on our commander
@ -909,18 +925,58 @@ def cmdr_data(data, is_beta):
raise ValueError("this isn't possible")
logger.info(data['commander']['name'])
# Determining source galaxy for the data
if data.source_host == SERVER_LIVE:
...
elif data.source_host == SERVER_BETA:
...
elif data.source_host == SERVER_LEGACY:
...
```
| Parameter | Type | Description |
| :-------- | :--------------: | :------------------------------------------------------------------------------------------------------- |
| `data` | `CAPIData` | `/profile` API response, with `/market` and `/shipyard` added under the keys `marketdata` and `shipdata` |
| `is_beta` | `bool` | If the game is currently in beta |
`CAPIData` is a class, which you can `from companion import CAPIDATA`, and is
based on `UserDict`. The actual data from CAPI queries is thus accessible
via python's normal `data['key']` syntax. However, being a class, it can also
have extra properties, such as `source_host`, as shown above. Plugin authors
are free to use *that* property, **but MUST NOT rely on any other extra
properties present in `CAPIData`, they are for internal use only.**
#### CAPI data for Legacy
When CAPI data has been retrieved from the separate CAPI host for the Legacy
galaxy, because the Journal gameversion indicated the player is playing/last
played in that galaxy, a different function will be called,
`cmdr_data_legacy()`.
```python
def cmdr_data_legacy(data, is_beta):
"""
We have new data on our commander
"""
if data.get('commander') is None or data['commander'].get('name') is None:
raise ValueError("this isn't possible")
logger.info(data['commander']['name'])
```
This gets called when the application has just fetched fresh Cmdr and station
data from Frontier's servers.
**IF AND ONLY IF** your code definitely handles the Live/Legacy split itself
then you *may* simply:
| Parameter | Type | Description |
| :-------- | :--------------: | :------------------------------------------------------------------------------------------------------- |
| `data` | `Dict[str, Any]` | `/profile` API response, with `/market` and `/shipyard` added under the keys `marketdata` and `shipdata` |
| `is_beta` | `bool` | If the game is currently in beta |
NB: Actually `data` is a custom type, based on `UserDict`, called `CAPIData`,
and has some extra properties. However, these are for **internal use only**
at this time, especially as there are some caveats about at least one of them.
```python
def cmdr_data_legacy(data, is_beta):
return cmdr_data(data, is_beta)
```
The core 'eddn' plugin might contain some useful hints about how to handle the
split **but do not rely on any extra properties on `data` unless they are
documented in [Available imports](#available-imports) in this document**.
---

View File

@ -44,9 +44,6 @@ else:
UserDict = collections.UserDict # type: ignore # Otherwise simply use the actual class
# Define custom type for the dicts that hold CAPI data
# CAPIData = NewType('CAPIData', Dict)
capi_query_cooldown = 60 # Minimum time between (sets of) CAPI queries
capi_default_requests_timeout = 10
auth_timeout = 30 # timeout for initial auth
@ -67,8 +64,8 @@ class CAPIData(UserDict):
def __init__(
self,
data: Union[str, Dict[str, Any], 'CAPIData', None] = None,
source_host: str = None,
source_endpoint: str = None
source_host: Optional[str] = None,
source_endpoint: Optional[str] = None
) -> None:
if data is None:
super().__init__()
@ -652,7 +649,7 @@ class Session(object):
self.requests_session.headers['User-Agent'] = user_agent
self.state = Session.STATE_OK
def login(self, cmdr: str = None, is_beta: Optional[bool] = None) -> bool:
def login(self, cmdr: Optional[str] = None, is_beta: Optional[bool] = None) -> bool:
"""
Attempt oAuth2 login.
@ -763,10 +760,6 @@ class Session(object):
:return: The resulting CAPI data, of type CAPIData.
"""
capi_data: CAPIData
if capi_host == SERVER_LEGACY:
logger.warning("Dropping CAPI request because this is the Legacy galaxy")
return capi_data
try:
logger.trace_if('capi.worker', 'Sending HTTP request...')
if conf_module.capi_pretend_down:
@ -921,7 +914,6 @@ class Session(object):
break
logger.trace_if('capi.worker', f'Processing query: {query.endpoint}')
capi_data: CAPIData
try:
if query.endpoint == self._CAPI_PATH_STATION:
capi_data = capi_station_queries(query.capi_host)
@ -1080,17 +1072,22 @@ class Session(object):
"""
if self.credentials is None:
# Can't tell if beta or not
logger.warning("Dropping CAPI request because unclear if game beta or not")
return ''
if self.credentials['beta']:
logger.debug(f"Using {SERVER_BETA} because {self.credentials['beta']=}")
return SERVER_BETA
if monitor.is_live_galaxy():
logger.debug(f"Using {SERVER_LIVE} because monitor.is_live_galaxy() was True")
return SERVER_LIVE
# return SERVER_LEGACY # Not Yet
logger.warning("Dropping CAPI request because this is the Legacy galaxy, which is not yet supported")
return ""
else:
logger.debug(f"Using {SERVER_LEGACY} because monitor.is_live_galaxy() was False")
return SERVER_LEGACY
return ''
######################################################################

12
plug.py
View File

@ -370,7 +370,7 @@ def notify_dashboard_entry(
return error
def notify_newdata(
def notify_capidata(
data: companion.CAPIData,
is_beta: bool
) -> Optional[str]:
@ -383,13 +383,21 @@ def notify_newdata(
"""
error = None
for plugin in PLUGINS:
cmdr_data = plugin._get_func('cmdr_data')
# TODO: Handle it being Legacy data
if data.source_host == companion.SERVER_LEGACY:
cmdr_data = plugin._get_func('cmdr_data_legacy')
else:
cmdr_data = plugin._get_func('cmdr_data')
if cmdr_data:
try:
newerror = cmdr_data(data, is_beta)
error = error or newerror
except Exception:
logger.exception(f'Plugin "{plugin.name}" failed')
return error

View File

@ -2408,9 +2408,28 @@ def journal_entry( # noqa: C901, CCR001
return None
def cmdr_data_legacy(data: CAPIData, is_beta: bool) -> Optional[str]:
"""
Process new CAPI data for Legacy galaxy.
Ensuring the correct EDDN `header->gameversion` is achieved by use of
`EDDN.capi_gameversion_from_host_endpoint()` in:
`EDDN.export_outfitting()`
`EDDN.export_shipyard()`
`EDDN.export_outfitting()`
Thus we can just call through to the 'not Legacy' version of this function.
:param data: CAPI data to process.
:param is_beta: bool - True if this is a beta version of the Game.
:return: str - Error message, or `None` if no errors.
"""
return cmdr_data(data, is_beta)
def cmdr_data(data: CAPIData, is_beta: bool) -> Optional[str]: # noqa: CCR001
"""
Process new CAPI data.
Process new CAPI data for not-Legacy galaxy (might be beta).
:param data: CAPI data to process.
:param is_beta: bool - True if this is a beta version of the Game.

5
td.py
View File

@ -1,6 +1,7 @@
"""Export data for Trade Dangerous."""
import pathlib
import sys
import time
from collections import defaultdict
from operator import itemgetter
@ -29,9 +30,9 @@ def export(data: CAPIData) -> None:
# codecs can't automatically handle line endings, so encode manually where
# required
with open(data_path / data_filename, 'wb') as h:
# Format described here: https://bitbucket.org/kfsone/tradedangerous/wiki/Price%20Data
# Format described here: https://github.com/eyeonus/Trade-Dangerous/wiki/Price-Data
h.write('#! trade.py import -\n'.encode('utf-8'))
this_platform = 'darwin' and "Mac OS" or system()
this_platform = sys.platform == 'darwin' and "Mac OS" or system()
cmdr_name = data['commander']['name'].strip()
h.write(
f'# Created by {applongname} {appversion()} on {this_platform} for Cmdr {cmdr_name}.\n'.encode('utf-8')