Skip to content
Merged
37 changes: 28 additions & 9 deletions DOCS.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ Examples can be seen in the repository (`Discord-RPC/examples`) or [here](https:
Parameters :
- state (`str`)
- details (`str`)
- act_type (`int`) : [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](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)).
- 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.
Expand All @@ -91,9 +91,9 @@ 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.Button` for more easier.
- buttons (`list`) : list of dicts for buttons on user's profile. You can use `discordrpc.Button` for more easier.

Return : nothing.
Return : `True` if rpc successfully connected.

- method `RPC.disconnect()`<br>
Disconnecting and closing RPC socket.
Expand Down Expand Up @@ -122,17 +122,36 @@ Examples can be seen in the repository (`Discord-RPC/examples`) or [here](https:
Return : `True` or `False`


## class `discordrpc.Activity`
- Enum `Activity`<br>
Simplified Activity type payload in `RPC.set_activity`

Available values :
- Playing
- Streaming
- Listening
- Watching
- Custom
- Competing

> [!NOTE]
> Activity Type `Streaming` and `Custom` currently disabled.<br>
> [Details](https://github.com/Senophyx/Discord-RPC/issues/28#issuecomment-2301287350)


## class `discordrpc.Button()`
- function `Button()`<br>
Simplified button payload in `RPC.set_activity`

Parameters :
- button_one_label (`str`) : Label for button one.
- button_one_url (`str`) : Url for button one.
- button_two_label (`str`) : Label for button two.
- button_two_url (`str`) : Url for button two.
- text (`test`)
- text (`url`)

Return : Payload dict.

Return : List of button dict.
> [!NOTE]
> Discord does not display buttons in your own Activity.<br>
> You won’t see them yourself — but other users will see them correctly.

## class `discordrpc.utils`
- variable `discordrpc.utils.timestamp()`<br>
Expand Down Expand Up @@ -193,4 +212,4 @@ Examples can be seen in the repository (`Discord-RPC/examples`) or [here](https:
```
Discord-RPC project is under MIT License
Copyright (c) 2021-2024 Senophyx and EterNomm.
```
```
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
# Discord RPC
An Python wrapper for Discord RPC API. Allow you to make own custom RPC.

