mirror of
https://github.com/EDCD/EDMarketConnector.git
synced 2025-06-15 14:52:15 +03:00
Merge tag 'Release/4.0.4' into releases
Release 4.0.4
This commit is contained in:
commit
405edfacb6
20
ChangeLog.md
20
ChangeLog.md
@ -1,6 +1,26 @@
|
|||||||
This is the master changelog for Elite Dangerous Market Connector. Entries are in reverse chronological order (latest first).
|
This is the master changelog for Elite Dangerous Market Connector. Entries are in reverse chronological order (latest first).
|
||||||
---
|
---
|
||||||
|
|
||||||
|
Release 4.0.4
|
||||||
|
===
|
||||||
|
|
||||||
|
* Built using Python 3.7.8. Prior 4.0.x releases used 3.7.7.
|
||||||
|
* Don't crash if no non-default Journal Directory has been set.
|
||||||
|
* Only send to Inara API at most once every 30 seconds. This should avoid
|
||||||
|
the "Inara 400 Too much requests, slow down, cowboy. ;) ..." message and
|
||||||
|
being locked out from the API for an hour as a result. Any events that
|
||||||
|
require data to be sent during the 30s cooldown will be queued and sent when
|
||||||
|
that timer expires.
|
||||||
|
|
||||||
|
This was caused by previous changes in an attempt to send cargo events
|
||||||
|
to Inara more often. This fix retains that enhancement.
|
||||||
|
|
||||||
|
Note that if you log out and stop EDMC within 30 seconds you might have
|
||||||
|
some events not sent. If we tried to force a send then it might hit the
|
||||||
|
limit when you want to log back in and continue playing. As it is you can
|
||||||
|
re-run EDMC and log back into the game to ensure Inara is synchronised
|
||||||
|
properly.
|
||||||
|
|
||||||
Release 4.0.3
|
Release 4.0.3
|
||||||
===
|
===
|
||||||
|
|
||||||
|
@ -235,7 +235,7 @@
|
|||||||
/* Appearance setting prompt. [prefs.py] */
|
/* Appearance setting prompt. [prefs.py] */
|
||||||
"Language" = "Jazyk";
|
"Language" = "Jazyk";
|
||||||
|
|
||||||
/* [EDMarketConnector.py] */
|
/* [EDMarketConnector.py] - Leave '{HH}:{MM}:{SS}' as-is */
|
||||||
"Last updated at {HH}:{MM}:{SS}" = "Poslední aktualizace {HH}:{MM}:{SS}";
|
"Last updated at {HH}:{MM}:{SS}" = "Poslední aktualizace {HH}:{MM}:{SS}";
|
||||||
|
|
||||||
/* Federation rank. [stats.py] */
|
/* Federation rank. [stats.py] */
|
||||||
|
@ -235,7 +235,7 @@
|
|||||||
/* Appearance setting prompt. [prefs.py] */
|
/* Appearance setting prompt. [prefs.py] */
|
||||||
"Language" = "Sprache";
|
"Language" = "Sprache";
|
||||||
|
|
||||||
/* [EDMarketConnector.py] */
|
/* [EDMarketConnector.py] - Leave '{HH}:{MM}:{SS}' as-is */
|
||||||
"Last updated at {HH}:{MM}:{SS}" = "Zuletzt aktualisiert um {HH}:{MM}:{SS}";
|
"Last updated at {HH}:{MM}:{SS}" = "Zuletzt aktualisiert um {HH}:{MM}:{SS}";
|
||||||
|
|
||||||
/* Federation rank. [stats.py] */
|
/* Federation rank. [stats.py] */
|
||||||
|
@ -490,6 +490,9 @@
|
|||||||
/* Update button in main window. [EDMarketConnector.py] */
|
/* Update button in main window. [EDMarketConnector.py] */
|
||||||
"Update" = "Update";
|
"Update" = "Update";
|
||||||
|
|
||||||
|
/* Option to use alternate URL method on shipyard links [prefs.py] */
|
||||||
|
"Use alternate URL method" = "Use alternate URL method";
|
||||||
|
|
||||||
/* Status dialog subtitle - CR value of ship. [stats.py] */
|
/* Status dialog subtitle - CR value of ship. [stats.py] */
|
||||||
"Value" = "Value";
|
"Value" = "Value";
|
||||||
|
|
||||||
|
@ -235,7 +235,7 @@
|
|||||||
/* Appearance setting prompt. [prefs.py] */
|
/* Appearance setting prompt. [prefs.py] */
|
||||||
"Language" = "Idioma";
|
"Language" = "Idioma";
|
||||||
|
|
||||||
/* [EDMarketConnector.py] */
|
/* [EDMarketConnector.py] - Leave '{HH}:{MM}:{SS}' as-is */
|
||||||
"Last updated at {HH}:{MM}:{SS}" = "Última actualización: {HH}:{MM}:{SS}";
|
"Last updated at {HH}:{MM}:{SS}" = "Última actualización: {HH}:{MM}:{SS}";
|
||||||
|
|
||||||
/* Federation rank. [stats.py] */
|
/* Federation rank. [stats.py] */
|
||||||
|
@ -235,7 +235,7 @@
|
|||||||
/* Appearance setting prompt. [prefs.py] */
|
/* Appearance setting prompt. [prefs.py] */
|
||||||
"Language" = "Kieli";
|
"Language" = "Kieli";
|
||||||
|
|
||||||
/* [EDMarketConnector.py] */
|
/* [EDMarketConnector.py] - Leave '{HH}:{MM}:{SS}' as-is */
|
||||||
"Last updated at {HH}:{MM}:{SS}" = "Päivitetty viimeksi {HH}:{MM}:{SS}";
|
"Last updated at {HH}:{MM}:{SS}" = "Päivitetty viimeksi {HH}:{MM}:{SS}";
|
||||||
|
|
||||||
/* Federation rank. [stats.py] */
|
/* Federation rank. [stats.py] */
|
||||||
|
@ -235,7 +235,7 @@
|
|||||||
/* Appearance setting prompt. [prefs.py] */
|
/* Appearance setting prompt. [prefs.py] */
|
||||||
"Language" = "Langue";
|
"Language" = "Langue";
|
||||||
|
|
||||||
/* [EDMarketConnector.py] */
|
/* [EDMarketConnector.py] - Leave '{HH}:{MM}:{SS}' as-is */
|
||||||
"Last updated at {HH}:{MM}:{SS}" = "Dernière mise à jour à {HH}:{MM}:{SS}";
|
"Last updated at {HH}:{MM}:{SS}" = "Dernière mise à jour à {HH}:{MM}:{SS}";
|
||||||
|
|
||||||
/* Federation rank. [stats.py] */
|
/* Federation rank. [stats.py] */
|
||||||
|
@ -232,7 +232,7 @@
|
|||||||
/* Appearance setting prompt. [prefs.py] */
|
/* Appearance setting prompt. [prefs.py] */
|
||||||
"Language" = "Nyelv";
|
"Language" = "Nyelv";
|
||||||
|
|
||||||
/* [EDMarketConnector.py] */
|
/* [EDMarketConnector.py] - Leave '{HH}:{MM}:{SS}' as-is */
|
||||||
"Last updated at {HH}:{MM}:{SS}" = "Utoljára frissítve {HH}:{MM}:{SS}";
|
"Last updated at {HH}:{MM}:{SS}" = "Utoljára frissítve {HH}:{MM}:{SS}";
|
||||||
|
|
||||||
/* Federation rank. [stats.py] */
|
/* Federation rank. [stats.py] */
|
||||||
|
@ -235,7 +235,7 @@
|
|||||||
/* Appearance setting prompt. [prefs.py] */
|
/* Appearance setting prompt. [prefs.py] */
|
||||||
"Language" = "Lingua";
|
"Language" = "Lingua";
|
||||||
|
|
||||||
/* [EDMarketConnector.py] */
|
/* [EDMarketConnector.py] - Leave '{HH}:{MM}:{SS}' as-is */
|
||||||
"Last updated at {HH}:{MM}:{SS}" = "Ultimo aggiornamento alle {HH}:{MM}:{SS}";
|
"Last updated at {HH}:{MM}:{SS}" = "Ultimo aggiornamento alle {HH}:{MM}:{SS}";
|
||||||
|
|
||||||
/* Federation rank. [stats.py] */
|
/* Federation rank. [stats.py] */
|
||||||
|
@ -235,7 +235,7 @@
|
|||||||
/* Appearance setting prompt. [prefs.py] */
|
/* Appearance setting prompt. [prefs.py] */
|
||||||
"Language" = "言語";
|
"Language" = "言語";
|
||||||
|
|
||||||
/* [EDMarketConnector.py] */
|
/* [EDMarketConnector.py] - Leave '{HH}:{MM}:{SS}' as-is */
|
||||||
"Last updated at {HH}:{MM}:{SS}" = "最終更新時間 {HH}:{MM}:{SS}";
|
"Last updated at {HH}:{MM}:{SS}" = "最終更新時間 {HH}:{MM}:{SS}";
|
||||||
|
|
||||||
/* Federation rank. [stats.py] */
|
/* Federation rank. [stats.py] */
|
||||||
|
@ -217,7 +217,7 @@
|
|||||||
/* Appearance setting prompt. [prefs.py] */
|
/* Appearance setting prompt. [prefs.py] */
|
||||||
"Language" = "Valoda";
|
"Language" = "Valoda";
|
||||||
|
|
||||||
/* [EDMarketConnector.py] */
|
/* [EDMarketConnector.py] - Leave '{HH}:{MM}:{SS}' as-is */
|
||||||
"Last updated at {HH}:{MM}:{SS}" = "Pēdējo reizi atjaunināts {HH}:{MM}:{SS}";
|
"Last updated at {HH}:{MM}:{SS}" = "Pēdējo reizi atjaunināts {HH}:{MM}:{SS}";
|
||||||
|
|
||||||
/* Federation rank. [stats.py] */
|
/* Federation rank. [stats.py] */
|
||||||
|
@ -235,7 +235,7 @@
|
|||||||
/* Appearance setting prompt. [prefs.py] */
|
/* Appearance setting prompt. [prefs.py] */
|
||||||
"Language" = "Taal";
|
"Language" = "Taal";
|
||||||
|
|
||||||
/* [EDMarketConnector.py] */
|
/* [EDMarketConnector.py] - Leave '{HH}:{MM}:{SS}' as-is */
|
||||||
"Last updated at {HH}:{MM}:{SS}" = "Voor het laatst bijgewerkt om {HH}:{MM}:{SS}";
|
"Last updated at {HH}:{MM}:{SS}" = "Voor het laatst bijgewerkt om {HH}:{MM}:{SS}";
|
||||||
|
|
||||||
/* Federation rank. [stats.py] */
|
/* Federation rank. [stats.py] */
|
||||||
@ -490,6 +490,9 @@
|
|||||||
/* Update button in main window. [EDMarketConnector.py] */
|
/* Update button in main window. [EDMarketConnector.py] */
|
||||||
"Update" = "Bijwerken";
|
"Update" = "Bijwerken";
|
||||||
|
|
||||||
|
/* Option to use alternate URL method on shipyard links [prefs.py] */
|
||||||
|
"Use alternate URL method" = "Gebruik andere URL methode";
|
||||||
|
|
||||||
/* Status dialog subtitle - CR value of ship. [stats.py] */
|
/* Status dialog subtitle - CR value of ship. [stats.py] */
|
||||||
"Value" = "Waarde";
|
"Value" = "Waarde";
|
||||||
|
|
||||||
|
@ -235,7 +235,7 @@
|
|||||||
/* Appearance setting prompt. [prefs.py] */
|
/* Appearance setting prompt. [prefs.py] */
|
||||||
"Language" = "Język";
|
"Language" = "Język";
|
||||||
|
|
||||||
/* [EDMarketConnector.py] */
|
/* [EDMarketConnector.py] - Leave '{HH}:{MM}:{SS}' as-is */
|
||||||
"Last updated at {HH}:{MM}:{SS}" = "Ostatnia aktualizacja {HH}:{MM}:{SS}";
|
"Last updated at {HH}:{MM}:{SS}" = "Ostatnia aktualizacja {HH}:{MM}:{SS}";
|
||||||
|
|
||||||
/* Federation rank. [stats.py] */
|
/* Federation rank. [stats.py] */
|
||||||
|
@ -235,7 +235,7 @@
|
|||||||
/* Appearance setting prompt. [prefs.py] */
|
/* Appearance setting prompt. [prefs.py] */
|
||||||
"Language" = "Idioma";
|
"Language" = "Idioma";
|
||||||
|
|
||||||
/* [EDMarketConnector.py] */
|
/* [EDMarketConnector.py] - Leave '{HH}:{MM}:{SS}' as-is */
|
||||||
"Last updated at {HH}:{MM}:{SS}" = "Última atualização {HH}:{MM}:{SS}";
|
"Last updated at {HH}:{MM}:{SS}" = "Última atualização {HH}:{MM}:{SS}";
|
||||||
|
|
||||||
/* Federation rank. [stats.py] */
|
/* Federation rank. [stats.py] */
|
||||||
|
@ -235,7 +235,7 @@
|
|||||||
/* Appearance setting prompt. [prefs.py] */
|
/* Appearance setting prompt. [prefs.py] */
|
||||||
"Language" = "Linguagem";
|
"Language" = "Linguagem";
|
||||||
|
|
||||||
/* [EDMarketConnector.py] */
|
/* [EDMarketConnector.py] - Leave '{HH}:{MM}:{SS}' as-is */
|
||||||
"Last updated at {HH}:{MM}:{SS}" = "Última actualização: {HH}:{MM}:{SS}";
|
"Last updated at {HH}:{MM}:{SS}" = "Última actualização: {HH}:{MM}:{SS}";
|
||||||
|
|
||||||
/* Federation rank. [stats.py] */
|
/* Federation rank. [stats.py] */
|
||||||
|
@ -235,7 +235,7 @@
|
|||||||
/* Appearance setting prompt. [prefs.py] */
|
/* Appearance setting prompt. [prefs.py] */
|
||||||
"Language" = "Язык";
|
"Language" = "Язык";
|
||||||
|
|
||||||
/* [EDMarketConnector.py] */
|
/* [EDMarketConnector.py] - Leave '{HH}:{MM}:{SS}' as-is */
|
||||||
"Last updated at {HH}:{MM}:{SS}" = "Последнее обновление в {HH}:{MM}:{SS}";
|
"Last updated at {HH}:{MM}:{SS}" = "Последнее обновление в {HH}:{MM}:{SS}";
|
||||||
|
|
||||||
/* Federation rank. [stats.py] */
|
/* Federation rank. [stats.py] */
|
||||||
|
@ -184,7 +184,7 @@
|
|||||||
/* Appearance setting prompt. [prefs.py] */
|
/* Appearance setting prompt. [prefs.py] */
|
||||||
"Language" = "Jezik";
|
"Language" = "Jezik";
|
||||||
|
|
||||||
/* [EDMarketConnector.py] */
|
/* [EDMarketConnector.py] - Leave '{HH}:{MM}:{SS}' as-is */
|
||||||
"Last updated at {HH}:{MM}:{SS}" = "Zadnja osvežitev podatkov {HH}:{MM}:{SS} ";
|
"Last updated at {HH}:{MM}:{SS}" = "Zadnja osvežitev podatkov {HH}:{MM}:{SS} ";
|
||||||
|
|
||||||
/* Federation rank. [stats.py] */
|
/* Federation rank. [stats.py] */
|
||||||
|
@ -235,7 +235,7 @@
|
|||||||
/* Appearance setting prompt. [prefs.py] */
|
/* Appearance setting prompt. [prefs.py] */
|
||||||
"Language" = "Jezik";
|
"Language" = "Jezik";
|
||||||
|
|
||||||
/* [EDMarketConnector.py] */
|
/* [EDMarketConnector.py] - Leave '{HH}:{MM}:{SS}' as-is */
|
||||||
"Last updated at {HH}:{MM}:{SS}" = "Posljednji put osvježeno {HH}:{MM}:{SS}";
|
"Last updated at {HH}:{MM}:{SS}" = "Posljednji put osvježeno {HH}:{MM}:{SS}";
|
||||||
|
|
||||||
/* Federation rank. [stats.py] */
|
/* Federation rank. [stats.py] */
|
||||||
|
@ -235,7 +235,7 @@
|
|||||||
/* Appearance setting prompt. [prefs.py] */
|
/* Appearance setting prompt. [prefs.py] */
|
||||||
"Language" = "Jezik";
|
"Language" = "Jezik";
|
||||||
|
|
||||||
/* [EDMarketConnector.py] */
|
/* [EDMarketConnector.py] - Leave '{HH}:{MM}:{SS}' as-is */
|
||||||
"Last updated at {HH}:{MM}:{SS}" = "Poslednji put osveženo {HH}:{MM}:{SS}";
|
"Last updated at {HH}:{MM}:{SS}" = "Poslednji put osveženo {HH}:{MM}:{SS}";
|
||||||
|
|
||||||
/* Federation rank. [stats.py] */
|
/* Federation rank. [stats.py] */
|
||||||
|
@ -235,7 +235,7 @@
|
|||||||
/* Appearance setting prompt. [prefs.py] */
|
/* Appearance setting prompt. [prefs.py] */
|
||||||
"Language" = "Språk";
|
"Language" = "Språk";
|
||||||
|
|
||||||
/* [EDMarketConnector.py] */
|
/* [EDMarketConnector.py] - Leave '{HH}:{MM}:{SS}' as-is */
|
||||||
"Last updated at {HH}:{MM}:{SS}" = "Senaste uppdatering: {HH}:{MM}:{SS}";
|
"Last updated at {HH}:{MM}:{SS}" = "Senaste uppdatering: {HH}:{MM}:{SS}";
|
||||||
|
|
||||||
/* Federation rank. [stats.py] */
|
/* Federation rank. [stats.py] */
|
||||||
|
@ -235,7 +235,7 @@
|
|||||||
/* Appearance setting prompt. [prefs.py] */
|
/* Appearance setting prompt. [prefs.py] */
|
||||||
"Language" = "Мова";
|
"Language" = "Мова";
|
||||||
|
|
||||||
/* [EDMarketConnector.py] */
|
/* [EDMarketConnector.py] - Leave '{HH}:{MM}:{SS}' as-is */
|
||||||
"Last updated at {HH}:{MM}:{SS}" = "Останнє оновлення було {HH}:{MM}:{SS}";
|
"Last updated at {HH}:{MM}:{SS}" = "Останнє оновлення було {HH}:{MM}:{SS}";
|
||||||
|
|
||||||
/* Federation rank. [stats.py] */
|
/* Federation rank. [stats.py] */
|
||||||
@ -490,6 +490,9 @@
|
|||||||
/* Update button in main window. [EDMarketConnector.py] */
|
/* Update button in main window. [EDMarketConnector.py] */
|
||||||
"Update" = "Оновлення";
|
"Update" = "Оновлення";
|
||||||
|
|
||||||
|
/* Option to use alternate URL method on shipyard links [prefs.py] */
|
||||||
|
"Use alternate URL method" = "Використовувати альтернативний URL-метод";
|
||||||
|
|
||||||
/* Status dialog subtitle - CR value of ship. [stats.py] */
|
/* Status dialog subtitle - CR value of ship. [stats.py] */
|
||||||
"Value" = "Вартість";
|
"Value" = "Вартість";
|
||||||
|
|
||||||
|
@ -232,7 +232,7 @@
|
|||||||
/* Appearance setting prompt. [prefs.py] */
|
/* Appearance setting prompt. [prefs.py] */
|
||||||
"Language" = "语言";
|
"Language" = "语言";
|
||||||
|
|
||||||
/* [EDMarketConnector.py] */
|
/* [EDMarketConnector.py] - Leave '{HH}:{MM}:{SS}' as-is */
|
||||||
"Last updated at {HH}:{MM}:{SS}" = "最后更新于 {HH}:{MM}:{SS}";
|
"Last updated at {HH}:{MM}:{SS}" = "最后更新于 {HH}:{MM}:{SS}";
|
||||||
|
|
||||||
/* Federation rank. [stats.py] */
|
/* Federation rank. [stats.py] */
|
||||||
|
67
PLUGINS.md
67
PLUGINS.md
@ -55,6 +55,72 @@ import myNotebook as nb
|
|||||||
```
|
```
|
||||||
For creating UI elements.
|
For creating UI elements.
|
||||||
|
|
||||||
|
---
|
||||||
|
### Logging
|
||||||
|
Currently (still in 4.0.3) the only way to provide any logged output from a
|
||||||
|
plugin is to use `print(...)` statements. When running the application from
|
||||||
|
the packaged executeable all output is redirected to a log file. See
|
||||||
|
[Reporting a problem](https://github.com/EDCD/EDMarketConnector/wiki/Troubleshooting#reporting-a-problem)
|
||||||
|
for the location of this log file.
|
||||||
|
|
||||||
|
A future version of EDMC will implement proper logging using the Python
|
||||||
|
`logging` module. Plugin developers should get their code ready for this by
|
||||||
|
using the following code instead of simple `print(...)` statements
|
||||||
|
|
||||||
|
Insert this at the top-level of your load.py file (so not inside
|
||||||
|
`plugin_start3()`):
|
||||||
|
```python
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from config import appname
|
||||||
|
|
||||||
|
# This could also be returned from plugin_start3()
|
||||||
|
plugin_name = os.path.basename(os.path.dirname(__file__))
|
||||||
|
|
||||||
|
# A Logger is used per 'found' plugin to make it easy to include the plugin's
|
||||||
|
# folder name in the logging output format.
|
||||||
|
logger = logging.getLogger(f'{appname}.{plugin_name}')
|
||||||
|
|
||||||
|
# If the Logger has handlers then it was already set up by the core code, else
|
||||||
|
# it needs setting up here.
|
||||||
|
if not logger.hasHandlers():
|
||||||
|
level = logging.INFO # So logger.info(...) is equivalent to print()
|
||||||
|
|
||||||
|
logger.setLevel(level)
|
||||||
|
logger_channel = logging.StreamHandler()
|
||||||
|
logger_channel.setLevel(level)
|
||||||
|
logger_formatter = logging.Formatter(f'%(asctime)s - %(name)s - %(levelname)s - %(module)s:%(lineno)d:%(funcName)s: %(message)s')
|
||||||
|
logger_formatter.default_time_format = '%Y-%m-%d %H:%M:%S'
|
||||||
|
logger_formatter.default_msec_format = '%s.%03d'
|
||||||
|
logger_channel.setFormatter(logger_formatter)
|
||||||
|
logger.addHandler(logger_channel)
|
||||||
|
```
|
||||||
|
|
||||||
|
Then replace `print(...)` statements with one of the following:
|
||||||
|
```python
|
||||||
|
logger.info('some info message') # instead of print(...)
|
||||||
|
|
||||||
|
logger.debug('something only for debug')
|
||||||
|
|
||||||
|
logger.warning('Something needs warning about')
|
||||||
|
|
||||||
|
logger.error('Some error happened')
|
||||||
|
|
||||||
|
logger.critical('Something went wrong in a critical manner')
|
||||||
|
|
||||||
|
try:
|
||||||
|
...
|
||||||
|
except Exception:
|
||||||
|
# This logs at 'ERROR' level.
|
||||||
|
# Also automatically includes exception information.
|
||||||
|
logger.exception('An exception occurred')
|
||||||
|
|
||||||
|
try:
|
||||||
|
...
|
||||||
|
except Exception as e:
|
||||||
|
logger.debug('Exception we only note in debug output', exc_info=e)
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
### Startup
|
### Startup
|
||||||
@ -363,6 +429,7 @@ If the player has chosen to "Send flight log and Cmdr status to Inara" this
|
|||||||
gets called when the player starts the game, enters a new system, docks or
|
gets called when the player starts the game, enters a new system, docks or
|
||||||
undocks. It is called some time after the corresponding `journal_entry()`
|
undocks. It is called some time after the corresponding `journal_entry()`
|
||||||
event.
|
event.
|
||||||
|
|
||||||
---
|
---
|
||||||
```python
|
```python
|
||||||
def inara_notify_ship(eventData):
|
def inara_notify_ship(eventData):
|
||||||
|
21
config.py
21
config.py
@ -1,5 +1,6 @@
|
|||||||
import numbers
|
import numbers
|
||||||
import sys
|
import sys
|
||||||
|
import warnings
|
||||||
from os import getenv, makedirs, mkdir, pardir
|
from os import getenv, makedirs, mkdir, pardir
|
||||||
from os.path import expanduser, dirname, exists, isdir, join, normpath
|
from os.path import expanduser, dirname, exists, isdir, join, normpath
|
||||||
from sys import platform
|
from sys import platform
|
||||||
@ -12,7 +13,7 @@ appcmdname = 'EDMC'
|
|||||||
# appversion **MUST** follow Semantic Versioning rules:
|
# appversion **MUST** follow Semantic Versioning rules:
|
||||||
# <https://semver.org/#semantic-versioning-specification-semver>
|
# <https://semver.org/#semantic-versioning-specification-semver>
|
||||||
# Major.Minor.Patch(-prerelease)(+buildmetadata)
|
# Major.Minor.Patch(-prerelease)(+buildmetadata)
|
||||||
appversion = '4.0.3' #-rc1+a872b5f'
|
appversion = '4.0.4' #-rc1+a872b5f'
|
||||||
# For some things we want appversion without (possible) +build metadata
|
# For some things we want appversion without (possible) +build metadata
|
||||||
appversion_nobuild = str(semantic_version.Version(appversion).truncate('prerelease'))
|
appversion_nobuild = str(semantic_version.Version(appversion).truncate('prerelease'))
|
||||||
copyright = u'© 2015-2019 Jonathan Harris, 2020 EDCD'
|
copyright = u'© 2015-2019 Jonathan Harris, 2020 EDCD'
|
||||||
@ -367,25 +368,13 @@ class Config(object):
|
|||||||
# Common
|
# Common
|
||||||
|
|
||||||
def get_password(self, account):
|
def get_password(self, account):
|
||||||
try:
|
warnings.warn("password subsystem is no longer supported", DeprecationWarning)
|
||||||
import keyring
|
|
||||||
return keyring.get_password(self.identifier, account)
|
|
||||||
except ImportError:
|
|
||||||
return None
|
|
||||||
|
|
||||||
def set_password(self, account, password):
|
def set_password(self, account, password):
|
||||||
try:
|
warnings.warn("password subsystem is no longer supported", DeprecationWarning)
|
||||||
import keyring
|
|
||||||
keyring.set_password(self.identifier, account, password)
|
|
||||||
except ImportError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def delete_password(self, account):
|
def delete_password(self, account):
|
||||||
try:
|
warnings.warn("password subsystem is no longer supported", DeprecationWarning)
|
||||||
import keyring
|
|
||||||
keyring.delete_password(self.identifier, account)
|
|
||||||
except:
|
|
||||||
pass # don't care - silently fail
|
|
||||||
|
|
||||||
# singleton
|
# singleton
|
||||||
config = Config()
|
config = Config()
|
||||||
|
@ -168,11 +168,36 @@
|
|||||||
<!-- Windows -->
|
<!-- Windows -->
|
||||||
|
|
||||||
<item>
|
<item>
|
||||||
<title>Release 4.0.3</title>
|
<title>Release 4.0.4</title>
|
||||||
<description>
|
<description>
|
||||||
<![CDATA[
|
<![CDATA[
|
||||||
<style>body { font-family:"Segoe UI","Tahoma"; font-size: 75%; } h2 { font-family:"Segoe UI","Tahoma"; font-size: 105%; }</style>
|
<style>body { font-family:"Segoe UI","Tahoma"; font-size: 75%; } h2 { font-family:"Segoe UI","Tahoma"; font-size: 105%; }</style>
|
||||||
|
|
||||||
|
<h2>Release 4.0.4</h2>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<p>Built using Python 3.7.8. Prior 4.0.x releases used 3.7.7.</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>Don't crash if no non-default Journal Directory has been set.</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>Only send to Inara API at most once every 30 seconds. This should avoid
|
||||||
|
the "Inara 400 Too much requests, slow down, cowboy. ;) ..." message and
|
||||||
|
being locked out from the API for an hour as a result. Any events that
|
||||||
|
require data to be sent during the 30s cooldown will be queued and sent when
|
||||||
|
that timer expires.</p>
|
||||||
|
<p>This was caused by previous changes in an attempt to send cargo events
|
||||||
|
to Inara more often. This fix retains that enhancement.</p>
|
||||||
|
<p>Note that if you log out and stop EDMC within 30 seconds you might have
|
||||||
|
some events not sent. If we tried to force a send then it might hit the
|
||||||
|
limit when you want to log back in and continue playing. As it is you can
|
||||||
|
re-run EDMC and log back into the game to ensure Inara is synchronised
|
||||||
|
properly.</p>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
|
||||||
<h2>Release 4.0.3</h2>
|
<h2>Release 4.0.3</h2>
|
||||||
<p><strong>NB: Anyone who installed a 4.0.3-rcX release candidate version should first
|
<p><strong>NB: Anyone who installed a 4.0.3-rcX release candidate version should first
|
||||||
uninstall it before installing this.</strong>
|
uninstall it before installing this.</strong>
|
||||||
@ -578,11 +603,11 @@ If any of your plugins are listed in that section then they will need updating,
|
|||||||
]]>
|
]]>
|
||||||
</description>
|
</description>
|
||||||
<enclosure
|
<enclosure
|
||||||
url="https://github.com/EDCD/EDMarketConnector/releases/download/Release/4.0.3/EDMarketConnector_win_4.0.3.msi"
|
url="https://github.com/EDCD/EDMarketConnector/releases/download/Release/4.0.4/EDMarketConnector_win_4.0.4.msi"
|
||||||
sparkle:os="windows"
|
sparkle:os="windows"
|
||||||
sparkle:installerArguments="/passive LAUNCH=yes"
|
sparkle:installerArguments="/passive LAUNCH=yes"
|
||||||
sparkle:version="4.0.3"
|
sparkle:version="4.0.4"
|
||||||
length="11325440"
|
length="11419648"
|
||||||
type="application/octet-stream"
|
type="application/octet-stream"
|
||||||
/>
|
/>
|
||||||
</item>
|
</item>
|
||||||
|
@ -118,9 +118,14 @@ class EDLogs(FileSystemEventHandler):
|
|||||||
|
|
||||||
def start(self, root):
|
def start(self, root):
|
||||||
self.root = root
|
self.root = root
|
||||||
logdir = expanduser(config.get('journaldir') or config.default_journal_dir) # type: ignore # config is weird
|
journal_dir = config.get('journaldir') or config.default_journal_dir
|
||||||
|
|
||||||
if not logdir or not isdir(logdir): # type: ignore # config does weird things in its get
|
if journal_dir is None:
|
||||||
|
journal_dir = ''
|
||||||
|
|
||||||
|
logdir = expanduser(journal_dir) # type: ignore # config is weird
|
||||||
|
|
||||||
|
if not logdir or not isdir(logdir):
|
||||||
self.stop()
|
self.stop()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@ -21,7 +21,6 @@ import plug
|
|||||||
if __debug__:
|
if __debug__:
|
||||||
from traceback import print_exc
|
from traceback import print_exc
|
||||||
|
|
||||||
|
|
||||||
_TIMEOUT = 20
|
_TIMEOUT = 20
|
||||||
FAKE = ['CQC', 'Training', 'Destination'] # Fake systems that shouldn't be sent to Inara
|
FAKE = ['CQC', 'Training', 'Destination'] # Fake systems that shouldn't be sent to Inara
|
||||||
CREDIT_RATIO = 1.05 # Update credits if they change by 5% over the course of a session
|
CREDIT_RATIO = 1.05 # Update credits if they change by 5% over the course of a session
|
||||||
@ -50,8 +49,11 @@ this.loadout = None
|
|||||||
this.fleet = None
|
this.fleet = None
|
||||||
this.shipswap = False # just swapped ship
|
this.shipswap = False # just swapped ship
|
||||||
|
|
||||||
this.last_update_time = time.time() # last time we updated (set to now because we're going to update quickly)
|
# last time we updated, if unset in config this is 0, which means an instant update
|
||||||
FLOOD_LIMIT_SECONDS = 45 # minimum time between sending non-major cargo triggered messages
|
LAST_UPDATE_CONF_KEY = 'inara_last_update'
|
||||||
|
# this.last_update_time = config.getint(LAST_UPDATE_CONF_KEY)
|
||||||
|
FLOOD_LIMIT_SECONDS = 30 # minimum time between sending events
|
||||||
|
this.timer_run = True
|
||||||
|
|
||||||
|
|
||||||
# Main window clicks
|
# Main window clicks
|
||||||
@ -86,6 +88,10 @@ def plugin_start3(plugin_dir):
|
|||||||
this.thread = Thread(target = worker, name = 'Inara worker')
|
this.thread = Thread(target = worker, name = 'Inara worker')
|
||||||
this.thread.daemon = True
|
this.thread.daemon = True
|
||||||
this.thread.start()
|
this.thread.start()
|
||||||
|
|
||||||
|
this.timer_thread = Thread(target=call_timer, name='Inara timer')
|
||||||
|
this.timer_thread.daemon = True
|
||||||
|
this.timer_thread.start()
|
||||||
return 'Inara'
|
return 'Inara'
|
||||||
|
|
||||||
def plugin_app(parent):
|
def plugin_app(parent):
|
||||||
@ -97,11 +103,14 @@ def plugin_app(parent):
|
|||||||
def plugin_stop():
|
def plugin_stop():
|
||||||
# Send any unsent events
|
# Send any unsent events
|
||||||
call()
|
call()
|
||||||
|
time.sleep(0.1) # Sleep for 100ms to allow call to go out, and to force a context switch to our other threads
|
||||||
# Signal thread to close and wait for it
|
# Signal thread to close and wait for it
|
||||||
this.queue.put(None)
|
this.queue.put(None)
|
||||||
this.thread.join()
|
this.thread.join()
|
||||||
this.thread = None
|
this.thread = None
|
||||||
|
|
||||||
|
this.timer_run = False
|
||||||
|
|
||||||
def plugin_prefs(parent, cmdr, is_beta):
|
def plugin_prefs(parent, cmdr, is_beta):
|
||||||
|
|
||||||
PADX = 10
|
PADX = 10
|
||||||
@ -189,7 +198,7 @@ def journal_entry(cmdr, is_beta, system, station, entry, state):
|
|||||||
|
|
||||||
# Send any unsent events when switching accounts
|
# Send any unsent events when switching accounts
|
||||||
if cmdr and cmdr != this.cmdr:
|
if cmdr and cmdr != this.cmdr:
|
||||||
call()
|
call(force=True)
|
||||||
|
|
||||||
this.cmdr = cmdr
|
this.cmdr = cmdr
|
||||||
this.FID = state['FID']
|
this.FID = state['FID']
|
||||||
@ -246,8 +255,6 @@ def journal_entry(cmdr, is_beta, system, station, entry, state):
|
|||||||
|
|
||||||
if config.getint('inara_out') and not is_beta and not this.multicrew and credentials(cmdr):
|
if config.getint('inara_out') and not is_beta and not this.multicrew and credentials(cmdr):
|
||||||
try:
|
try:
|
||||||
old_events = len(this.events) # Will only send existing events if we add a new event below
|
|
||||||
|
|
||||||
# Dump starting state to Inara
|
# Dump starting state to Inara
|
||||||
|
|
||||||
if (this.newuser or
|
if (this.newuser or
|
||||||
@ -307,6 +314,8 @@ def journal_entry(cmdr, is_beta, system, station, entry, state):
|
|||||||
this.loadout = make_loadout(state)
|
this.loadout = make_loadout(state)
|
||||||
add_event('setCommanderShipLoadout', entry['timestamp'], this.loadout)
|
add_event('setCommanderShipLoadout', entry['timestamp'], this.loadout)
|
||||||
|
|
||||||
|
call() # Call here just to be sure that if we can send, we do, otherwise it'll get it in the next tick
|
||||||
|
|
||||||
|
|
||||||
# Promotions
|
# Promotions
|
||||||
elif entry['event'] == 'Promotion':
|
elif entry['event'] == 'Promotion':
|
||||||
@ -435,35 +444,21 @@ def journal_entry(cmdr, is_beta, system, station, entry, state):
|
|||||||
|
|
||||||
cargo = [OrderedDict([('itemName', k), ('itemCount', state['Cargo'][k])]) for k in sorted(state['Cargo'])]
|
cargo = [OrderedDict([('itemName', k), ('itemCount', state['Cargo'][k])]) for k in sorted(state['Cargo'])]
|
||||||
|
|
||||||
# if our cargo differers from last we checked, we're at a station,
|
# Send cargo and materials if changed
|
||||||
# and our flood limit isnt covered, queue an update
|
if this.cargo != cargo:
|
||||||
should_poll = this.cargo != cargo and time.time() - this.last_update_time > FLOOD_LIMIT_SECONDS
|
add_event('setCommanderInventoryCargo', entry['timestamp'], cargo)
|
||||||
|
this.cargo = cargo
|
||||||
# Send event(s) to Inara
|
materials = []
|
||||||
if entry['event'] == 'ShutDown' or len(this.events) > old_events or should_poll:
|
for category in ['Raw', 'Manufactured', 'Encoded']:
|
||||||
|
materials.extend([ OrderedDict([('itemName', k), ('itemCount', state[category][k])]) for k in sorted(state[category]) ])
|
||||||
# Send cargo and materials if changed
|
if this.materials != materials:
|
||||||
if this.cargo != cargo:
|
add_event('setCommanderInventoryMaterials', entry['timestamp'], materials)
|
||||||
add_event('setCommanderInventoryCargo', entry['timestamp'], cargo)
|
this.materials = materials
|
||||||
this.cargo = cargo
|
|
||||||
materials = []
|
|
||||||
for category in ['Raw', 'Manufactured', 'Encoded']:
|
|
||||||
materials.extend([ OrderedDict([('itemName', k), ('itemCount', state[category][k])]) for k in sorted(state[category]) ])
|
|
||||||
if this.materials != materials:
|
|
||||||
add_event('setCommanderInventoryMaterials', entry['timestamp'], materials)
|
|
||||||
this.materials = materials
|
|
||||||
|
|
||||||
# Queue a call to Inara
|
|
||||||
call()
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if __debug__: print_exc()
|
if __debug__: print_exc()
|
||||||
return str(e)
|
return str(e)
|
||||||
|
|
||||||
#
|
|
||||||
# Events that don't need to be sent immediately but will be sent on the next mandatory event
|
|
||||||
#
|
|
||||||
|
|
||||||
# Send credits and stats to Inara on startup only - otherwise may be out of date
|
# Send credits and stats to Inara on startup only - otherwise may be out of date
|
||||||
if entry['event'] == 'LoadGame':
|
if entry['event'] == 'LoadGame':
|
||||||
add_event('setCommanderCredits', entry['timestamp'],
|
add_event('setCommanderCredits', entry['timestamp'],
|
||||||
@ -857,14 +852,22 @@ def add_event(name, timestamp, data):
|
|||||||
('eventData', data),
|
('eventData', data),
|
||||||
]))
|
]))
|
||||||
|
|
||||||
|
def call_timer(wait=FLOOD_LIMIT_SECONDS):
|
||||||
|
while this.timer_run:
|
||||||
|
time.sleep(wait)
|
||||||
|
if this.timer_run: # check again in here just in case we're closing and the stars align
|
||||||
|
call()
|
||||||
|
|
||||||
# Queue a call to Inara, handled in Worker thread
|
# Queue a call to Inara, handled in Worker thread
|
||||||
def call(callback=None):
|
def call(callback=None, force=False):
|
||||||
if not this.events:
|
if not this.events:
|
||||||
return
|
return
|
||||||
|
|
||||||
this.last_update_time = time.time()
|
if (time.time() - config.getint(LAST_UPDATE_CONF_KEY)) <= FLOOD_LIMIT_SECONDS and not force:
|
||||||
|
return
|
||||||
|
|
||||||
|
config.set(LAST_UPDATE_CONF_KEY, int(time.time()))
|
||||||
|
print(f"INARA: {time.asctime()} sending {len(this.events)} entries to inara (call)")
|
||||||
data = OrderedDict([
|
data = OrderedDict([
|
||||||
('header', OrderedDict([
|
('header', OrderedDict([
|
||||||
('appName', applongname),
|
('appName', applongname),
|
||||||
@ -887,6 +890,8 @@ def worker():
|
|||||||
else:
|
else:
|
||||||
(url, data, callback) = item
|
(url, data, callback) = item
|
||||||
|
|
||||||
|
print(f"INARA: {time.asctime()} sending {len(data['events'])} entries to inara (worker)")
|
||||||
|
|
||||||
retrying = 0
|
retrying = 0
|
||||||
while retrying < 3:
|
while retrying < 3:
|
||||||
try:
|
try:
|
||||||
@ -896,28 +901,35 @@ def worker():
|
|||||||
status = reply['header']['eventStatus']
|
status = reply['header']['eventStatus']
|
||||||
if callback:
|
if callback:
|
||||||
callback(reply)
|
callback(reply)
|
||||||
elif status // 100 != 2: # 2xx == OK (maybe with warnings)
|
elif status // 100 != 2: # 2xx == OK (maybe with warnings)
|
||||||
# Log fatal errors
|
# Log fatal errors
|
||||||
print('Inara\t%s %s' % (reply['header']['eventStatus'], reply['header'].get('eventStatusText', '')))
|
print('Inara\t%s %s' % (reply['header']['eventStatus'], reply['header'].get('eventStatusText', '')))
|
||||||
print(json.dumps(data, indent=2, separators = (',', ': ')))
|
print(json.dumps(data, indent=2, separators = (',', ': ')))
|
||||||
plug.show_error(_('Error: Inara {MSG}').format(MSG = reply['header'].get('eventStatusText', status)))
|
plug.show_error(_('Error: Inara {MSG}').format(MSG=reply['header'].get('eventStatusText', status)))
|
||||||
else:
|
else:
|
||||||
# Log individual errors and warnings
|
# Log individual errors and warnings
|
||||||
for data_event, reply_event in zip(data['events'], reply['events']):
|
for data_event, reply_event in zip(data['events'], reply['events']):
|
||||||
if reply_event['eventStatus'] != 200:
|
if reply_event['eventStatus'] != 200:
|
||||||
print('Inara\t%s %s\t%s' % (reply_event['eventStatus'], reply_event.get('eventStatusText', ''), json.dumps(data_event)))
|
print('Inara\t%s %s\t%s' % (reply_event['eventStatus'], reply_event.get('eventStatusText', ''), json.dumps(data_event)))
|
||||||
if reply_event['eventStatus'] // 100 != 2:
|
if reply_event['eventStatus'] // 100 != 2:
|
||||||
plug.show_error(_('Error: Inara {MSG}').format(MSG = '%s, %s' % (data_event['eventName'], reply_event.get('eventStatusText', reply_event['eventStatus']))))
|
plug.show_error(_('Error: Inara {MSG}').format(
|
||||||
if data_event['eventName'] in ['addCommanderTravelCarrierJump', 'addCommanderTravelDock', 'addCommanderTravelFSDJump', 'setCommanderTravelLocation']:
|
MSG=f'{data_event["eventName"]},'
|
||||||
|
f'{reply_event.get("eventStatusText", reply_event["eventStatus"])}'))
|
||||||
|
if data_event['eventName'] in ('addCommanderTravelCarrierJump',
|
||||||
|
'addCommanderTravelDock',
|
||||||
|
'addCommanderTravelFSDJump',
|
||||||
|
'setCommanderTravelLocation'):
|
||||||
this.lastlocation = reply_event.get('eventData', {})
|
this.lastlocation = reply_event.get('eventData', {})
|
||||||
this.system_link.event_generate('<<InaraLocation>>', when="tail") # calls update_location in main thread
|
# calls update_location in main thread
|
||||||
|
this.system_link.event_generate('<<InaraLocation>>', when="tail")
|
||||||
elif data_event['eventName'] in ['addCommanderShip', 'setCommanderShip']:
|
elif data_event['eventName'] in ['addCommanderShip', 'setCommanderShip']:
|
||||||
this.lastship = reply_event.get('eventData', {})
|
this.lastship = reply_event.get('eventData', {})
|
||||||
this.system_link.event_generate('<<InaraShip>>', when="tail") # calls update_ship in main thread
|
# calls update_ship in main thread
|
||||||
|
this.system_link.event_generate('<<InaraShip>>', when="tail")
|
||||||
|
|
||||||
break
|
break
|
||||||
except:
|
except Exception:
|
||||||
if __debug__: print_exc()
|
logger.debug('Sending events', exc_info=e)
|
||||||
retrying += 1
|
retrying += 1
|
||||||
else:
|
else:
|
||||||
if callback:
|
if callback:
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
certifi==2019.9.11
|
certifi==2019.9.11
|
||||||
keyring==19.2.0
|
|
||||||
pathtools>=0.1.2
|
|
||||||
requests>=2.11.1
|
requests>=2.11.1
|
||||||
watchdog>=0.8.3
|
watchdog>=0.8.3
|
||||||
|
semantic-version>=2.8.5
|
||||||
|
2
setup.py
2
setup.py
@ -73,7 +73,6 @@ if sys.platform=='darwin':
|
|||||||
'optimize': 2,
|
'optimize': 2,
|
||||||
'packages': [
|
'packages': [
|
||||||
'requests',
|
'requests',
|
||||||
'keyring.backends',
|
|
||||||
'sqlite3', # Included for plugins
|
'sqlite3', # Included for plugins
|
||||||
],
|
],
|
||||||
'includes': [
|
'includes': [
|
||||||
@ -119,7 +118,6 @@ elif sys.platform=='win32':
|
|||||||
'optimize': 2,
|
'optimize': 2,
|
||||||
'packages': [
|
'packages': [
|
||||||
'requests',
|
'requests',
|
||||||
'keyring.backends',
|
|
||||||
'sqlite3', # Included for plugins
|
'sqlite3', # Included for plugins
|
||||||
],
|
],
|
||||||
'includes': [
|
'includes': [
|
||||||
|
Loading…
x
Reference in New Issue
Block a user