pypresence added

pull/1/head
davehornik 2023-04-01 05:00:56 +02:00
parent 9913ac793c
commit 195515aeb5
22 changed files with 1170 additions and 0 deletions

View File

@ -0,0 +1 @@
pip

View File

@ -0,0 +1,12 @@
Copyright 2021 qwertyquerty
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -0,0 +1,93 @@
Metadata-Version: 2.1
Name: pypresence
Version: 4.2.1
Summary: Discord RPC client written in Python
Home-page: https://github.com/qwertyquerty/pypresence
Author: qwertyquerty
License: MIT
Keywords: discord rich presence pypresence rpc api wrapper gamers chat irc
Platform: Windows
Platform: Linux
Platform: OSX
Classifier: Development Status :: 5 - Production/Stable
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: Microsoft :: Windows
Classifier: Operating System :: POSIX :: Linux
Classifier: Operating System :: MacOS :: MacOS X
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Intended Audience :: Developers
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Software Development :: Libraries
Classifier: Topic :: Communications :: Chat
Classifier: Framework :: AsyncIO
Requires-Python: >=3.5
Description-Content-Type: text/markdown
License-File: LICENSE
> A Discord Rich Presence Client in Python? Looks like you've come to the right place.
Written by: [qwertyquerty](https://github.com/qwertyquerty)
Notable Contributors: [GiovanniMCMXCIX](https://github.com/GiovanniMCMXCIX), [GhostofGoes](https://github.com/GhostofGoes)
[![GitHub stars](https://img.shields.io/github/stars/qwertyquerty/pypresence.svg?style=for-the-badge&label=Stars)](https://github.com/qwertyquerty/pypresence) [![license](https://img.shields.io/github/license/qwertyquerty/pypresence.svg?style=for-the-badge)](https://github.com/qwertyquerty/pypresence/blob/master/LICENSE) ![GitHub last commit](https://img.shields.io/github/last-commit/qwertyquerty/pypresence.svg?style=for-the-badge) ![GitHub top language](https://img.shields.io/github/languages/top/qwertyquerty/pypresence.svg?style=for-the-badge) ![PyPI](https://img.shields.io/pypi/v/pypresence.svg?style=for-the-badge)
## NOTE: Only Python versions 3.8 and above are supported.
## NOTICE: Activity() class has been removed in 4.0.0
### [Documentation](https://qwertyquerty.github.io/pypresence/html/index.html), [Discord Server](https://discord.gg/JF3kg77), [Patreon](https://www.patreon.com/qwertyquerty)
----------
**Use this badge in your project's Readme to show you're using pypresence! The markdown code is below.**
[![pypresence](https://img.shields.io/badge/using-pypresence-00bb88.svg?style=for-the-badge&logo=discord&logoWidth=20)](https://github.com/qwertyquerty/pypresence)
```markdown
[![pypresence](https://img.shields.io/badge/using-pypresence-00bb88.svg?style=for-the-badge&logo=discord&logoWidth=20)](https://github.com/qwertyquerty/pypresence)
```
----------
# Installation
Install pypresence with **`pip`**
For the latest development version:
### `pip install https://github.com/qwertyquerty/pypresence/archive/master.zip`
For the latest stable version:
### `pip install pypresence`
----------
# Documentation
> **NOTE**: You need an **authorized app** to do anything besides rich presence!
#### [pypresence Documentation](https://qwertyquerty.github.io/pypresence/html/index.html)
#### [Discord Rich Presence Documentation](https://discordapp.com/developers/docs/rich-presence/how-to)
#### [Discord RPC Documentation](https://discordapp.com/developers/docs/topics/rpc)
#### [pyresence Discord Support Server](https://discord.gg/JF3kg77)
#### [Discord API Support Server](https://discord.gg/discord-api)
----------
# Examples
Examples can be found in the [examples](https://github.com/qwertyquerty/pypresence/tree/master/examples) directory, and you can contribute your own examples if you wish, just read [examples.md](https://github.com/qwertyquerty/pypresence/blob/master/examples/examples.md)!

View File

@ -0,0 +1,22 @@
pypresence-4.2.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
pypresence-4.2.1.dist-info/LICENSE,sha256=IL42tAZ9O6DIG5I1n2zGEZmtvUwCbvAatrGNpeO5uqM,1458
pypresence-4.2.1.dist-info/METADATA,sha256=UcCkjfMyBRIp2fM3u8xN9tzXqdjD34Jvrgu1x8Db87g,3954
pypresence-4.2.1.dist-info/RECORD,,
pypresence-4.2.1.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
pypresence-4.2.1.dist-info/WHEEL,sha256=WzZ8cwjh8l0jtULNjYq1Hpr-WCqCRgPr--TX4P5I1Wo,110
pypresence-4.2.1.dist-info/top_level.txt,sha256=HgPRAKZXYeILcC2btf3X4WYdVa6CdfiT2Kl_O42RSuY,11
pypresence-4.2.1.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
pypresence/__init__.py,sha256=sSVp9Oda3CZzK9stAnTsxXXwoPPbGIiujxjlRqaXIAY,385
pypresence/__pycache__/__init__.cpython-311.pyc,,
pypresence/__pycache__/baseclient.cpython-311.pyc,,
pypresence/__pycache__/client.cpython-311.pyc,,
pypresence/__pycache__/exceptions.cpython-311.pyc,,
pypresence/__pycache__/payloads.cpython-311.pyc,,
pypresence/__pycache__/presence.cpython-311.pyc,,
pypresence/__pycache__/utils.cpython-311.pyc,,
pypresence/baseclient.py,sha256=GO-CTAL-arUPX06-W-RJuMWjnatzJ1AExJD4V3tWfg8,4520
pypresence/client.py,sha256=qEAjRdN9Q_IUzzAKe4v1587oor6pj4HDq86GZggcwWM,15461
pypresence/exceptions.py,sha256=QKjRCAPf-_NakKxxOama1-l9ircpMr2rhKTCCbcbQi0,1717
pypresence/payloads.py,sha256=0-a_Mau58mqEorJaoCEFTB1VpmCU61CuKV8jFTQhmEE,8560
pypresence/presence.py,sha256=5B-MvOCtvfQVwwjn5d4zwE1EqUSriWJ7WOkYLokmWr4,3629
pypresence/utils.py,sha256=hOdCaK8Ic-vp-RMD7lKsOiaTdbC8gCOmUg53N5lTsvs,2056

View File

@ -0,0 +1,6 @@
Wheel-Version: 1.0
Generator: bdist_wheel (0.37.0)
Root-Is-Purelib: true
Tag: py2-none-any
Tag: py3-none-any

View File

@ -0,0 +1 @@
pypresence

View File

@ -0,0 +1 @@

View File

@ -0,0 +1,17 @@
"""
Python RPC Client for Discord
-----------------------------
By: qwertyquerty and LewdNeko
"""
from .baseclient import BaseClient
from .client import Client, AioClient
from .exceptions import *
from .presence import Presence, AioPresence
__title__ = 'pypresence'
__author__ = 'qwertyquerty'
__copyright__ = 'Copyright 2018 qwertyquerty'
__license__ = 'MIT'
__version__ = '4.2.1'

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,123 @@
import asyncio
import inspect
import json
import os
import struct
import sys
import tempfile
from typing import Union, Optional
# TODO: Get rid of this import * lol
from .exceptions import *
from .payloads import Payload
from .utils import get_ipc_path, get_event_loop
class BaseClient:
def __init__(self, client_id: str, **kwargs):
pipe = kwargs.get('pipe', None)
loop = kwargs.get('loop', None)
handler = kwargs.get('handler', None)
self.isasync = kwargs.get('isasync', False)
client_id = str(client_id)
self.ipc_path = get_ipc_path(pipe)
if not self.ipc_path:
raise DiscordNotFound
if loop is not None:
self.update_event_loop(loop)
else:
self.update_event_loop(get_event_loop())
self.sock_reader: Optional[asyncio.StreamReader] = None
self.sock_writer: Optional[asyncio.StreamWriter] = None
self.client_id = client_id
if handler is not None:
if not inspect.isfunction(handler):
raise PyPresenceException('Error handler must be a function.')
args = inspect.getfullargspec(handler).args
if args[0] == 'self':
args = args[1:]
if len(args) != 2:
raise PyPresenceException('Error handler should only accept two arguments.')
if self.isasync:
if not inspect.iscoroutinefunction(handler):
raise InvalidArgument('Coroutine', 'Subroutine', 'You are running async mode - '
'your error handler should be awaitable.')
err_handler = self._async_err_handle
else:
err_handler = self._err_handle
loop.set_exception_handler(err_handler)
self.handler = handler
if getattr(self, "on_event", None): # Tasty bad code ;^)
self._events_on = True
else:
self._events_on = False
def update_event_loop(self, loop):
# noinspection PyAttributeOutsideInit
self.loop = loop
asyncio.set_event_loop(self.loop)
def _err_handle(self, loop, context: dict):
result = self.handler(context['exception'], context['future'])
if inspect.iscoroutinefunction(self.handler):
loop.run_until_complete(result)
# noinspection PyUnusedLocal
async def _async_err_handle(self, loop, context: dict):
await self.handler(context['exception'], context['future'])
async def read_output(self):
try:
preamble = await self.sock_reader.read(8)
status_code, length = struct.unpack('<II', preamble[:8])
data = await self.sock_reader.read(length)
except BrokenPipeError:
raise InvalidID
payload = json.loads(data.decode('utf-8'))
if payload["evt"] == "ERROR":
raise ServerError(payload["data"]["message"])
return payload
def send_data(self, op: int, payload: Union[dict, Payload]):
if isinstance(payload, Payload):
payload = payload.data
payload = json.dumps(payload)
assert self.sock_writer is not None, "You must connect your client before sending events!"
self.sock_writer.write(
struct.pack(
'<II',
op,
len(payload)) +
payload.encode('utf-8'))
async def handshake(self):
if sys.platform == 'linux' or sys.platform == 'darwin':
self.sock_reader, self.sock_writer = await asyncio.open_unix_connection(self.ipc_path)
elif sys.platform == 'win32' or sys.platform == 'win64':
self.sock_reader = asyncio.StreamReader(loop=self.loop)
reader_protocol = asyncio.StreamReaderProtocol(
self.sock_reader, loop=self.loop)
try:
self.sock_writer, _ = await self.loop.create_pipe_connection(lambda: reader_protocol, self.ipc_path)
except FileNotFoundError:
raise InvalidPipe
self.send_data(0, {'v': 1, 'client_id': self.client_id})
preamble = await self.sock_reader.read(8)
code, length = struct.unpack('<ii', preamble)
data = json.loads(await self.sock_reader.read(length))
if 'code' in data:
raise DiscordError(data['code'], data['message'])
if self._events_on:
self.sock_reader.feed_data = self.on_event

View File

@ -0,0 +1,380 @@
import inspect
import struct
import json
import os
from typing import List
from .baseclient import BaseClient
from .exceptions import *
from .payloads import Payload
class Client(BaseClient):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._closed = False
self._events = {}
def register_event(self, event: str, func: callable, args=None):
if args is None:
args = {}
if inspect.iscoroutinefunction(func):
raise NotImplementedError
elif len(inspect.signature(func).parameters) != 1:
raise ArgumentError
self.subscribe(event, args)
self._events[event.lower()] = func
def unregister_event(self, event: str, args=None):
if args is None:
args = {}
event = event.lower()
if event not in self._events:
raise EventNotFound
self.unsubscribe(event, args)
del self._events[event]
# noinspection PyProtectedMember
def on_event(self, data):
if self.sock_reader._eof:
raise PyPresenceException('feed_data after feed_eof')
if not data:
return
self.sock_reader._buffer.extend(data)
self.sock_reader._wakeup_waiter()
if (self.sock_reader._transport is not None and
not self.sock_reader._paused and
len(self.sock_reader._buffer) > 2 * self.sock_reader._limit):
try:
self.sock_reader._transport.pause_reading()
except NotImplementedError:
self.sock_reader._transport = None
else:
self.sock_reader._paused = True
end = 0
while end < len(data):
# While chunks are available in data
start = end + 8
status_code, length = struct.unpack('<II', data[end:start])
end = length + start
payload = json.loads(data[start:end].decode('utf-8'))
if payload["evt"] is not None:
evt = payload["evt"].lower()
if evt in self._events:
self._events[evt](payload["data"])
elif evt == 'error':
raise DiscordError(payload["data"]["code"], payload["data"]["message"])
def authorize(self, client_id: str, scopes: List[str]):
payload = Payload.authorize(client_id, scopes)
self.send_data(1, payload)
return self.loop.run_until_complete(self.read_output())
def authenticate(self, token: str):
payload = Payload.authenticate(token)
self.send_data(1, payload)
return self.loop.run_until_complete(self.read_output())
def get_guilds(self):
payload = Payload.get_guilds()
self.send_data(1, payload)
return self.loop.run_until_complete(self.read_output())
def get_guild(self, guild_id: str):
payload = Payload.get_guild(guild_id)
self.send_data(1, payload)
return self.loop.run_until_complete(self.read_output())
def get_channel(self, channel_id: str):
payload = Payload.get_channel(channel_id)
self.send_data(1, payload)
return self.loop.run_until_complete(self.read_output())
def get_channels(self, guild_id: str):
payload = Payload.get_channels(guild_id)
self.send_data(1, payload)
return self.loop.run_until_complete(self.read_output())
def set_user_voice_settings(self, user_id: str, pan_left: float = None,
pan_right: float = None, volume: int = None,
mute: bool = None):
payload = Payload.set_user_voice_settings(user_id, pan_left, pan_right, volume, mute)
self.send_data(1, payload)
return self.loop.run_until_complete(self.read_output())
def select_voice_channel(self, channel_id: str):
payload = Payload.select_voice_channel(channel_id)
self.send_data(1, payload)
return self.loop.run_until_complete(self.read_output())
def get_selected_voice_channel(self):
payload = Payload.get_selected_voice_channel()
self.send_data(1, payload)
return self.loop.run_until_complete(self.read_output())
def select_text_channel(self, channel_id: str):
payload = Payload.select_text_channel(channel_id)
self.send_data(1, payload)
return self.loop.run_until_complete(self.read_output())
def set_activity(self, pid: int = os.getpid(),
state: str = None, details: str = None,
start: int = None, end: int = None,
large_image: str = None, large_text: str = None,
small_image: str = None, small_text: str = None,
party_id: str = None, party_size: list = None,
join: str = None, spectate: str = None,
match: str = None, buttons: list = None,
instance: bool = True):
payload = Payload.set_activity(pid=pid, state=state, details=details, start=start, end=end,
large_image=large_image, large_text=large_text, small_image=small_image,
small_text=small_text, party_id=party_id, party_size=party_size, join=join,
spectate=spectate, match=match, buttons=buttons, instance=instance,
activity=True)
self.send_data(1, payload)
return self.loop.run_until_complete(self.read_output())
def clear_activity(self, pid: int = os.getpid()):
payload = Payload.set_activity(pid, activity=None)
self.send_data(1, payload)
return self.loop.run_until_complete(self.read_output())
def subscribe(self, event: str, args=None):
if args is None:
args = {}
payload = Payload.subscribe(event, args)
self.send_data(1, payload)
return self.loop.run_until_complete(self.read_output())
def unsubscribe(self, event: str, args=None):
if args is None:
args = {}
payload = Payload.unsubscribe(event, args)
self.send_data(1, payload)
return self.loop.run_until_complete(self.read_output())
def get_voice_settings(self):
payload = Payload.get_voice_settings()
self.send_data(1, payload)
return self.loop.run_until_complete(self.read_output())
def set_voice_settings(self, _input: dict = None, output: dict = None,
mode: dict = None, automatic_gain_control: bool = None,
echo_cancellation: bool = None, noise_suppression: bool = None,
qos: bool = None, silence_warning: bool = None,
deaf: bool = None, mute: bool = None):
payload = Payload.set_voice_settings(_input, output, mode, automatic_gain_control, echo_cancellation,
noise_suppression, qos, silence_warning, deaf, mute)
self.send_data(1, payload)
return self.loop.run_until_complete(self.read_output())
def capture_shortcut(self, action: str):
payload = Payload.capture_shortcut(action)
self.send_data(1, payload)
return self.loop.run_until_complete(self.read_output())
def send_activity_join_invite(self, user_id: str):
payload = Payload.send_activity_join_invite(user_id)
self.send_data(1, payload)
return self.loop.run_until_complete(self.read_output())
def close_activity_request(self, user_id: str):
payload = Payload.close_activity_request(user_id)
self.send_data(1, payload)
return self.loop.run_until_complete(self.read_output())
def close(self):
self.send_data(2, {'v': 1, 'client_id': self.client_id})
self.sock_writer.close()
self._closed = True
self.loop.close()
def start(self):
self.loop.run_until_complete(self.handshake())
def read(self):
return self.loop.run_until_complete(self.read_output())
class AioClient(BaseClient):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs, isasync=True)
self._closed = False
self._events = {}
async def register_event(self, event: str, func: callable, args=None):
if args is None:
args = {}
if not inspect.iscoroutinefunction(func):
raise InvalidArgument('Coroutine', 'Subroutine', 'Event function must be a coroutine')
elif len(inspect.signature(func).parameters) != 1:
raise ArgumentError
await self.subscribe(event, args)
self._events[event.lower()] = func
async def unregister_event(self, event: str, args=None):
if args is None:
args = {}
event = event.lower()
if event not in self._events:
raise EventNotFound
await self.unsubscribe(event, args)
del self._events[event]
# noinspection PyProtectedMember
async def on_event(self, data):
if self.sock_reader._eof:
raise PyPresenceException('feed_data after feed_eof')
if not data:
return
self.sock_reader._buffer.extend(data)
self.sock_reader._wakeup_waiter()
if (self.sock_reader._transport is not None and
not self.sock_reader._paused and
len(self.sock_reader._buffer) > 2 * self.sock_reader._limit):
try:
self.sock_reader._transport.pause_reading()
except NotImplementedError:
self.sock_reader._transport = None
else:
self.sock_reader._paused = True
payload = json.loads(data[8:].decode('utf-8'))
if payload["evt"] is not None:
evt = payload["evt"].lower()
if evt in self._events:
await self._events[evt](payload["data"])
elif evt == 'error':
raise DiscordError(payload["data"]["code"], payload["data"]["message"])
async def authorize(self, client_id: str, scopes: List[str]):
payload = Payload.authorize(client_id, scopes)
self.send_data(1, payload)
return await self.read_output()
async def authenticate(self, token: str):
payload = Payload.authenticate(token)
self.send_data(1, payload)
return await self.read_output()
async def get_guilds(self):
payload = Payload.get_guilds()
self.send_data(1, payload)
return await self.read_output()
async def get_guild(self, guild_id: str):
payload = Payload.get_guild(guild_id)
self.send_data(1, payload)
return await self.read_output()
async def get_channel(self, channel_id: str):
payload = Payload.get_channel(channel_id)
self.send_data(1, payload)
return await self.read_output()
async def get_channels(self, guild_id: str):
payload = Payload.get_channels(guild_id)
self.send_data(1, payload)
return await self.read_output()
async def set_user_voice_settings(self, user_id: str, pan_left: float = None,
pan_right: float = None, volume: int = None,
mute: bool = None):
payload = Payload.set_user_voice_settings(user_id, pan_left, pan_right, volume, mute)
self.send_data(1, payload)
return await self.read_output()
async def select_voice_channel(self, channel_id: str):
payload = Payload.select_voice_channel(channel_id)
self.send_data(1, payload)
return await self.read_output()
async def get_selected_voice_channel(self):
payload = Payload.get_selected_voice_channel()
self.send_data(1, payload)
return await self.read_output()
async def select_text_channel(self, channel_id: str):
payload = Payload.select_text_channel(channel_id)
self.send_data(1, payload)
return await self.read_output()
async def set_activity(self, pid: int = os.getpid(),
state: str = None, details: str = None,
start: int = None, end: int = None,
large_image: str = None, large_text: str = None,
small_image: str = None, small_text: str = None,
party_id: str = None, party_size: list = None,
join: str = None, spectate: str = None,
buttons: list = None,
match: str = None, instance: bool = True):
payload = Payload.set_activity(pid, state, details, start, end, large_image, large_text,
small_image, small_text, party_id, party_size, join, spectate,
match, buttons, instance, activity=True)
self.send_data(1, payload)
return await self.read_output()
async def clear_activity(self, pid: int = os.getpid()):
payload = Payload.set_activity(pid, activity=None)
self.send_data(1, payload)
return await self.read_output()
async def subscribe(self, event: str, args=None):
if args is None:
args = {}
payload = Payload.subscribe(event, args)
self.send_data(1, payload)
return await self.read_output()
async def unsubscribe(self, event: str, args=None):
if args is None:
args = {}
payload = Payload.unsubscribe(event, args)
self.send_data(1, payload)
return await self.read_output()
async def get_voice_settings(self):
payload = Payload.get_voice_settings()
self.send_data(1, payload)
return await self.read_output()
async def set_voice_settings(self, _input: dict = None, output: dict = None,
mode: dict = None, automatic_gain_control: bool = None,
echo_cancellation: bool = None, noise_suppression: bool = None,
qos: bool = None, silence_warning: bool = None,
deaf: bool = None, mute: bool = None):
payload = Payload.set_voice_settings(_input, output, mode, automatic_gain_control, echo_cancellation,
noise_suppression, qos, silence_warning, deaf, mute)
self.send_data(1, payload)
return await self.read_output()
async def capture_shortcut(self, action: str):
payload = Payload.capture_shortcut(action)
self.send_data(1, payload)
return await self.read_output()
async def send_activity_join_invite(self, user_id: str):
payload = Payload.send_activity_join_invite(user_id)
self.send_data(1, payload)
return await self.read_output()
async def close_activity_request(self, user_id: str):
payload = Payload.close_activity_request(user_id)
self.send_data(1, payload)
return await self.read_output()
def close(self):
self.send_data(2, {'v': 1, 'client_id': self.client_id})
self.sock_writer.close()
self._closed = True
self.loop.close()
async def start(self):
await self.handshake()
async def read(self):
return await self.read_output()

View File

@ -0,0 +1,50 @@
class PyPresenceException(Exception):
def __init__(self, message: str = None):
if message is None:
message = 'An error has occurred within PyPresence'
super().__init__(message)
class DiscordNotFound(PyPresenceException):
def __init__(self):
super().__init__('Could not find Discord installed and running on this machine.')
class InvalidID(PyPresenceException):
def __init__(self):
super().__init__('Client ID is Invalid')
class InvalidPipe(PyPresenceException):
def __init__(self):
super().__init__('Pipe Not Found - Is Discord Running?')
class InvalidArgument(PyPresenceException):
def __init__(self, expected, received, description: str = None):
description = '\n{0}'.format(description) if description else ''
super().__init__('Bad argument passed. Expected {0} but got {1} instead{2}'.format(expected, received,
description)
)
class ServerError(PyPresenceException):
def __init__(self, message: str):
super().__init__(message.replace(']', '').replace('[', '').capitalize())
class DiscordError(PyPresenceException):
def __init__(self, code: int, message: str):
self.code = code
self.message = message
super().__init__('Error Code: {0} Message: {1}'.format(code, message))
class ArgumentError(PyPresenceException):
def __init__(self):
super().__init__('Supplied function must have one argument.')
class EventNotFound(PyPresenceException):
def __init__(self, event):
super().__init__('No event with name {0} exists.'.format(event))

View File

@ -0,0 +1,307 @@
import json
import os
import time
from typing import List, Union
from .utils import remove_none
class Payload:
def __init__(self, data, clear_none=True):
if clear_none:
data = remove_none(data)
self.data = data
def __str__(self):
return json.dumps(self.data, indent=2)
@staticmethod
def time():
return time.time()
@classmethod
def set_activity(cls, pid: int = os.getpid(),
state: str = None, details: str = None,
start: int = None, end: int = None,
large_image: str = None, large_text: str = None,
small_image: str = None, small_text: str = None,
party_id: str = None, party_size: list = None,
join: str = None, spectate: str = None,
match: str = None, buttons: list = None,
instance: bool = True, activity: Union[bool, None] = True,
_rn: bool = True):
# They should already be an int because we give typehints, but some people are fucking stupid and use
# IDLE or some other stupid shit.
if start:
start = int(start)
if end:
end = int(end)
if activity is None:
act_details = None
clear = True
else:
act_details = {
"state": state,
"details": details,
"timestamps": {
"start": start,
"end": end
},
"assets": {
"large_image": large_image,
"large_text": large_text,
"small_image": small_image,
"small_text": small_text
},
"party": {
"id": party_id,
"size": party_size
},
"secrets": {
"join": join,
"spectate": spectate,
"match": match
},
"buttons": buttons,
"instance": instance
}
clear = False
payload = {
"cmd": "SET_ACTIVITY",
"args": {
"pid": pid,
"activity": act_details
},
"nonce": '{:.20f}'.format(cls.time())
}
if _rn:
clear = _rn
return cls(payload, clear)
@classmethod
def authorize(cls, client_id: str, scopes: List[str]):
payload = {
"cmd": "AUTHORIZE",
"args": {
"client_id": str(client_id),
"scopes": scopes
},
"nonce": '{:.20f}'.format(cls.time())
}
return cls(payload)
@classmethod
def authenticate(cls, token: str):
payload = {
"cmd": "AUTHENTICATE",
"args": {
"access_token": token
},
"nonce": '{:.20f}'.format(cls.time())
}
return cls(payload)
@classmethod
def get_guilds(cls):
payload = {
"cmd": "GET_GUILDS",
"args": {
},
"nonce": '{:.20f}'.format(cls.time())
}
return cls(payload)
@classmethod
def get_guild(cls, guild_id: str):
payload = {
"cmd": "GET_GUILD",
"args": {
"guild_id": str(guild_id),
},
"nonce": '{:.20f}'.format(cls.time())
}
return cls(payload)
@classmethod
def get_channels(cls, guild_id: str):
payload = {
"cmd": "GET_CHANNELS",
"args": {
"guild_id": str(guild_id),
},
"nonce": '{:.20f}'.format(cls.time())
}
return cls(payload)
@classmethod
def get_channel(cls, channel_id: str):
payload = {
"cmd": "GET_CHANNEL",
"args": {
"channel_id": str(channel_id),
},
"nonce": '{:.20f}'.format(cls.time())
}
return cls(payload)
@classmethod
def set_user_voice_settings(cls, user_id: str, pan_left: float = None,
pan_right: float = None, volume: int = None,
mute: bool = None):
payload = {
"cmd": "SET_USER_VOICE_SETTINGS",
"args": {
"user_id": str(user_id),
"pan": {
"left": pan_left,
"right": pan_right
},
"volume": volume,
"mute": mute
},
"nonce": '{:.20f}'.format(cls.time())
}
return cls(payload, True)
@classmethod
def select_voice_channel(cls, channel_id: str):
payload = {
"cmd": "SELECT_VOICE_CHANNEL",
"args": {
"channel_id": str(channel_id),
},
"nonce": '{:.20f}'.format(cls.time())
}
return cls(payload)
@classmethod
def get_selected_voice_channel(cls):
payload = {
"cmd": "GET_SELECTED_VOICE_CHANNEL",
"args": {
},
"nonce": '{:.20f}'.format(cls.time())
}
return cls(payload)
@classmethod
def select_text_channel(cls, channel_id: str):
payload = {
"cmd": "SELECT_TEXT_CHANNEL",
"args": {
"channel_id": str(channel_id),
},
"nonce": '{:.20f}'.format(cls.time())
}
return cls(payload)
@classmethod
def subscribe(cls, event: str, args=None):
if args is None:
args = {}
payload = {
"cmd": "SUBSCRIBE",
"args": args,
"evt": event.upper(),
"nonce": '{:.20f}'.format(cls.time())
}
return cls(payload)
@classmethod
def unsubscribe(cls, event: str, args=None):
if args is None:
args = {}
payload = {
"cmd": "UNSUBSCRIBE",
"args": args,
"evt": event.upper(),
"nonce": '{:.20f}'.format(cls.time())
}
return cls(payload)
@classmethod
def get_voice_settings(cls):
payload = {
"cmd": "GET_VOICE_SETTINGS",
"args": {
},
"nonce": '{:.20f}'.format(cls.time())
}
return cls(payload)
@classmethod
def set_voice_settings(cls, _input: dict = None, output: dict = None,
mode: dict = None, automatic_gain_control: bool = None,
echo_cancellation: bool = None, noise_suppression: bool = None,
qos: bool = None, silence_warning: bool = None,
deaf: bool = None, mute: bool = None):
payload = {
"cmd": "SET_VOICE_SETTINGS",
"args": {
"input": _input,
"output": output,
"mode": mode,
"automatic_gain_control": automatic_gain_control,
"echo_cancellation": echo_cancellation,
"noise_suppression": noise_suppression,
"qos": qos,
"silence_warning": silence_warning,
"deaf": deaf,
"mute": mute
},
"nonce": '{:.20f}'.format(cls.time())
}
return cls(payload, True)
@classmethod
def capture_shortcut(cls, action: str):
payload = {
"cmd": "CAPTURE_SHORTCUT",
"args": {
"action": action.upper()
},
"nonce": '{:.20f}'.format(cls.time())
}
return cls(payload)
@classmethod
def send_activity_join_invite(cls, user_id: str):
payload = {
"cmd": "SEND_ACTIVITY_JOIN_INVITE",
"args": {
"user_id": str(user_id)
},
"nonce": '{:.20f}'.format(cls.time())
}
return cls(payload)
@classmethod
def close_activity_request(cls, user_id: str):
payload = {
"cmd": "CLOSE_ACTIVITY_REQUEST",
"args": {
"user_id": str(user_id)
},
"nonce": '{:.20f}'.format(cls.time())
}
return cls(payload)

View File

@ -0,0 +1,87 @@
import json
import os
import time
from .baseclient import BaseClient
from .payloads import Payload
from .utils import remove_none, get_event_loop
class Presence(BaseClient):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def update(self, pid: int = os.getpid(),
state: str = None, details: str = None,
start: int = None, end: int = None,
large_image: str = None, large_text: str = None,
small_image: str = None, small_text: str = None,
party_id: str = None, party_size: list = None,
join: str = None, spectate: str = None,
match: str = None, buttons: list = None,
instance: bool = True,
_donotuse=True):
if _donotuse is True:
payload = Payload.set_activity(pid=pid, state=state, details=details, start=start, end=end,
large_image=large_image, large_text=large_text,
small_image=small_image, small_text=small_text, party_id=party_id,
party_size=party_size, join=join, spectate=spectate,
match=match, buttons=buttons, instance=instance, activity=True)
else:
payload = _donotuse
self.send_data(1, payload)
return self.loop.run_until_complete(self.read_output())
def clear(self, pid: int = os.getpid()):
payload = Payload.set_activity(pid, activity=None)
self.send_data(1, payload)
return self.loop.run_until_complete(self.read_output())
def connect(self):
self.update_event_loop(get_event_loop())
self.loop.run_until_complete(self.handshake())
def close(self):
self.send_data(2, {'v': 1, 'client_id': self.client_id})
self.sock_writer.close()
self.loop.close()
class AioPresence(BaseClient):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs, isasync=True)
async def update(self, pid: int = os.getpid(),
state: str = None, details: str = None,
start: int = None, end: int = None,
large_image: str = None, large_text: str = None,
small_image: str = None, small_text: str = None,
party_id: str = None, party_size: list = None,
join: str = None, spectate: str = None,
match: str = None, buttons: list = None,
instance: bool = True):
payload = Payload.set_activity(pid=pid, state=state, details=details, start=start, end=end,
large_image=large_image, large_text=large_text,
small_image=small_image, small_text=small_text, party_id=party_id,
party_size=party_size, join=join, spectate=spectate,
match=match, buttons=buttons, instance=instance, activity=True)
self.send_data(1, payload)
return await self.read_output()
async def clear(self, pid: int = os.getpid()):
payload = Payload.set_activity(pid, activity=None)
self.send_data(1, payload)
return await self.read_output()
async def connect(self):
self.update_event_loop(get_event_loop())
await self.handshake()
def close(self):
self.send_data(2, {'v': 1, 'client_id': self.client_id})
self.sock_writer.close()
self.loop.close()

View File

@ -0,0 +1,70 @@
"""Util functions that are needed but messy."""
import asyncio
import json
import os
import sys
import tempfile
import time
from .exceptions import PyPresenceException
# Made by https://github.com/LewdNeko ;^)
def remove_none(d: dict):
for item in d.copy():
if isinstance(d[item], dict):
if len(d[item]):
d[item] = remove_none(d[item])
if not len(d[item]):
del d[item]
elif d[item] is None:
del d[item]
return d
# Returns on first IPC pipe matching Discord's
def get_ipc_path(pipe=None):
ipc = 'discord-ipc-'
if pipe:
ipc = f"{ipc}{pipe}"
if sys.platform in ('linux', 'darwin'):
tempdir = (os.environ.get('XDG_RUNTIME_DIR') or tempfile.gettempdir())
paths = ['.', 'snap.discord', 'app/com.discordapp.Discord']
elif sys.platform == 'win32':
tempdir = r'\\?\pipe'
paths = ['.']
else:
return
for path in paths:
full_path = os.path.abspath(os.path.join(tempdir, path))
if sys.platform == 'win32' or os.path.isdir(full_path):
for entry in os.scandir(full_path):
if entry.name.startswith(ipc):
return entry.path
def get_event_loop(force_fresh=False):
if sys.platform in ('linux', 'darwin'):
if force_fresh:
return asyncio.new_event_loop()
loop = asyncio.get_event_loop()
if loop.is_closed():
return asyncio.new_event_loop()
return loop
elif sys.platform == 'win32':
if force_fresh:
return asyncio.ProactorEventLoop()
loop = asyncio.get_event_loop()
if isinstance(loop, asyncio.ProactorEventLoop) and not loop.is_closed():
return loop
return asyncio.ProactorEventLoop()
# This code used to do something. I don't know what, though.
try: # Thanks, Rapptz :^)
create_task = asyncio.ensure_future
except AttributeError:
create_task = getattr(asyncio, "async")
# No longer crashes Python 3.7