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