summaryrefslogtreecommitdiff
path: root/discord_rpc/connection/rpc.py
diff options
context:
space:
mode:
authorJesusaves <cpntb1@ymail.com>2024-02-05 11:17:23 -0300
committerJesusaves <cpntb1@ymail.com>2024-02-05 11:17:23 -0300
commit29ffe5de3c308013742b5bd97f7d75b09bd3b427 (patch)
tree7199cecaf204701770de171d007e561589b19762 /discord_rpc/connection/rpc.py
parentf6b8c0c64757c73b6f2063d3a6d93ce2f8f527d5 (diff)
downloadtkinter-29ffe5de3c308013742b5bd97f7d75b09bd3b427.tar.gz
tkinter-29ffe5de3c308013742b5bd97f7d75b09bd3b427.tar.bz2
tkinter-29ffe5de3c308013742b5bd97f7d75b09bd3b427.tar.xz
tkinter-29ffe5de3c308013742b5bd97f7d75b09bd3b427.zip
Some button aligning, a CI template, and Discord RPC
Diffstat (limited to 'discord_rpc/connection/rpc.py')
-rw-r--r--discord_rpc/connection/rpc.py175
1 files changed, 175 insertions, 0 deletions
diff --git a/discord_rpc/connection/rpc.py b/discord_rpc/connection/rpc.py
new file mode 100644
index 0000000..ad11007
--- /dev/null
+++ b/discord_rpc/connection/rpc.py
@@ -0,0 +1,175 @@
+from __future__ import absolute_import
+import logging
+import json
+from ..codes import errorcodes
+from ..codes import opcodes
+from ..codes import statecodes
+from ..util.utils import is_callable, json2dict, range
+from .ipc import BaseConnection
+
+
+_RPC_VERSION = 1
+
+
+class RpcConnection(object):
+ _connection = None
+ _state = statecodes.Disconnected
+ _app_id = None
+ _last_err_code = 0
+ _last_err_msg = ''
+ _pipe_no = 0
+ _on_connect = None
+ _on_disconnect = None
+
+ def __init__(self, app_id, pipe_no=0, log=True, logger=None, log_file=None, log_level=logging.INFO):
+ self._connection = BaseConnection(log=log, logger=logger, log_file=log_file, log_level=log_level)
+ self._app_id = str(app_id)
+ if pipe_no in range(0, 10):
+ self._pipe_no = pipe_no
+
+ def open(self):
+ if self.state == statecodes.Connected:
+ self.log('debug', 'Already connected; no need to open.')
+ return
+
+ if self.state == statecodes.Disconnected:
+ self.connection.open(pipe_no=self._pipe_no)
+ if not self.connection.is_open:
+ self.log('warning', 'Failed to open IPC connection.')
+ return
+
+ if self.state == statecodes.SentHandshake:
+ did_read, data = self.read()
+ if did_read:
+ cmd = data.get('cmd', None)
+ evt = data.get('evt', None)
+ if all(x is not None for x in (cmd, evt)) and cmd == 'DISPATCH' and evt == 'READY':
+ self.state = statecodes.Connected
+ if self.on_connect is not None:
+ self.on_connect(data)
+ self.log('info', 'IPC connected successfully.')
+ else:
+ data = {'v': _RPC_VERSION, 'client_id': self.app_id}
+ if self.connection.write(json.dumps(data), opcodes.Handshake):
+ self.state = statecodes.SentHandshake
+ self.log('debug', 'IPC connection sent handshake.')
+ else:
+ self.log('warning', 'IPC failed to send handshake.')
+ self.close()
+
+ def close(self):
+ if self.on_disconnect is not None and self.state in (statecodes.Connected, statecodes.SentHandshake):
+ self.on_disconnect(self._last_err_code, self._last_err_msg)
+ self.log('debug', 'Attempting to close IPC connection.')
+ if self.connection is not None:
+ self.connection.close()
+ else:
+ self.log('warning', 'Called close without a connection!')
+ self.state = statecodes.Disconnected
+
+ def write(self, data):
+ if isinstance(data, dict):
+ data = json.dumps(data)
+ if not self.connection.write(data, opcodes.Frame):
+ self.log('warning', 'Failed to write frame to IPC connection.')
+ self.close()
+ return False
+ return True
+
+ def read(self):
+ if self.state not in (statecodes.Connected, statecodes.SentHandshake):
+ self.log('debug', 'We aren\'t connected, therefore we cannot read data yet.')
+ return False
+ while True:
+ did_read, opcode, data = self.connection.read()
+ self.log('debug', 'ipc.read(): read: {}, Opcode: {}, data: {}'.format(did_read, opcode, data))
+ if not did_read:
+ err_reason = data[0]
+ if (err_reason == errorcodes.PipeClosed and not self.connection.is_open) \
+ or err_reason == errorcodes.ReadCorrupt:
+ self._last_err_code = err_reason
+ self._last_err_msg = data[1]
+ self.log('debug', 'Failed to read; Connection closed. {}'.format(data))
+ self.close()
+ return False, None
+ if opcode == opcodes.Close:
+ data = json2dict(data)
+ self._last_err_code = data.get('code', -1)
+ self._last_err_msg = data.get('message', '')
+ self.log('debug', 'Opcode == Close. Closing connection.')
+ self.close()
+ return False, None
+ elif opcode == opcodes.Frame:
+ data = json2dict(data)
+ self.log('debug', 'Successful read: {}'.format(data))
+ return True, data
+ elif opcode == opcodes.Ping:
+ if not self.connection.write('', opcodes.Pong):
+ self.log('warning', 'Failed to send Pong message.')
+ self.close()
+ elif opcode == opcodes.Pong:
+ # Discord does nothing here
+ pass
+ else:
+ # something bad happened
+ self._last_err_code = errorcodes.ReadCorrupt
+ self._last_err_msg = 'Bad IPC frame.'
+ self.log('warning', 'Got a bad frame from IPC connection.')
+ self.close()
+ return False, None
+
+ def destroy(self):
+ self.log('info', 'Destroying RPC connection.')
+ self.close()
+ self.connection.destroy()
+ self._connection = None
+
+ def log(self, *args):
+ if self._connection is not None:
+ self._connection.log(*args)
+
+ @property
+ def connection(self):
+ return self._connection
+
+ @property
+ def state(self):
+ return self._state
+
+ @state.setter
+ def state(self, state):
+ if isinstance(state, int) and state in (statecodes.Connected, statecodes.SentHandshake,
+ statecodes.Disconnected, statecodes.AwaitingResponse):
+ self._state = state
+ else:
+ self.log('warning', 'Invalid state number!')
+
+ @property
+ def app_id(self):
+ return self._app_id
+
+ @property
+ def is_open(self):
+ return self.state == statecodes.Connected
+
+ @property
+ def on_connect(self):
+ return self._on_connect
+
+ @on_connect.setter
+ def on_connect(self, callback):
+ if callback is None or is_callable(callback):
+ self._on_connect = callback
+ else:
+ self.log('warning', 'on_connect must be callable/None!')
+
+ @property
+ def on_disconnect(self):
+ return self._on_disconnect
+
+ @on_disconnect.setter
+ def on_disconnect(self, callback):
+ if callback is None or is_callable(callback):
+ self._on_disconnect = callback
+ else:
+ self.log('warning', 'on_disconnect must be callable/None!')