[![Changelog](https://img.shields.io/badge/Discord--RPC-Changelog-informational?style=for-the-badge&logo=github)](https://senophyx.id/projects/discord-rpc/#change-logs)
[![Changelog](https://img.shields.io/badge/Changelog-blue?style=for-the-badge&logo=github)](https://senophyx.id/projects/discord-rpc/#change-logs)
[![Documentation](https://img.shields.io/badge/Documentation-gray?style=for-the-badge&logo=googledocs&logoColor=white)](https://github.com/Senophyx/Discord-RPC/blob/main/DOCS.md)

## Install
- PyPI
Expand All @@ -32,7 +33,6 @@ rpc.run()
```
`rpc.run()` is only used if you are only running Discord RPC on the current file/instance. If there are other programs/tasks on the current instance, `rpc.run()` does not need to be used.

See documentation [here](https://github.com/Senophyx/Discord-RPC/blob/main/DOCS.md).<br>
More examples [here](https://github.com/Senophyx/discord-rpc/tree/main/examples).


Expand Down
1 change: 1 addition & 0 deletions discordrpc/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from .presence import RPC
from .button import Button
from .exceptions import *
from .types import *
from .utils import timestamp, date_to_timestamp

__title__ = "Discord RPC"
Expand Down
31 changes: 3 additions & 28 deletions discordrpc/button.py
Original file line number Diff line number Diff line change
@@ -1,32 +1,7 @@
from .exceptions import *

valid_url = ["https://", "http://"]

def _payload(label:str, url:str):
if any(v in url for v in valid_url):
payloads = {"label": label, "url": url}
return payloads
else:
def Button(text:str, url:str):
if not url.startswith(("http://", "https://")):
raise InvalidURL


def Button(
button_one_label:str,
button_one_url:str,
button_two_label:str,
button_two_url:str):

if button_one_label == None:
raise ButtonError('"button_one_label" cannot None')
if button_one_url == None:
raise ButtonError('"button_one_url" cannot None')
if button_two_label == None:
raise ButtonError('"button_two_label" cannot None')
if button_two_url == None:
raise ButtonError('"button_two_url" cannot None')

btn_one = _payload(label=button_one_label, url=button_one_url)
btn_two = _payload(label=button_two_label, url=button_two_url)
payloads = [btn_one, btn_two]

return payloads
return {"label": text, "url": url}
6 changes: 5 additions & 1 deletion discordrpc/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,11 @@ class ButtonError(RPCException):
def __init__(self, message: str = None):
super().__init__(message=message)

# https://github.com/Senophyx/Discord-RPC/issues/28#issuecomment-2301287350
class InvalidActivityType(RPCException):
def __init__(self, message):
super().__init__(f"Activity type must be <Activity>, not {message}")

# https://github.com/Senophyx/Discord-RPC/issues/28#issuecomment-2301287350
class ActivityTypeDisabled(RPCException):
def __init__(self):
super().__init__(f"Activity Type 1 and 4 currently disabled. See https://github.com/Senophyx/Discord-RPC/issues/28#issuecomment-2301287350")
66 changes: 44 additions & 22 deletions discordrpc/presence.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import uuid
import re
from .exceptions import *
from .types import *
from .utils import remove_none
import logging
import time
Expand Down Expand Up @@ -55,27 +56,32 @@ def _setup(self):

def set_activity(
self,
state: str=None, details:str=None, act_type:int=0,
state: str=None, details:str=None, act_type:Activity=Activity.Playing,
ts_start:int=None, ts_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_secret:str=None, spectate_secret:str=None,
match_secret:str=None, buttons:list=None
):
) -> bool:

if type(party_id) == int:
party_id = str(party_id)

if type(act_type) != Activity:
raise InvalidActivityType(type(act_type))

# https://github.com/Senophyx/Discord-RPC/issues/28#issuecomment-2301287350
invalidType = ["1", "4"]
if any(invtype in str(act_type) for invtype in invalidType):
raise InvalidActivityType()
if act_type in [Activity.Streaming, Activity.Custom]:
raise ActivityTypeDisabled()

if buttons and len(buttons) > 2:
raise ButtonError("Max 2 buttons allowed")

act = {
"state": state,
"details": details,
"type": act_type,
"type": act_type.value,
"timestamps": {
"start": ts_start,
"end": ts_end
Expand Down Expand Up @@ -113,9 +119,14 @@ def set_activity(
if not self.ipc.connected:
return

self.ipc._send(payload, OP_FRAME)
self.is_running = True
log.info('RPC set')
try:
self.ipc._send(payload, OP_FRAME)
self.is_running = True
log.info('RPC set')
return True
except Exception as e:
log.error('Failed to set RPC')
self.disconnect()

def disconnect(self):
if not self.ipc.connected:
Expand Down Expand Up @@ -146,16 +157,19 @@ def __init__(self, app_id, exit_if_discord_close, exit_on_disconnect):
try:
self.socket = open(path, "w+b")
except OSError as e:
if not self.exit_if_discord_close:
raise Error("Failed to open {!r}: {}".format(path, e))
if self.exit_if_discord_close:
log.debug("Failed to open {!r}: {}".format(path, e))
raise DiscordNotOpened()
else:
log.debug("Discord seems to be close.")
else:
break

else:
if not self.exit_if_discord_close:
if self.exit_if_discord_close:
raise DiscordNotOpened()
else:
log.debug("Discord seems to be close.")
log.warning("Discord is closed")
self.connected = False

if self.connected:
Expand Down Expand Up @@ -208,10 +222,14 @@ def handshake(self):
raise InvalidID

def disconnect(self):
self._send({}, OP_CLOSE)

self.socket.close()
try:
self._send({}, OP_CLOSE)
self.socket.close()
except Exception as e:
log.debug("Socket closed before command was received")

self.socket = None
self.connected = False

log.warning("Closing RPC")
if self.exit_on_disconnect:
Expand Down Expand Up @@ -239,10 +257,10 @@ def __init__(self, app_id, exit_if_discord_close, exit_on_disconnect):
pass

else:
if not self.exit_if_discord_close:
if self.exit_if_discord_close:
raise DiscordNotOpened()
else:
log.debug("Discord seems to be close.")
log.warning("Discord is closed")
self.connected = False

if self.connected:
Expand Down Expand Up @@ -285,12 +303,16 @@ def handshake(self):
raise InvalidID

def disconnect(self):
self._send({}, OP_CLOSE)
try:
self._send({}, OP_CLOSE)
self.socket.shutdown(socket.SHUT_RDWR)
self.socket.close()
except Exception as e:
log.debug("Socket closed before command was received")

self.socket.shutdown(socket.SHUT_RDWR)
self.socket.close()
self.socket = None
self.connected = False

log.warning("Closing RPC")
if self.exit_on_disconnect:
sys.exit()
sys.exit()
12 changes: 12 additions & 0 deletions discordrpc/types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from enum import Enum


# https://discord.com/developers/docs/events/gateway-events#activity-object-activity-types

class Activity(Enum):
Playing = 0
Streaming = 1
Listening = 2
Watching = 3
Custom = 4
Competing = 5
21 changes: 21 additions & 0 deletions examples/rpc-with-activitytype.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import discordrpc
from discordrpc import Activity
import time


rpc = discordrpc.RPC(app_id=123456789)


current_time = int(time.time())
finish_time = current_time + 200

rpc.set_activity(
state="With activity type",
details="Music",
act_type=Activity.Listening,
ts_start=current_time,
ts_end=finish_time
)


rpc.run()
14 changes: 5 additions & 9 deletions examples/rpc-with-button.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,17 @@
import discordrpc
from discordrpc.button import Button
from discordrpc import Button

rpc = discordrpc.RPC(app_id=1234567891011)

button = Button(
button_one_label="Repository",
button_one_url="https://github.com/Senophyx/discord-rpc",
button_two_label="Discord Server",
button_two_url="https://discord.gg/qpT2AeYZRN"
)

rpc.set_activity(
state="Made by Senophyx",
details="Discord-RPC",
buttons=button
buttons=[
Button("Repository", "https://github.com/Senophyx/discord-rpc"),
Button("Discord", "https://discord.gg/qpT2AeYZRN"),
]
)



rpc.run()
4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
from setuptools import setup, find_packages
import re

with open('README.md') as f:
with open('README.md', encoding='utf-8') as f:
long_description = f.read()

version = ''
with open('discordrpc/__init__.py') as f:
with open('discordrpc/__init__.py', encoding='utf-8') as f:
version = re.search(r'^__version__\s*=\s*[\'"]([^\'"]*)[\'"]', f.read(), re.MULTILINE).group(1)

if not version:
Expand Down