diff --git a/DOCS.md b/DOCS.md index de6a819..c36d056 100644 --- a/DOCS.md +++ b/DOCS.md @@ -82,7 +82,9 @@ Examples can be seen in the repository (`Discord-RPC/examples`) or [here](https: Parameters : - state (`str`) - details (`str`) - - act_type (`discordrpc.Activity`) : [Activity Types](https://discord.com/developers/docs/topics/gateway-events#activity-object-activity-types) (Activity Type `1` and `4` is currently disabled, see [#28](https://github.com/Senophyx/Discord-RPC/issues/28#issuecomment-2301287350)). + - act_type (`discordrpc.Activity`) : [Activity Types](#class-discordrpcactivity) (Activity Type `1` and `4` is currently disabled, see [#28](https://github.com/Senophyx/Discord-RPC/issues/28#issuecomment-2301287350)). + - state_url (`str`) : URL that is linked when clicking on the state text. + - details_url (`str`) : URL that is linked when clicking on the details text. - ts_start (`int`) : Timestamp start. - ts_end (`int`) : Timestamp end. - large_image (`str`) : The name of the image that has been uploaded to the Discord Developer Portal. @@ -94,10 +96,15 @@ Examples can be seen in the repository (`Discord-RPC/examples`) or [here](https: - join_secret (`str`) : Secret for chat invitations and ask to join button. - spectate_secret (`str`) : Secret for spectate button. - match_secret (`str`) : Secret for for spectate and join button - - buttons (`list`) : list of dicts for buttons on user's profile. You can use `discordrpc.Button` for more easier. + - buttons (`list`) : list of dicts for buttons on user's profile. You can use [`discordrpc.Button`](#function-discordrpcbutton) for more easier. Return : `True` if rpc successfully connected. +- method `RPC.clear()`
+ Clear activity status. + + Return : nothing. + - method `RPC.disconnect()`
Disconnecting and closing RPC socket. @@ -124,10 +131,15 @@ Examples can be seen in the repository (`Discord-RPC/examples`) or [here](https: Return : `True` or `False` +- variable `self.User`
+ Returns information about the user to whom the connection occurred.
+ [Available attributes](#class-discordrpcuser) + ## class `discordrpc.Activity` - Enum `Activity`
- Simplified Activity type payload in `RPC.set_activity` + Simplified Activity type payload in `RPC.set_activity`
+ [Discord docs](https://discord.com/developers/docs/topics/gateway-events#activity-object-activity-types) Available values : - Playing @@ -142,13 +154,12 @@ Examples can be seen in the repository (`Discord-RPC/examples`) or [here](https: > [Details](https://github.com/Senophyx/Discord-RPC/issues/28#issuecomment-2301287350) -## class `discordrpc.Button()` -- function `Button()`
- Simplified button payload in `RPC.set_activity` +## function `discordrpc.Button()` +- Simplified button payload in `RPC.set_activity` Parameters : - - text (`test`) - - text (`url`) + - text (`str`) + - url (`str`) Return : Payload dict. @@ -156,6 +167,27 @@ Examples can be seen in the repository (`Discord-RPC/examples`) or [here](https: > Discord does not display buttons in your own Activity.
> You won’t see them yourself — but other users will see them correctly. + +## function `discordrpc.Progressbar()` +- Simplified `ts_start` and `ts_end` payload in `RPC.set_activity` + + Parameters : + - current (`int`) + - duration (`int`) + + Return : Payload dict. + + +## class `discordrpc.User()` + Attributes : + - id (`int`) + - username (`str`) + - name (`str`) + - avatar (URL `str`) + - bot (`bool`) + - premium_type (`int`) ([details](https://discord.com/developers/docs/resources/user#user-object-premium-types)) + + ## class `discordrpc.utils` - variable `discordrpc.utils.timestamp()`
Return current time in epoch timestamp (`int`). @@ -169,6 +201,9 @@ Examples can be seen in the repository (`Discord-RPC/examples`) or [here](https: date_to_timestamp('14/06/2025-00:00:00') ``` +- function `discordrpc.utils.use_local_time()`
+ Simplified `ts_start` payload in `RPC.set_activity` + ## Exceptions & Errors - `RPCException`
@@ -204,6 +239,11 @@ Examples can be seen in the repository (`Discord-RPC/examples`) or [here](https: How-to-Fix : Check if `Button` function are set correctly +- `ProgressbarError`
+ There is an error in the `Progressbar` function, usually because the first parameter (current) is more then second parameter (duration). + + How-to-Fix : Make sure that duration > current + ## Links - [Github Repository](https://github.com/Senophyx/Discord-RPC) diff --git a/discordrpc/__init__.py b/discordrpc/__init__.py index e8598b3..5782154 100644 --- a/discordrpc/__init__.py +++ b/discordrpc/__init__.py @@ -1,8 +1,9 @@ from .presence import RPC from .button import Button +from .progressbar import Progressbar from .exceptions import * from .types import * -from .utils import timestamp, date_to_timestamp +from .utils import timestamp, date_to_timestamp, use_local_time __title__ = "Discord RPC" __version__ = "5.1" diff --git a/discordrpc/button.py b/discordrpc/button.py index 154220b..de78c25 100644 --- a/discordrpc/button.py +++ b/discordrpc/button.py @@ -3,5 +3,5 @@ def Button(text:str, url:str): if not url.startswith(("http://", "https://")): - raise InvalidURL + raise InvalidURL() return {"label": text, "url": url} diff --git a/discordrpc/exceptions.py b/discordrpc/exceptions.py index 9c44205..22ae980 100644 --- a/discordrpc/exceptions.py +++ b/discordrpc/exceptions.py @@ -28,6 +28,10 @@ class ButtonError(RPCException): def __init__(self, message: str = None): super().__init__(message=message) +class ProgressbarError(RPCException): + def __init__(self, message): + super().__init__(message=message) + class InvalidActivityType(RPCException): def __init__(self, message): super().__init__(f"Activity type must be , not {message}") @@ -35,4 +39,4 @@ def __init__(self, message): # https://github.com/Senophyx/Discord-RPC/issues/28#issuecomment-2301287350 class ActivityTypeDisabled(RPCException): def __init__(self): - super().__init__(f"Activity type `Streaming` and `Custom` currently disabled. See https://github.com/Senophyx/Discord-RPC/issues/28#issuecomment-2301287350") \ No newline at end of file + super().__init__(f"Activity type `Streaming` and `Custom` currently disabled. See https://github.com/Senophyx/Discord-RPC/issues/28#issuecomment-2301287350") diff --git a/discordrpc/presence.py b/discordrpc/presence.py index c8ebfee..80744ad 100644 --- a/discordrpc/presence.py +++ b/discordrpc/presence.py @@ -7,7 +7,7 @@ import re from .exceptions import * from .types import * -from .utils import remove_none +from .utils import * import logging import time @@ -28,7 +28,9 @@ def __init__(self, app_id:int, debug:bool=False, output:bool=True, exit_if_disco self.app_id = str(app_id) self.exit_if_discord_close = exit_if_discord_close self.exit_on_disconnect = exit_on_disconnect - self.User={} + + self.user_data = {} + self.User = User() if debug == True: log.setLevel(logging.DEBUG) @@ -42,27 +44,26 @@ def __init__(self, app_id:int, debug:bool=False, output:bool=True, exit_if_disco def _setup(self): if sys.platform == "win32": self.ipc = WindowsPipe(self.app_id, self.exit_if_discord_close, self.exit_on_disconnect) - if not self.ipc.connected: - return - - self.User=self.ipc.handshake() - else: self.ipc = UnixPipe(self.app_id, self.exit_if_discord_close, self.exit_on_disconnect) - if not self.ipc.connected: - return - - self.User=self.ipc.handshake() + + if not self.ipc.connected: return + self.user_data = self.ipc.handshake() + self.User = User(self.user_data) def set_activity( self, state: str=None, details:str=None, act_type:Activity=Activity.Playing, + state_url:str=None, details_url:str=None, ts_start:int=None, ts_end:int=None, + # progressbar:dict=None, + # use_local_time:bool=False, 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_secret:str=None, spectate_secret:str=None, - match_secret:str=None, buttons:list=None + match_secret:str=None, buttons:list=None, + clear=False ) -> bool: if type(party_id) == int: @@ -82,6 +83,8 @@ def set_activity( "state": state, "details": details, "type": act_type.value, + "state_url": state_url, + "details_url": details_url, "timestamps": { "start": ts_start, "end": ts_end @@ -108,7 +111,7 @@ def set_activity( 'cmd': 'SET_ACTIVITY', 'args': { 'pid': os.getpid(), - 'activity': remove_none(act) + 'activity': None if clear else remove_none(act) }, 'nonce': str(uuid.uuid4()) } @@ -128,6 +131,9 @@ def set_activity( log.error('Failed to set RPC') self.disconnect() + def clear(self): + self.set_activity(clear=True) + def disconnect(self): if not self.ipc.connected: return @@ -219,7 +225,7 @@ def handshake(self): except KeyError: if data['code'] == 4000: - raise InvalidID + raise InvalidID() def disconnect(self): try: @@ -300,7 +306,7 @@ def handshake(self): except KeyError: if data['code'] == 4000: - raise InvalidID + raise InvalidID() def disconnect(self): try: diff --git a/discordrpc/progressbar.py b/discordrpc/progressbar.py new file mode 100644 index 0000000..67e1379 --- /dev/null +++ b/discordrpc/progressbar.py @@ -0,0 +1,14 @@ +import time +from .exceptions import * + + +def Progressbar(current:int, duration:int) -> dict: + if int(current) > int(duration): + raise ProgressbarError("Current cannot exceed Duration") + + current_time = int(time.time()) - int(current) + finish_time = current_time + int(duration) + + return { + "ts_start": current_time, "ts_end": finish_time + } diff --git a/discordrpc/types.py b/discordrpc/types.py index 31251c0..540462e 100644 --- a/discordrpc/types.py +++ b/discordrpc/types.py @@ -2,7 +2,6 @@ # https://discord.com/developers/docs/events/gateway-events#activity-object-activity-types - class Activity(Enum): Playing = 0 Streaming = 1 @@ -10,3 +9,24 @@ class Activity(Enum): Watching = 3 Custom = 4 Competing = 5 + + +class User(): + def __init__(self, data:dict=None): + data = data or {} + self.id: int = int(data.get('id', 0)) + self.username: str = data.get('username') + self.name: str = data.get('global_name') + self.avatar: str = self._parse_avatar(data) + self.bot: bool = data.get('bot', False) + self.premium_type: int = int(data.get('premium_type', 0)) # https://discord.com/developers/docs/resources/user#user-object-premium-types + + def _parse_avatar(self, data:dict, size:int=1024) -> str: + if data.get('avatar'): + ext = "gif" if data.get('avatar').startswith("a_") else "png" + return f"https://cdn.discordapp.com/avatars/{self.id}/{data.get('avatar')}.{ext}?size={size}" + else: + return f"https://cdn.discordapp.com/embed/avatars/0.png" + + def __str__(self): + return f"User({self.name})" diff --git a/discordrpc/utils.py b/discordrpc/utils.py index 1a702da..7667048 100644 --- a/discordrpc/utils.py +++ b/discordrpc/utils.py @@ -1,5 +1,5 @@ import time -import datetime +from datetime import datetime # Credits to qwertyquerty # https://github.com/qwertyquerty/pypresence/blob/master/pypresence/utils.py#L12C1-L21C13 @@ -21,5 +21,12 @@ def remove_none(d: dict): def date_to_timestamp(date:str): return int(time.mktime( - datetime.datetime.strptime(date, "%d/%m/%Y-%H:%M:%S").timetuple() - )) \ No newline at end of file + datetime.strptime(date, "%d/%m/%Y-%H:%M:%S").timetuple() + )) + +def use_local_time(): + now = datetime.now() + seconds_since_midnight = now.hour * 3600 + now.minute * 60 + now.second + return { + "ts_start": int(time.time()) - seconds_since_midnight + } diff --git a/examples/get-user.py b/examples/get-user.py new file mode 100644 index 0000000..9fd50bd --- /dev/null +++ b/examples/get-user.py @@ -0,0 +1,10 @@ +import discordrpc + +rpc = discordrpc.RPC(app_id=1397914682659963050) + +print(rpc.User.id) +print(rpc.User.name) +print(f"@{rpc.User.username}") +print(rpc.User.avatar) + +rpc.run() diff --git a/examples/rpc-local-time.py b/examples/rpc-local-time.py new file mode 100644 index 0000000..004adfd --- /dev/null +++ b/examples/rpc-local-time.py @@ -0,0 +1,13 @@ +import discordrpc +from discordrpc import use_local_time + + +rpc = discordrpc.RPC(app_id=1397914682659963050) + +rpc.set_activity( + state="Wow! It's shows my clock", + details="Local time example", + **use_local_time() +) + +rpc.run() diff --git a/examples/rpc-with-progressbar.py b/examples/rpc-with-progressbar.py new file mode 100644 index 0000000..0b2f2e4 --- /dev/null +++ b/examples/rpc-with-progressbar.py @@ -0,0 +1,14 @@ +import discordrpc +from discordrpc import Activity, Progressbar + + +rpc = discordrpc.RPC(app_id=1397914682659963050) + +rpc.set_activity( + state="With Progressbar", + details="Music", + act_type=Activity.Listening, + **Progressbar(50, 200) +) + +rpc.run()