[2051] And a few more, with timezones

This commit is contained in:
David Sangrey committed 2023-11-17 19:44:36 -05:00
1 parent 45a38ccdaa
commit 4dd83f5fa4
4 files changed
+105 -64

No files matched your search

+2 -1
View File
@@ -11,6 +11,7 @@ import json
import sys
import time
import tkinter as tk
from calendar import timegm
from os.path import getsize, isdir, isfile, join
from typing import Any, cast
from watchdog.observers.api import BaseObserver
@@ -182,7 +183,7 @@ class Dashboard(FileSystemEventHandler):
if data: # Can be empty if polling while the file is being re-written
entry = json.loads(data)
# Status file is shared between beta and live. Filter out status not in this game session.
entry_timestamp = time.mktime(time.strptime(entry['timestamp'], '%Y-%m-%dT%H:%M:%SZ'))
entry_timestamp = timegm(time.strptime(entry['timestamp'], '%Y-%m-%dT%H:%M:%SZ'))
if entry_timestamp >= self.session_start and self.status != entry:
self.status = entry
self.root.event_generate('<<DashboardEvent>>', when="tail")
+18 -16
View File
@@ -1,13 +1,17 @@
"""Export ship loadout in Companion API json format."""
"""
loadout.py - Export ship loadout in Companion API json format.
Copyright (c) EDCD, All Rights Reserved
Licensed under the GNU General Public License.
See LICENSE file.
"""
from __future__ import annotations
import json
import os
import pathlib
import re
import time
from os import listdir
from os.path import join
from typing import Optional
import companion
import util_ships
from config import config
@@ -16,7 +20,7 @@ from EDMCLogging import get_main_logger
logger = get_main_logger()
def export(data: companion.CAPIData, requested_filename: Optional[str] = None) -> None:
def export(data: companion.CAPIData, requested_filename: str | None = None) -> None:
"""
Write Ship Loadout in Companion API JSON format.
@@ -32,15 +36,14 @@ def export(data: companion.CAPIData, requested_filename: Optional[str] = None) -
with open(requested_filename, 'wt') as h:
h.write(string)
return
elif not requested_filename:
if not requested_filename:
logger.error(f"{requested_filename=} is not valid")
return
# Look for last ship of this type
ship = util_ships.ship_file_name(data['ship'].get('shipName'), data['ship']['name'])
regexp = re.compile(re.escape(ship) + r'\.\d\d\d\d\-\d\d\-\d\dT\d\d\.\d\d\.\d\d\.txt')
oldfiles = sorted([x for x in os.listdir(config.get_str('outdir')) if regexp.match(x)])
regexp = re.compile(re.escape(ship) + r'\.\d\d\d\d-\d\d-\d\dT\d\d\.\d\d\.\d\d\.txt')
oldfiles = sorted([x for x in listdir(config.get_str('outdir')) if regexp.match(x)])
if oldfiles:
with open(join(config.get_str('outdir'), oldfiles[-1]), 'rU') as h:
if h.read() == string:
@@ -50,10 +53,9 @@ def export(data: companion.CAPIData, requested_filename: Optional[str] = None) -
# Write
with open(
pathlib.Path(config.get_str('outdir')) / pathlib.Path(
ship + '.' + time.strftime('%Y-%m-%dT%H.%M.%S', time.localtime(query_time)) + '.txt'
),
'wt'
) as h:
output_directory = config.get_str('outdir')
ship_time = time.strftime('%Y-%m-%dT%H.%M.%S', time.localtime(query_time))
file_path = join(output_directory, f"{ship}.{ship_time}.txt")
with open(file_path, 'wt') as h:
h.write(string)
+51 -28
View File
@@ -1,12 +1,17 @@
"""protocol handler for cAPI authorisation."""
# spell-checker: words ntdll GURL alloc wfile instantiatable pyright
"""
protocol.py - Protocol Handler for cAPI Auth.
Copyright (c) EDCD, All Rights Reserved
Licensed under the GNU General Public License.
See LICENSE file.
"""
from __future__ import annotations
import os
import sys
import threading
import urllib.error
import urllib.parse
import urllib.request
from typing import TYPE_CHECKING, Optional, Type
from urllib import parse
from typing import TYPE_CHECKING, Type
from config import config
from constants import appname, protocolhandler_redirect
@@ -34,7 +39,7 @@ class GenericProtocolHandler:
def __init__(self) -> None:
self.redirect = protocolhandler_redirect # Base redirection URL
self.master: 'tkinter.Tk' = None # type: ignore
self.lastpayload: Optional[str] = None
self.lastpayload: str | None = None
def start(self, master: 'tkinter.Tk') -> None:
"""Start Protocol Handler."""
@@ -42,7 +47,6 @@ class GenericProtocolHandler:
def close(self) -> None:
"""Stop / Close Protocol Handler."""
pass
def event(self, url: str) -> None:
"""Generate an auth event."""
@@ -75,7 +79,7 @@ if sys.platform == 'darwin' and getattr(sys, 'frozen', False): # noqa: C901 # i
def start(self, master: 'tkinter.Tk') -> None:
"""Start Protocol Handler."""
GenericProtocolHandler.start(self, master)
self.lasturl: Optional[str] = None
self.lasturl: str | None = None
self.eventhandler = EventHandler.alloc().init()
def poll(self) -> None:
@@ -106,12 +110,11 @@ if sys.platform == 'darwin' and getattr(sys, 'frozen', False): # noqa: C901 # i
def handleEvent_withReplyEvent_(self, event, replyEvent) -> None: # noqa: N802 N803 # Required to override
"""Actual event handling from NSAppleEventManager."""
protocolhandler.lasturl = urllib.parse.unquote( # noqa: F821: type: ignore # It's going to be a DPH in
# this code
protocolhandler.lasturl = parse.unquote(
event.paramDescriptorForKeyword_(keyDirectObject).stringValue()
).strip()
protocolhandler.master.after(DarwinProtocolHandler.POLL, protocolhandler.poll) # noqa: F821 # type: ignore
protocolhandler.master.after(DarwinProtocolHandler.POLL, protocolhandler.poll)
elif (config.auth_force_edmc_protocol
@@ -124,9 +127,8 @@ elif (config.auth_force_edmc_protocol
# This could be false if you use auth_force_edmc_protocol, but then you get to keep the pieces
assert sys.platform == 'win32'
# spell-checker: words HBRUSH HICON WPARAM wstring WNDCLASS HMENU HGLOBAL
from ctypes import windll # type: ignore
from ctypes import ( # type: ignore
POINTER, WINFUNCTYPE, Structure, byref, c_long, c_void_p, create_unicode_buffer, wstring_at
windll, POINTER, WINFUNCTYPE, Structure, byref, c_long, c_void_p, create_unicode_buffer, wstring_at
)
from ctypes.wintypes import (
ATOM, BOOL, DWORD, HBRUSH, HGLOBAL, HICON, HINSTANCE, HMENU, HWND, INT, LPARAM, LPCWSTR, LPMSG, LPVOID, LPWSTR,
@@ -271,7 +273,7 @@ elif (config.auth_force_edmc_protocol
def __init__(self) -> None:
super().__init__()
self.thread: Optional[threading.Thread] = None
self.thread: threading.Thread | None = None
def start(self, master: 'tkinter.Tk') -> None:
"""Start the DDE thread."""
@@ -342,7 +344,7 @@ elif (config.auth_force_edmc_protocol
if args.lower().startswith('open("') and args.endswith('")'):
logger.trace_if('frontier-auth.windows', f'args are: {args}')
url = urllib.parse.unquote(args[6:-2]).strip()
url = parse.unquote(args[6:-2]).strip()
if url.startswith(self.redirect):
logger.debug(f'Message starts with {self.redirect}')
self.event(url)
@@ -381,7 +383,7 @@ else: # Linux / Run from source
if not os.getenv("EDMC_NO_UI"):
logger.info(f'Web server listening on {self.redirect}')
self.thread: Optional[threading.Thread] = None
self.thread: threading.Thread | None = None
def start(self, master: 'tkinter.Tk') -> None:
"""Start the HTTP server thread."""
@@ -420,15 +422,14 @@ else: # Linux / Run from source
def parse(self) -> bool:
"""Parse a request."""
logger.trace_if('frontier-auth.http', f'Got message on path: {self.path}')
url = urllib.parse.unquote(self.path)
url = parse.unquote(self.path)
if url.startswith('/auth'):
logger.debug('Request starts with /auth, sending to protocolhandler.event()')
protocolhandler.event(url) # noqa: F821
protocolhandler.event(url)
self.send_response(200)
return True
else:
self.send_response(404) # Not found
return False
self.send_response(404) # Not found
return False
def do_HEAD(self) -> None: # noqa: N802 # Required to override
"""Handle HEAD Request."""
@@ -440,14 +441,37 @@ else: # Linux / Run from source
if self.parse():
self.send_header('Content-Type', 'text/html')
self.end_headers()
self.wfile.write('<html><head><title>Authentication successful</title></head>'.encode('utf-8'))
self.wfile.write('<body><p>Authentication successful</p></body>'.encode('utf-8'))
self.wfile.write(self._generate_auth_response().encode('utf-8'))
else:
self.send_response(404)
self.end_headers()
def log_request(self, code: int | str = '-', size: int | str = '-') -> None:
"""Override to prevent logging."""
pass
def _generate_auth_response(self) -> str:
"""
Generate the authentication response HTML.
:return: The HTML content of the authentication response.
"""
return (
'<html>'
'<head>'
'<title>Authentication successful - Elite: Dangerous</title>'
'<style>'
'body { background-color: #000; color: #fff; font-family: "Helvetica Neue", Arial, sans-serif; }'
'h1 { text-align: center; margin-top: 100px; }'
'p { text-align: center; }'
'</style>'
'</head>'
'<body>'
'<h1>Authentication successful</h1>'
'<p>Thank you for authenticating.</p>'
'<p>Please close this browser tab now.</p>'
'</body>'
'</html>'
)
def get_handler_impl() -> Type[GenericProtocolHandler]:
@@ -459,14 +483,13 @@ def get_handler_impl() -> Type[GenericProtocolHandler]:
if sys.platform == 'darwin' and getattr(sys, 'frozen', False):
return DarwinProtocolHandler # pyright: reportUnboundVariable=false
elif (
if (
(sys.platform == 'win32' and config.auth_force_edmc_protocol)
or (getattr(sys, 'frozen', False) and not is_wine and not config.auth_force_localserver)
):
return WindowsProtocolHandler
else:
return LinuxProtocolHandler
return LinuxProtocolHandler
# *late init* singleton
+34 -19
View File
@@ -1,5 +1,9 @@
"""
A clickable ttk label for HTTP links.
ttkHyperlinkLabel.py - Clickable ttk labels.
Copyright (c) EDCD, All Rights Reserved
Licensed under the GNU General Public License.
See LICENSE file.
In addition to standard ttk.Label arguments, takes the following arguments:
url: The URL as a string that the user will be sent to on clicking on
@@ -14,6 +18,8 @@ In addition to standard ttk.Label arguments, takes the following arguments:
May be imported by plugins
"""
from __future__ import annotations
import sys
import tkinter as tk
import webbrowser
@@ -26,14 +32,22 @@ if TYPE_CHECKING:
# FIXME: Split this into multi-file module to separate the platforms
class HyperlinkLabel(sys.platform == 'darwin' and tk.Label or ttk.Label, object): # type: ignore
class HyperlinkLabel(sys.platform == 'darwin' and tk.Label or ttk.Label): # type: ignore
"""Clickable label for HTTP links."""
def __init__(self, master: tk.Frame | None = None, **kw: Any) -> None:
self.url = 'url' in kw and kw.pop('url') or None
"""
Initialize the HyperlinkLabel.
:param master: The master widget.
:param kw: Additional keyword arguments.
"""
self.font_u: tk_font.Font
self.font_n = None
self.url = kw.pop('url', None)
self.popup_copy = kw.pop('popup_copy', False)
self.underline = kw.pop('underline', None) # override ttk.Label's underline
self.foreground = kw.get('foreground') or 'blue'
self.foreground = kw.get('foreground', 'blue')
self.disabledforeground = kw.pop('disabledforeground', ttk.Style().lookup(
'TLabel', 'foreground', ('disabled',))) # ttk.Label doesn't support disabledforeground option
@@ -44,11 +58,11 @@ class HyperlinkLabel(sys.platform == 'darwin' and tk.Label or ttk.Label, object)
tk.Label.__init__(self, master, **kw)
else:
ttk.Label.__init__(self, master, **kw) # type: ignore
ttk.Label.__init__(self, master, **kw)
self.bind('<Button-1>', self._click)
self.menu = tk.Menu(None, tearoff=tk.FALSE)
self.menu = tk.Menu(tearoff=tk.FALSE)
# LANG: Label for 'Copy' as in 'Copy and Paste'
self.menu.add_command(label=_('Copy'), command=self.copy) # As in Copy and Paste
self.bind(sys.platform == 'darwin' and '<Button-2>' or '<Button-3>', self._contextmenu)
@@ -74,29 +88,30 @@ class HyperlinkLabel(sys.platform == 'darwin' and tk.Label or ttk.Label, object)
setattr(self, thing, kw[thing])
# Emulate disabledforeground option for ttk.Label
if kw.get('state') == tk.DISABLED:
if 'foreground' not in kw:
if 'state' in kw:
state = kw['state']
if state == tk.DISABLED and 'foreground' not in kw:
kw['foreground'] = self.disabledforeground
elif 'state' in kw:
if 'foreground' not in kw:
elif state != tk.DISABLED and 'foreground' not in kw:
kw['foreground'] = self.foreground
if 'font' in kw:
self.font_n = kw['font']
self.font_u = tk_font.Font(font=self.font_n)
self.font_u.configure(underline=True)
kw['font'] = self.underline is True and self.font_u or self.font_n
kw['font'] = self.font_u if self.underline is True else self.font_n
if 'cursor' not in kw:
if (kw['state'] if 'state' in kw else str(self['state'])) == tk.DISABLED:
state = kw.get('state', str(self['state']))
if state == tk.DISABLED:
kw['cursor'] = 'arrow' # System default
elif self.url and (kw['text'] if 'text' in kw else self['text']):
kw['cursor'] = sys.platform == 'darwin' and 'pointinghand' or 'hand2'
kw['cursor'] = 'pointinghand' if sys.platform == 'darwin' else 'hand2'
else:
kw['cursor'] = (sys.platform == 'darwin' and 'notallowed') or (
sys.platform == 'win32' and 'no') or 'circle'
kw['cursor'] = 'notallowed' if sys.platform == 'darwin' else (
'no' if sys.platform == 'win32' else 'circle')
return super(HyperlinkLabel, self).configure(cnf, **kw)
return super().configure(cnf, **kw)
def __setitem__(self, key: str, value: Any) -> None:
"""
@@ -105,15 +120,15 @@ class HyperlinkLabel(sys.platform == 'darwin' and tk.Label or ttk.Label, object)
:param key: option name
:param value: option value
"""
self.configure(None, **{key: value})
self.configure(**{key: value})
def _enter(self, event: tk.Event) -> None:
if self.url and self.underline is not False and str(self['state']) != tk.DISABLED:
super(HyperlinkLabel, self).configure(font=self.font_u)
super().configure(font=self.font_u)
def _leave(self, event: tk.Event) -> None:
if not self.underline:
super(HyperlinkLabel, self).configure(font=self.font_n)
super().configure(font=self.font_n)
def _click(self, event: tk.Event) -> None:
if self.url and self['text'] and str(self['state']) != tk.DISABLED: