diff --git a/build.py b/build.py index 4ee997587..cf35f7c54 100644 --- a/build.py +++ b/build.py @@ -110,6 +110,7 @@ def build() -> None: "plugins/edsm.py", "plugins/edsy.py", "plugins/inara.py", + "plugins/spansh_core.py", ] options: dict = { "py2exe": { diff --git a/plugins/edsm.py b/plugins/edsm.py index 2ed31ab33..ba292ca6c 100644 --- a/plugins/edsm.py +++ b/plugins/edsm.py @@ -205,7 +205,7 @@ def plugin_start3(plugin_dir: str) -> str: # Can't be earlier since can only call PhotoImage after window is created this._IMG_KNOWN = tk.PhotoImage(data=IMG_KNOWN_B64) # green circle this._IMG_UNKNOWN = tk.PhotoImage(data=IMG_UNKNOWN_B64) # red circle - this._IMG_NEW = tk.PhotoImage(data=IMG_NEW_B64) + this._IMG_NEW = tk.PhotoImage(data=IMG_NEW_B64) # yellow star this._IMG_ERROR = tk.PhotoImage(data=IMG_ERR_B64) # BBC Mode 5 '?' # Migrate old settings diff --git a/plugins/spansh_core.py b/plugins/spansh_core.py new file mode 100644 index 000000000..18a51846e --- /dev/null +++ b/plugins/spansh_core.py @@ -0,0 +1,195 @@ +""" +spansh_core.py - Spansh URL provider. + +Copyright (c) EDCD, All Rights Reserved +Licensed under the GNU General Public License. +See LICENSE file. + +This is an EDMC 'core' plugin. +All EDMC plugins are *dynamically* loaded at run-time. + +We build for Windows using `py2exe`. +`py2exe` can't possibly know about anything in the dynamically loaded core plugins. + +Thus, you **MUST** check if any imports you add in this file are only +referenced in this file (or only in any other core plugin), and if so... + + YOU MUST ENSURE THAT PERTINENT ADJUSTMENTS ARE MADE IN + `build.py` TO ENSURE THE FILES ARE ACTUALLY PRESENT + IN AN END-USER INSTALLATION ON WINDOWS. +""" +from __future__ import annotations + +import tkinter as tk +from typing import Any, cast +import requests +from companion import CAPIData +from config import appname, config +from EDMCLogging import get_main_logger + +logger = get_main_logger() + + +class This: + """Holds module globals.""" + + def __init__(self): + self.parent: tk.Tk + self.shutting_down = False # Plugin is shutting down. + self.system_link: tk.Widget = None # type: ignore + self.system_name: str | None = None # type: ignore + self.system_address: str | None = None # type: ignore + self.system_population: int | None = None + self.station_link: tk.Widget = None # type: ignore + self.station_name = None + self.station_marketid = None + self.on_foot = False + + +this = This() +STATION_UNDOCKED: str = '×' # "Station" name to display when not docked = U+00D7 + + +def plugin_start3(plugin_dir: str) -> str: + """ + Start the plugin. + + :param plugin_dir: NAme of directory this was loaded from. + :return: Identifier string for this plugin. + """ + return 'spansh' + + +def plugin_app(parent: tk.Tk) -> None: + """ + Construct this plugin's main UI, if any. + + :param parent: The tk parent to place our widgets into. + :return: See PLUGINS.md#display + """ + this.parent = parent + this.system_link = parent.nametowidget(f".{appname.lower()}.system") + this.station_link = parent.nametowidget(f".{appname.lower()}.station") + + +def plugin_stop() -> None: + """Plugin shutdown hook.""" + this.shutting_down = True + + +def journal_entry( + cmdr: str, is_beta: bool, system: str, station: str, entry: dict[str, Any], state: dict[str, Any] +) -> str: + """ + Handle a new Journal event. + + :param cmdr: Name of Commander. + :param is_beta: Whether game beta was detected. + :param system: Name of current tracked system. + :param station: Name of current tracked station location. + :param entry: The journal event. + :param state: `monitor.state` + :return: None if no error, else an error string. + """ + this.on_foot = state['OnFoot'] + this.system_address = state['SystemAddress'] + this.system_name = state['SystemName'] + this.system_population = state['SystemPopulation'] + this.station_name = state['StationName'] + this.station_marketid = state['MarketID'] + + # Only actually change URLs if we are current provider. + if config.get_str('system_provider') == 'spansh': + this.system_link['text'] = this.system_name + # Do *NOT* set 'url' here, as it's set to a function that will call + # through correctly. We don't want a static string. + this.system_link.update_idletasks() + + if config.get_str('station_provider') == 'spansh': + to_set: str = cast(str, this.station_name) + if not to_set: + if this.system_population is not None and this.system_population > 0: + to_set = STATION_UNDOCKED + else: + to_set = '' + + this.station_link['text'] = to_set + # Do *NOT* set 'url' here, as it's set to a function that will call + # through correctly. We don't want a static string. + this.station_link.update_idletasks() + + return '' + + +def cmdr_data(data: CAPIData, is_beta: bool) -> str | None: + """ + Process new CAPI data. + + :param data: The latest merged CAPI data. + :param is_beta: Whether game beta was detected. + :return: Optional error string. + """ + # Always store initially, even if we're not the *current* system provider. + if not this.station_marketid and data['commander']['docked']: + this.station_marketid = data['lastStarport']['id'] + + # Only trust CAPI if these aren't yet set + if not this.system_name: + this.system_name = data['lastSystem']['name'] + if not this.station_name and data['commander']['docked']: + this.station_name = data['lastStarport']['name'] + + # Override standard URL functions + if config.get_str('system_provider') == 'spansh': + this.system_link['text'] = this.system_name + # Do *NOT* set 'url' here, as it's set to a function that will call + # through correctly. We don't want a static string. + this.system_link.update_idletasks() + if config.get_str('station_provider') == 'spansh': + if data['commander']['docked'] or this.on_foot and this.station_name: + this.station_link['text'] = this.station_name + elif data['lastStarport']['name'] and data['lastStarport']['name'] != "": + this.station_link['text'] = STATION_UNDOCKED + else: + this.station_link['text'] = '' + # Do *NOT* set 'url' here, as it's set to a function that will call + # through correctly. We don't want a static string. + this.station_link.update_idletasks() + + return '' + + +def system_url(system_name: str) -> str: + """ + Construct an appropriate spansh URL for the provided system. + + :param system_name: Will be overridden with `this.system_address` if that + is set. + :return: The URL, empty if no data was available to construct it. + """ + if this.system_address: + return requests.utils.requote_uri(f'https://www.spansh.co.uk/system/{this.system_address}') + + if system_name: + return requests.utils.requote_uri(f'https://www.spansh.co.uk/search/{system_name}') + + return '' + + +def station_url(system_name: str, station_name: str) -> str: + """ + Construct an appropriate spansh URL for a station. + + Ignores `station_name` in favour of `this.station_marketid`. + + :param system_name: Name of the system the station is in. + :param station_name: **NOT USED** + :return: The URL, empty if no data was available to construct it. + """ + if this.station_marketid: + return requests.utils.requote_uri(f'https://www.spansh.co.uk/station/{this.station_marketid}') + + if system_name: + return system_url(system_name) + + return ''