import json import os import socket import sys import struct import time from .exceptions import * OP_HANDSHAKE = 0 OP_FRAME = 1 OP_CLOSE = 2 OP_PING = 3 OP_PONG = 4 class RPC: def __init__(self, client_id): self.client_id = client_id self._connect() self._do_handshake() self._output = None self.show_output = False @classmethod def set_id(self, app_id): app_id = str(app_id) platform = sys.platform if platform == 'win32': return DiscordWindows(app_id) else: return DiscordUnix(app_id) def _connect(self): pass def _do_handshake(self): op, data = self.send_recv({'v': 1, 'client_id': self.client_id}, op=OP_HANDSHAKE) if op == OP_FRAME and data['cmd'] == 'DISPATCH' and data['evt'] == 'READY': return else: if op == OP_CLOSE: self.close() if data['code'] == 4000: raise InvalidID() raise RPCException(f"Unknown error : {data}") def _write(self, data:bytes): pass def _recv(self, size:int) -> bytes: pass def _recv_header(self): header = self._recv_exact(8) return struct.unpack(" bytes: buf = b"" while size_remaining: chunk = self._recv(size_remaining) buf += chunk size_remaining -= len(chunk) return buf def close(self): try: self.send({}, op=OP_CLOSE) finally: self._close() def _close(self): pass def __enter__(self): return self def __exit__(self, *_): self.close() def send_recv(self, data, op=OP_FRAME): self.send(data, op) return self.recv() def send(self, data, op=OP_FRAME): data = json.dumps(data, separators=(',', ':')).encode('UTF-8') header = struct.pack(" bytes: return self._open.read(size) def _close(self): self._open.close() class DiscordUnix(RPC): def _connect(self): self._socket = socket.socket(socket.AF_UNIX) pipe_path = self._get_pipe_path() for i in range(10): path = pipe_path.format(i) if not os.path.exists(path): continue try: self._socket.connect(path) except OSError as e: raise Error("Failed to open {!r}: {}".format(path, e)) else: break else: raise DiscordNotOpened def _get_pipe_path(self): env_keys = ('XDG_RUNTIME_DIR', 'TMPDIR', 'TMP', 'TEMP') for env_key in env_keys: dir_path = os.environ.get(env_key) if dir_path: break else: dir_path = '/tmp' return os.path.join(dir_path, 'discord-ipc-{}') def _write(self, data:bytes): self._socket.sendall(data) def _recv(self, size:int) -> bytes: return self._socket.recv(size) def _close(self): self._socket.close()