diff --git a/resources/analytics_handler.py b/resources/analytics_handler.py index 64e276966..d72a16f4b 100644 --- a/resources/analytics_handler.py +++ b/resources/analytics_handler.py @@ -9,8 +9,9 @@ from resources import network_handler, constants, global_settings DATE_FORMAT: str = "%Y-%m-%d %H-%M-%S" ANALYTICS_SERVER: str = "" SITE_KEY: str = "" +CRASH_URL: str = ANALYTICS_SERVER + "/crash" -VALID_ENTRIES: dict = { +VALID_ANALYTICS_ENTRIES: dict = { 'KEY': str, # Prevent abuse (embedded at compile time) 'UNIQUE_IDENTITY': str, # Host's UUID as SHA1 hash 'APPLICATION_NAME': str, # ex. OpenCore Legacy Patcher @@ -23,17 +24,56 @@ VALID_ENTRIES: dict = { 'TIMESTAMP': datetime.datetime, # ex. 2021-09-01-12-00-00 } +VALID_CRASH_ENTRIES: dict = { + 'KEY': str, # Prevent abuse (embedded at compile time) + 'APPLICATION_VERSION': str, # ex. 0.2.0 + 'OS_VERSION': str, # ex. 10.15.7 + 'MODEL': str, # ex. MacBookPro11,5 + 'TIMESTAMP': datetime.datetime, # ex. 2021-09-01-12-00-00 + 'CRASH_LOG': str, # ex. "This is a crash log" +} + class Analytics: def __init__(self, global_constants: constants.Constants) -> None: self.constants: constants.Constants = global_constants + self.unique_identity = str(self.constants.computer.uuid_sha1) + self.application = str("OpenCore Legacy Patcher") + self.version = str(self.constants.patcher_version) + self.os = str(self.constants.detected_os_version) + self.model = str(self.constants.computer.real_model) + self.date = str(datetime.datetime.now().strftime(DATE_FORMAT)) + + def send_analytics(self) -> None: if global_settings.GlobalEnviromentSettings().read_property("DisableCrashAndAnalyticsReporting") is True: return self._generate_base_data() - self._post_data() + self._post_analytics_data() + + + def send_crash_report(self, log_file: Path) -> None: + if ANALYTICS_SERVER == "": + return + if SITE_KEY == "": + return + if global_settings.GlobalEnviromentSettings().read_property("DisableCrashAndAnalyticsReporting") is True: + return + if not log_file.exists(): + return + + crash_data= { + "KEY": SITE_KEY, + "APPLICATION_VERSION": self.version, + "OS_VERSION": self.os, + "MODEL": self.model, + "TIMESTAMP": self.date, + "CRASH_LOG": log_file.read_text() + } + + network_handler.NetworkUtilities().post(CRASH_URL, json = crash_data) def _get_country(self) -> str: @@ -54,12 +94,6 @@ class Analytics: def _generate_base_data(self) -> None: - - self.unique_identity = str(self.constants.computer.uuid_sha1) - self.application = str("OpenCore Legacy Patcher") - self.version = str(self.constants.patcher_version) - self.os = str( self.constants.detected_os_version) - self.model = str(self.constants.computer.real_model) self.gpus = [] self.firmware = str(self.constants.computer.firmware_vendor) @@ -78,14 +112,14 @@ class Analytics: 'GPUS': self.gpus, 'FIRMWARE': self.firmware, 'LOCATION': self.location, - 'TIMESTAMP': str(datetime.datetime.now().strftime(DATE_FORMAT)), + 'TIMESTAMP': self.date, } # convert to JSON: self.data = json.dumps(self.data) - def _post_data(self) -> None: + def _post_analytics_data(self) -> None: # Post data to analytics server if ANALYTICS_SERVER == "": return diff --git a/resources/logging_handler.py b/resources/logging_handler.py index 32ab74a04..e9dc3a6a0 100644 --- a/resources/logging_handler.py +++ b/resources/logging_handler.py @@ -9,7 +9,7 @@ import applescript from pathlib import Path -from resources import constants +from resources import constants, analytics_handler class InitializeLoggingSupport: @@ -210,6 +210,12 @@ class InitializeLoggingSupport: """ logging.error("Uncaught exception in main thread", exc_info=(type, value, tb)) + logging.info("System Information:") + logging.info(f" Host Model Identifier: {self.constants.computer.real_model}") + logging.info(f" macOS Version: {self.constants.detected_os_version}") + logging.info(f" Patcher Version: {self.constants.patcher_version}") + logging.info(f" Arguments passed to Patcher: {sys.argv}") + if self.constants.cli_mode is True: return @@ -217,12 +223,21 @@ class InitializeLoggingSupport: error_msg += f"{type.__name__}: {value}" if tb: error_msg += f"\n\n{traceback.extract_tb(tb)[-1]}" - error_msg += "\n\nPlease report this error on our Discord server." + error_msg += "\n\nSend crash report to Dortania?" - applescript.AppleScript(f'display dialog "{error_msg}" with title "OpenCore Legacy Patcher ({self.constants.patcher_version})" buttons {{"OK"}} default button "OK" with icon caution giving up after 30').run() + # applescript.AppleScript(f'display dialog "{error_msg}" with title "OpenCore Legacy Patcher ({self.constants.patcher_version})" buttons {{"OK"}} default button "OK" with icon caution giving up after 30').run() - # Open log location - subprocess.run(["open", "--reveal", self.log_filepath]) + # Ask user if they want to send crash report + try: + result = applescript.AppleScript(f'display dialog "{error_msg}" with title "OpenCore Legacy Patcher ({self.constants.patcher_version})" buttons {{"Yes", "No"}} default button "Yes" with icon caution giving up after 30').run() + except Exception as e: + logging.error(f"Failed to display crash report dialog: {e}") + return + + if result[applescript.AEType(b'bhit')] != "Yes": + return + + threading.Thread(target=analytics_handler.Analytics(self.constants).send_crash_report, args=(self.log_filepath,)).start() def custom_thread_excepthook(args) -> None: diff --git a/resources/main.py b/resources/main.py index e6fb5d9f6..e0448c12e 100644 --- a/resources/main.py +++ b/resources/main.py @@ -92,7 +92,7 @@ class OpenCoreLegacyPatcher: # Generate defaults defaults.GenerateDefaults(self.computer.real_model, True, self.constants) - threading.Thread(target=analytics_handler.Analytics, args=(self.constants,)).start() + threading.Thread(target=analytics_handler.Analytics(self.constants).send_analytics).start() if utilities.check_cli_args() is None: self.constants.cli_mode = False