diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..d7ba8c7 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "py-discord-sdk"] + path = py_discord_sdk + url = git@github.com:Elite-Kode/py-discord-sdk.git diff --git a/.idea/EDMC-Discord-Presence.iml b/.idea/EDMC-Discord-Presence.iml index 86df155..aae7a6a 100644 --- a/.idea/EDMC-Discord-Presence.iml +++ b/.idea/EDMC-Discord-Presence.iml @@ -1,8 +1,11 @@ - - + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 72a8a68..d5b5957 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -3,5 +3,5 @@ - + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml index e19d45e..61f0798 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -3,6 +3,7 @@ + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml index 94a25f7..5a3dde8 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -1,6 +1,7 @@ - + + \ No newline at end of file diff --git a/Pipfile b/Pipfile deleted file mode 100644 index b723d01..0000000 --- a/Pipfile +++ /dev/null @@ -1,11 +0,0 @@ -[[source]] -name = "pypi" -url = "https://pypi.org/simple" -verify_ssl = true - -[dev-packages] - -[packages] - -[requires] -python_version = "3.7" diff --git a/Pipfile.lock b/Pipfile.lock deleted file mode 100644 index 9a51a28..0000000 --- a/Pipfile.lock +++ /dev/null @@ -1,20 +0,0 @@ -{ - "_meta": { - "hash": { - "sha256": "7e7ef69da7248742e869378f8421880cf8f0017f96d94d086813baa518a65489" - }, - "pipfile-spec": 6, - "requires": { - "python_version": "3.7" - }, - "sources": [ - { - "name": "pypi", - "url": "https://pypi.org/simple", - "verify_ssl": true - } - ] - }, - "default": {}, - "develop": {} -} diff --git a/load.py b/load.py index 317f22f..298ced8 100644 --- a/load.py +++ b/load.py @@ -14,162 +14,88 @@ # limitations under the License. # +import functools +import logging +import threading +import tkinter as tk from os.path import dirname, join -from sys import platform + +import semantic_version import sys import time -import ctypes -import myNotebook as nb -from config import config + import l10n -import functools -try: - import tkinter as tk -except ImportError: - import Tkinter as tk +import myNotebook as nb +from config import config, appname, appversion +from py_discord_sdk import discordsdk as dsdk + +plugin_name = "DiscordPresence" + +logger = logging.getLogger(f'{appname}.{plugin_name}') _ = functools.partial(l10n.Translations.translate, context=__file__) -CLIENT_ID = b'386149818227097610' +CLIENT_ID = 386149818227097610 -VERSION = '2.1.1' +VERSION = '3.0.0' # Add global var for Planet name (landing + around) planet = '' landingPad = '2' -# -# From discrod-rpc.h -# -discord_rpc_lib = 'discord-rpc.dll' -if platform == 'darwin': - discord_rpc_lib = 'libdiscord-rpc.dylib' -elif platform == 'linux' or platform == 'linux2': - discord_rpc_lib = 'libdiscord-rpc.so' -discord_rpc = ctypes.cdll.LoadLibrary(join(dirname(__file__), discord_rpc_lib)) - - -class DiscordRichPresence(ctypes.Structure): - _fields_ = [ - ('state', ctypes.c_char_p), # max 128 bytes - ('details', ctypes.c_char_p), # max 128 bytes - ('startTimestamp', ctypes.c_int64), - ('endTimestamp', ctypes.c_int64), - ('largeImageKey', ctypes.c_char_p), # max 32 bytes - ('largeImageText', ctypes.c_char_p), # max 128 bytes - ('smallImageKey', ctypes.c_char_p), # max 32 bytes - ('smallImageText', ctypes.c_char_p), # max 128 bytes - ('partyId', ctypes.c_char_p), # max 128 bytes - ('partySize', ctypes.c_int), - ('partyMax', ctypes.c_int), - ('matchSecret', ctypes.c_char_p), # max 128 bytes - ('joinSecret', ctypes.c_char_p), # max 128 bytes - ('spectateSecret', ctypes.c_char_p), # max 128 bytes - ('instance', ctypes.c_int8) - ] - - -class DiscordJoinRequest(ctypes.Structure): - _fields_ = [ - ('userId', ctypes.c_char_p), - ('username', ctypes.c_char_p), - ('avatar', ctypes.c_char_p) - ] - - -ReadyProc = ctypes.CFUNCTYPE(None) -DisconnectedProc = ctypes.CFUNCTYPE(None, ctypes.c_int, ctypes.c_char_p) # errorCode, message -ErroredProc = ctypes.CFUNCTYPE(None, ctypes.c_int, ctypes.c_char_p) # errorCode, message -JoinGameProc = ctypes.CFUNCTYPE(None, ctypes.c_char_p) # joinSecret -SpectateGameProc = ctypes.CFUNCTYPE(None, ctypes.c_char_p) # spectateSecret -JoinRequestProc = ctypes.CFUNCTYPE(None, ctypes.POINTER(DiscordJoinRequest)) - - -class DiscordEventHandlers(ctypes.Structure): - _fields_ = [ - ('ready', ReadyProc), - ('disconnected', DisconnectedProc), - ('errored', ErroredProc), - ('joinGame', JoinGameProc), - ('spectateGame', SpectateGameProc), - ('joinRequest', JoinRequestProc) - ] - - -DISCORD_REPLY_NO, DISCORD_REPLY_YES, DISCORD_REPLY_IGNORE = list(range(3)) - -Discord_Initialize = discord_rpc.Discord_Initialize -Discord_Initialize.argtypes = [ctypes.c_char_p, ctypes.POINTER(DiscordEventHandlers), ctypes.c_int, - ctypes.c_char_p] # applicationId, handlers, autoRegister, optionalSteamId -Discord_Shutdown = discord_rpc.Discord_Shutdown -Discord_Shutdown.argtypes = None -Discord_UpdatePresence = discord_rpc.Discord_UpdatePresence -Discord_UpdatePresence.argtypes = [ctypes.POINTER(DiscordRichPresence)] -Discord_Respond = discord_rpc.Discord_Respond -Discord_Respond.argtypes = [ctypes.c_char_p, ctypes.c_int] # userid, reply - - -# -# Callback handlers -# - - -def ready(): - print('ready') - - -def disconnected(errorCode, message): - print('disconnected', errorCode, message) - - -def errored(errorCode, message): - print('errored', errorCode, message) - - -def joinGame(joinSecret): - print('joinGame', joinSecret) - - -def spectateGame(spectateSecret): - print('spectateGame', spectateSecret) - - -def joinRequest(request): - print('joinRequest', request.userId, request.username, request.avatar) - - -event_handlers = DiscordEventHandlers(ReadyProc(ready), - DisconnectedProc(disconnected), - ErroredProc(errored), - JoinGameProc(joinGame), - SpectateGameProc(spectateGame), - JoinRequestProc(joinRequest)) - -Discord_Initialize(CLIENT_ID, event_handlers, True, None) this = sys.modules[__name__] # For holding module globals -this.presence_state = _('Connecting CMDR Interface').encode('utf-8') -this.presence_details = b'' -this.time_start = time.time() + +def callback(result): + logger.info(f'Callback: {result}') + if result == dsdk.Result.ok: + logger.info("Successfully set the activity!") + else: + logger.error(f'Error in callback: {result}') + raise Exception(result) def update_presence(): - presence = DiscordRichPresence() - if config.getint("disable_presence") == 0: - presence.state = this.presence_state - presence.details = this.presence_details - presence.startTimestamp = int(this.time_start) - Discord_UpdatePresence(presence) + if isinstance(appversion, str): + core_version = semantic_version.Version(appversion) + elif callable(appversion): + core_version = appversion() -this.disablePresence = None + logger.info(f'Core EDMC version: {core_version}') + if core_version < semantic_version.Version('5.0.0-beta1'): + logger.info('EDMC core version is before 5.0.0-beta1') + if config.getint("disable_presence") == 0: + this.activity.state = this.presence_state + this.activity.details = this.presence_details + else: + logger.info('EDMC core version is at least 5.0.0-beta1') + if config.get_int("disable_presence") == 0: + this.activity.state = this.presence_state + this.activity.details = this.presence_details + + this.activity.timestamps.start = int(this.time_start) + this.activity_manager.update_activity(this.activity, callback) def plugin_prefs(parent, cmdr, is_beta): """ Return a TK Frame for adding to the EDMC settings dialog. """ - this.disablePresence = tk.IntVar(value=config.getint("disable_presence")) + if isinstance(appversion, str): + core_version = semantic_version.Version(appversion) + + elif callable(appversion): + core_version = appversion() + + logger.info(f'Core EDMC version: {core_version}') + if core_version < semantic_version.Version('5.0.0-beta1'): + logger.info('EDMC core version is before 5.0.0-beta1') + this.disablePresence = tk.IntVar(value=config.getint("disable_presence")) + else: + logger.info('EDMC core version is at least 5.0.0-beta1') + this.disablePresence = tk.IntVar(value=config.get_int("disable_presence")) + frame = nb.Frame(parent) nb.Checkbutton(frame, text="Disable Presence", variable=this.disablePresence).grid() nb.Label(frame, text='Version %s' % VERSION).grid(padx=10, pady=10, sticky=tk.W) @@ -186,84 +112,107 @@ def prefs_changed(cmdr, is_beta): def plugin_start3(plugin_dir): - update_presence() - return 'DiscordPresence' + plugin_path = join(dirname(plugin_dir), plugin_name) + this.app = dsdk.Discord(CLIENT_ID, dsdk.CreateFlags.default, plugin_path) + this.activity_manager = this.app.get_activity_manager() + this.activity = dsdk.Activity() + + this.call_back_thread = threading.Thread(target=run_callbacks) + this.call_back_thread.setDaemon(True) + this.call_back_thread.start() + this.presence_state = _('Connecting CMDR Interface') + this.presence_details = '' + this.time_start = time.time() + + this.disablePresence = None -def plugin_start(): update_presence() return 'DiscordPresence' def plugin_stop(): - Discord_Shutdown() + this.activity_manager.clear_activity(callback) + this.call_back_thread = None def journal_entry(cmdr, is_beta, system, station, entry, state): global planet global landingPad + presence_state = this.presence_state + presence_details = this.presence_details if entry['event'] == 'StartUp': - this.presence_state = _('In system {system}').format(system=system).encode('utf-8') + presence_state = _('In system {system}').format(system=system) if station is None: - this.presence_details = _('Flying in normal space').encode('utf-8') + presence_details = _('Flying in normal space') else: - this.presence_details = _('Docked at {station}').format(station=station).encode('utf-8') + presence_details = _('Docked at {station}').format(station=station) elif entry['event'] == 'Location': - this.presence_state = _('In system {system}').format(system=system).encode('utf-8') + presence_state = _('In system {system}').format(system=system) if station is None: - this.presence_details = _('Flying in normal space').encode('utf-8') + presence_details = _('Flying in normal space') else: - this.presence_details = _('Docked at {station}').format(station=station).encode('utf-8') + presence_details = _('Docked at {station}').format(station=station) elif entry['event'] == 'StartJump': - this.presence_state = _('Jumping').encode('utf-8') + presence_state = _('Jumping') if entry['JumpType'] == 'Hyperspace': - this.presence_details = _('Jumping to system {system}').format(system=entry['StarSystem']).encode('utf-8') + presence_details = _('Jumping to system {system}').format(system=entry['StarSystem']) elif entry['JumpType'] == 'Supercruise': - this.presence_details = _('Preparing for supercruise').encode('utf-8') + presence_details = _('Preparing for supercruise') elif entry['event'] == 'SupercruiseEntry': - this.presence_state = _('In system {system}').format(system=system).encode('utf-8') - this.presence_details = _('Supercruising').encode('utf-8') + presence_state = _('In system {system}').format(system=system) + presence_details = _('Supercruising') elif entry['event'] == 'SupercruiseExit': - this.presence_state = _('In system {system}').format(system=system).encode('utf-8') - this.presence_details = _('Flying in normal space').encode('utf-8') + presence_state = _('In system {system}').format(system=system) + presence_details = _('Flying in normal space') elif entry['event'] == 'FSDJump': - this.presence_state = _('In system {system}').format(system=system).encode('utf-8') - this.presence_details = _('Supercruising').encode('utf-8') + presence_state = _('In system {system}').format(system=system) + presence_details = _('Supercruising') elif entry['event'] == 'Docked': - this.presence_state = _('In system {system}').format(system=system).encode('utf-8') - this.presence_details = _('Docked at {station}').format(station=station).encode('utf-8') + presence_state = _('In system {system}').format(system=system) + presence_details = _('Docked at {station}').format(station=station) elif entry['event'] == 'Undocked': - this.presence_state = _('In system {system}').format(system=system).encode('utf-8') - this.presence_details = _('Flying in normal space').encode('utf-8') + presence_state = _('In system {system}').format(system=system) + presence_details = _('Flying in normal space') elif entry['event'] == 'ShutDown': - this.presence_state = _('Connecting CMDR Interface').encode('utf-8') - this.presence_details = b'' + presence_state = _('Connecting CMDR Interface') + presence_details = '' elif entry['event'] == 'DockingGranted': landingPad = entry['LandingPad'] elif entry['event'] == 'Music': if entry['MusicTrack'] == 'MainMenu': - this.presence_state = _('Connecting CMDR Interface').encode('utf-8') - this.presence_details = b'' + presence_state = _('Connecting CMDR Interface') + presence_details = '' # Todo: This elif might not be executed on undocked. Functionality can be improved elif entry['event'] == 'Undocked' or entry['event'] == 'DockingCancelled' or entry['event'] == 'DockingTimeout': - this.presence_details = _('Flying near {station}').format(station=entry['StationName']).encode('utf-8') + presence_details = _('Flying near {station}').format(station=entry['StationName']) # Planetary events elif entry['event'] == 'ApproachBody': - this.presence_details = _('Approaching {body}').format(body=entry['Body']).encode('utf-8') + presence_details = _('Approaching {body}').format(body=entry['Body']) planet = entry['Body'] elif entry['event'] == 'Touchdown' and entry['PlayerControlled']: - this.presence_details = _('Landed on {body}').format(body=planet).encode('utf-8') + presence_details = _('Landed on {body}').format(body=planet) elif entry['event'] == 'Liftoff' and entry['PlayerControlled']: if entry['PlayerControlled']: - this.presence_details = _('Flying around {body}').format(body=planet).encode('utf-8') + presence_details = _('Flying around {body}').format(body=planet) else: - this.presence_details = _('In SRV on {body}, ship in orbit').format(body=planet).encode('utf-8') + presence_details = _('In SRV on {body}, ship in orbit').format(body=planet) elif entry['event'] == 'LeaveBody': - this.presence_details = _('Supercruising').encode('utf-8') + presence_details = _('Supercruising') # EXTERNAL VEHICLE EVENTS elif entry['event'] == 'LaunchSRV': - this.presence_details = _('In SRV on {body}').format(body=planet).encode('utf-8') + presence_details = _('In SRV on {body}').format(body=planet) elif entry['event'] == 'DockSRV': - this.presence_details = _('Landed on {body}').format(body=planet).encode('utf-8') - update_presence() + presence_details = _('Landed on {body}').format(body=planet) + + if presence_state != this.presence_state or presence_details != this.presence_details: + this.presence_state = presence_state + this.presence_details = presence_details + update_presence() + + +def run_callbacks(): + while True: + time.sleep(1 / 10) + this.app.run_callbacks() diff --git a/py_discord_sdk b/py_discord_sdk new file mode 160000 index 0000000..4e208a2 --- /dev/null +++ b/py_discord_sdk @@ -0,0 +1 @@ +Subproject commit 4e208a2c4e52378b001620bb4bd99a52b7ed4a3a