lynxpresence 4.6.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2022 qwertyquerty
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,92 @@
1
+ Metadata-Version: 2.4
2
+ Name: lynxpresence
3
+ Version: 4.6.2
4
+ Summary: Discord RPC client written in Python
5
+ Author: C0rn3j
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/C0rn3j/lynxpresence
8
+ Project-URL: Repository, https://github.com/C0rn3j/lynxpresence
9
+ Project-URL: Issues, https://github.com/C0rn3j/lynxpresence/issues
10
+ Keywords: discord,rich presence,lynxpresence,rpc,api,wrapper,gamers,chat,irc
11
+ Classifier: Development Status :: 5 - Production/Stable
12
+ Classifier: Operating System :: Microsoft :: Windows
13
+ Classifier: Operating System :: POSIX :: Linux
14
+ Classifier: Operating System :: MacOS :: MacOS X
15
+ Classifier: Programming Language :: Python
16
+ Classifier: Programming Language :: Python :: 3.9
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Programming Language :: Python :: 3 :: Only
22
+ Classifier: Programming Language :: Python :: Implementation :: CPython
23
+ Classifier: Typing :: Typed
24
+ Classifier: Intended Audience :: Developers
25
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
26
+ Classifier: Topic :: Software Development :: Libraries
27
+ Classifier: Topic :: Communications :: Chat
28
+ Classifier: Framework :: AsyncIO
29
+ Requires-Python: >=3.9
30
+ Description-Content-Type: text/markdown
31
+ License-File: LICENSE
32
+ Provides-Extra: dev
33
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
34
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
35
+ Requires-Dist: pytest-mock>=3.10.0; extra == "dev"
36
+ Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
37
+ Requires-Dist: black>=23.0.0; extra == "dev"
38
+ Requires-Dist: flake8>=6.0.0; extra == "dev"
39
+ Requires-Dist: mypy>=1.0.0; extra == "dev"
40
+ Requires-Dist: isort>=5.12.0; extra == "dev"
41
+ Requires-Dist: sphinx>=5.0.0; extra == "dev"
42
+ Requires-Dist: sphinx-rtd-theme>=1.2.0; extra == "dev"
43
+ Dynamic: license-file
44
+
45
+ Forked as upstream did not make a release in quite a while, I hope to kill this fork off in the future.
46
+
47
+ I recommend doing `import lynxpresence as pypresence` to keep compatibility and allow easy switching in case upstream picks up again.
48
+
49
+ > A Discord Rich Presence Client in Python? Looks like you've come to the right place.
50
+
51
+ ![PyPI](https://img.shields.io/pypi/v/lynxpresence.svg?style=for-the-badge)
52
+
53
+ ----------
54
+
55
+ # Installation
56
+
57
+ Install pypresence with `pip`, for the latest development version:
58
+
59
+ ```shell
60
+ pip install https://github.com/C0rn3j/lynxpresence/archive/master.zip
61
+ ```
62
+
63
+ For the latest stable version:
64
+
65
+ ```shell
66
+ pip install lynxpresence
67
+ ```
68
+
69
+ ----------
70
+
71
+ # Documentation
72
+
73
+ > [!Note]
74
+ > You need an **authorized app** to do anything besides rich presence!
75
+
76
+ #### [pypresence Documentation](https://qwertyquerty.github.io/pypresence/html/index.html)
77
+ #### [Discord Rich Presence Documentation](https://discord.com/developers/docs/rich-presence/how-to)
78
+ #### [Discord RPC Documentation](https://discord.com/developers/docs/topics/rpc)
79
+ #### [pyresence Discord Support Server](https://discord.gg/JF3kg77)
80
+ #### [Discord API Support Server](https://discord.gg/discord-api)
81
+
82
+ ----------
83
+
84
+ # Examples
85
+
86
+ Examples can be found in the [examples](https://github.com/C0rn3j/pypresence/tree/master/examples) directory, and you can contribute your own examples if you wish, just read [examples.md](https://github.com/C0rn3j/pypresence/blob/master/examples/examples.md)!
87
+
88
+ ----------
89
+
90
+ # Development
91
+
92
+ Want to contribute? Check out the **[Development Guide](DEVELOPMENT.md)** for setup instructions, testing, code quality tools, and contribution guidelines.
@@ -0,0 +1,48 @@
1
+ Forked as upstream did not make a release in quite a while, I hope to kill this fork off in the future.
2
+
3
+ I recommend doing `import lynxpresence as pypresence` to keep compatibility and allow easy switching in case upstream picks up again.
4
+
5
+ > A Discord Rich Presence Client in Python? Looks like you've come to the right place.
6
+
7
+ ![PyPI](https://img.shields.io/pypi/v/lynxpresence.svg?style=for-the-badge)
8
+
9
+ ----------
10
+
11
+ # Installation
12
+
13
+ Install pypresence with `pip`, for the latest development version:
14
+
15
+ ```shell
16
+ pip install https://github.com/C0rn3j/lynxpresence/archive/master.zip
17
+ ```
18
+
19
+ For the latest stable version:
20
+
21
+ ```shell
22
+ pip install lynxpresence
23
+ ```
24
+
25
+ ----------
26
+
27
+ # Documentation
28
+
29
+ > [!Note]
30
+ > You need an **authorized app** to do anything besides rich presence!
31
+
32
+ #### [pypresence Documentation](https://qwertyquerty.github.io/pypresence/html/index.html)
33
+ #### [Discord Rich Presence Documentation](https://discord.com/developers/docs/rich-presence/how-to)
34
+ #### [Discord RPC Documentation](https://discord.com/developers/docs/topics/rpc)
35
+ #### [pyresence Discord Support Server](https://discord.gg/JF3kg77)
36
+ #### [Discord API Support Server](https://discord.gg/discord-api)
37
+
38
+ ----------
39
+
40
+ # Examples
41
+
42
+ Examples can be found in the [examples](https://github.com/C0rn3j/pypresence/tree/master/examples) directory, and you can contribute your own examples if you wish, just read [examples.md](https://github.com/C0rn3j/pypresence/blob/master/examples/examples.md)!
43
+
44
+ ----------
45
+
46
+ # Development
47
+
48
+ Want to contribute? Check out the **[Development Guide](DEVELOPMENT.md)** for setup instructions, testing, code quality tools, and contribution guidelines.
@@ -0,0 +1,15 @@
1
+ """
2
+ Python RPC Client for Discord
3
+ """
4
+
5
+ from .baseclient import BaseClient
6
+ from .client import AioClient, Client
7
+ from .exceptions import *
8
+ from .presence import AioPresence, Presence
9
+ from .types import ActivityType, StatusDisplayType
10
+
11
+ __title__ = "lynxpresence"
12
+ __author__ = "C0rn3j"
13
+ __copyright__ = "Copyright 2018 - 2025"
14
+ __license__ = "MIT"
15
+ __version__ = "4.6.2"
@@ -0,0 +1,162 @@
1
+ from __future__ import annotations
2
+
3
+ import asyncio
4
+ import inspect
5
+ import json
6
+ import struct
7
+ import sys
8
+
9
+ # TODO: Get rid of this import * lol
10
+ from .exceptions import (
11
+ ConnectionTimeout,
12
+ DiscordError,
13
+ DiscordNotFound,
14
+ InvalidArgument,
15
+ InvalidID,
16
+ InvalidPipe,
17
+ PipeClosed,
18
+ PyPresenceException,
19
+ ResponseTimeout,
20
+ ServerError,
21
+ )
22
+ from .payloads import Payload
23
+ from .utils import get_event_loop, get_ipc_path
24
+
25
+
26
+ class BaseClient:
27
+
28
+ def __init__(self, client_id: str, **kwargs):
29
+ loop = kwargs.get("loop", None)
30
+ handler = kwargs.get("handler", None)
31
+ self.pipe = kwargs.get("pipe", None)
32
+ self.isasync = kwargs.get("isasync", False)
33
+ self.connection_timeout = kwargs.get("connection_timeout", 30)
34
+ self.response_timeout = kwargs.get("response_timeout", 10)
35
+
36
+ client_id = str(client_id)
37
+
38
+ if loop is not None:
39
+ self.update_event_loop(loop)
40
+ else:
41
+ self.update_event_loop(get_event_loop())
42
+
43
+ self.sock_reader: asyncio.StreamReader | None = None
44
+ self.sock_writer: asyncio.StreamWriter | None = None
45
+
46
+ self.client_id = client_id
47
+
48
+ if handler is not None:
49
+ if not inspect.isfunction(handler):
50
+ raise PyPresenceException("Error handler must be a function.")
51
+ args = inspect.getfullargspec(handler).args
52
+ if args[0] == "self":
53
+ args = args[1:]
54
+ if len(args) != 2:
55
+ raise PyPresenceException(
56
+ "Error handler should only accept two arguments."
57
+ )
58
+
59
+ if self.isasync:
60
+ if not inspect.iscoroutinefunction(handler):
61
+ raise InvalidArgument(
62
+ "Coroutine",
63
+ "Subroutine",
64
+ "You are running async mode - "
65
+ "your error handler should be awaitable.",
66
+ )
67
+ err_handler = self._async_err_handle
68
+ else:
69
+ err_handler = self._err_handle
70
+
71
+ self.loop.set_exception_handler(err_handler)
72
+ self.handler = handler
73
+
74
+ if getattr(self, "on_event", None): # Tasty bad code ;^)
75
+ self._events_on = True
76
+ else:
77
+ self._events_on = False
78
+
79
+ def update_event_loop(self, loop):
80
+ # noinspection PyAttributeOutsideInit
81
+ self.loop = loop
82
+ asyncio.set_event_loop(self.loop)
83
+
84
+ def _err_handle(self, loop, context: dict):
85
+ result = self.handler(context["exception"], context["future"])
86
+ if inspect.iscoroutinefunction(self.handler):
87
+ loop.run_until_complete(result)
88
+
89
+ # noinspection PyUnusedLocal
90
+ async def _async_err_handle(self, loop, context: dict):
91
+ await self.handler(context["exception"], context["future"])
92
+
93
+ async def read_output(self):
94
+ try:
95
+ preamble = await asyncio.wait_for(
96
+ self.sock_reader.read(8), self.response_timeout
97
+ )
98
+ status_code, length = struct.unpack("<II", preamble[:8])
99
+ data = await asyncio.wait_for(
100
+ self.sock_reader.read(length), self.response_timeout
101
+ )
102
+ except (BrokenPipeError, struct.error):
103
+ raise PipeClosed
104
+ except asyncio.TimeoutError:
105
+ raise ResponseTimeout
106
+ payload = json.loads(data.decode("utf-8"))
107
+ if payload["evt"] == "ERROR":
108
+ raise ServerError(payload["data"]["message"])
109
+ return payload
110
+
111
+ def send_data(self, op: int, payload: dict | Payload):
112
+ if isinstance(payload, Payload):
113
+ payload = payload.data
114
+ payload_string = json.dumps(payload)
115
+
116
+ assert (
117
+ self.sock_writer is not None
118
+ ), "You must connect your client before sending events!"
119
+
120
+ self.sock_writer.write(
121
+ struct.pack("<II", op, len(payload_string)) + payload_string.encode("utf-8")
122
+ )
123
+
124
+ async def create_reader_writer(self, ipc_path):
125
+ try:
126
+ if sys.platform == "linux" or sys.platform == "darwin":
127
+ self.sock_reader, self.sock_writer = await asyncio.wait_for(
128
+ asyncio.open_unix_connection(ipc_path), self.connection_timeout
129
+ )
130
+ elif sys.platform == "win32":
131
+ self.sock_reader = asyncio.StreamReader(loop=self.loop)
132
+ reader_protocol = asyncio.StreamReaderProtocol(
133
+ self.sock_reader, loop=self.loop
134
+ )
135
+ self.sock_writer, _ = await asyncio.wait_for(
136
+ self.loop.create_pipe_connection(lambda: reader_protocol, ipc_path),
137
+ self.connection_timeout,
138
+ )
139
+ except FileNotFoundError:
140
+ raise InvalidPipe
141
+ except asyncio.TimeoutError:
142
+ raise ConnectionTimeout
143
+
144
+ async def handshake(self):
145
+ ipc_path = get_ipc_path(self.pipe)
146
+ if not ipc_path:
147
+ raise DiscordNotFound
148
+
149
+ await self.create_reader_writer(ipc_path)
150
+
151
+ self.send_data(0, {"v": 1, "client_id": self.client_id})
152
+ preamble = await self.sock_reader.read(8)
153
+ if len(preamble) < 8:
154
+ raise InvalidPipe # this sometimes happens for some reason, perhaps discord cannot always accept all the connections?
155
+ code, length = struct.unpack("<ii", preamble)
156
+ data = json.loads(await self.sock_reader.read(length))
157
+ if "code" in data:
158
+ if data["message"] == "Invalid Client ID":
159
+ raise InvalidID
160
+ raise DiscordError(data["code"], data["message"])
161
+ if self._events_on:
162
+ self.sock_reader.feed_data = self.on_event