rlstatsapi 0.1.1__tar.gz → 0.1.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rlstatsapi
3
- Version: 0.1.1
3
+ Version: 0.1.2
4
4
  Summary: Rocket League Stats API client
5
5
  Author: Manuel Cabral
6
6
  License: MIT
@@ -32,7 +32,7 @@ pip install rlstatsapi
32
32
  From GitHub (latest `main`):
33
33
 
34
34
  ```bash
35
- pip install git+https://github.com/nania/RocketLeagueStatsAPI.git
35
+ pip install git+https://github.com/manucabral/RocketLeagueStatsAPI.git
36
36
  ```
37
37
 
38
38
  ## Rocket League setup
@@ -57,7 +57,6 @@ from rlstatsapi import StatsClient
57
57
 
58
58
  logging.basicConfig(level=logging.INFO)
59
59
 
60
-
61
60
  async def main() -> None:
62
61
  client = StatsClient(log_enabled=True)
63
62
  client.on_any(lambda msg: print(msg.event, msg.data))
@@ -68,17 +67,44 @@ async def main() -> None:
68
67
  finally:
69
68
  await client.disconnect()
70
69
 
70
+ asyncio.run(main())
71
+ ```
72
+
73
+ ## Example Event (Goals)
74
+
75
+ ```python
76
+ import asyncio
77
+ from rlstatsapi import StatsClient
78
+ from rlstatsapi.models import EventMessage
79
+ from rlstatsapi.types import GoalScoredPayload, cast_event_data
80
+
81
+ async def on_goal(msg: EventMessage) -> None:
82
+ data: GoalScoredPayload = cast_event_data("GoalScored", msg.data)
83
+ scorer = data.get("Scorer", {})
84
+ print("Goal by:", scorer.get("Name"))
85
+
86
+ async def main() -> None:
87
+ client = StatsClient()
88
+ client.on("GoalScored", on_goal)
89
+ await client.connect()
90
+ try:
91
+ await asyncio.Event().wait()
92
+ finally:
93
+ await client.disconnect()
71
94
 
72
95
  asyncio.run(main())
73
96
  ```
74
97
 
98
+
75
99
  ## Public API
76
100
 
77
- - `StatsClient(host="127.0.0.1", port=49123, reconnect=True, reconnect_delay=0.5, include_raw=False, queue_size=2048, connect_timeout=5.0, log_enabled=False)`
101
+ - `StatsClient(...)`
78
102
  - `connect()` / `disconnect()`
79
103
  - `on(event_name, handler)`
80
104
  - `on_any(handler)`
81
105
  - `events()`
106
+ - typed helpers: `EventName`, `TypedEventMessage[...]`, `cast_event_data(...)`
107
+
82
108
 
83
109
  ## Notes
84
110
 
@@ -13,7 +13,7 @@ pip install rlstatsapi
13
13
  From GitHub (latest `main`):
14
14
 
15
15
  ```bash
16
- pip install git+https://github.com/nania/RocketLeagueStatsAPI.git
16
+ pip install git+https://github.com/manucabral/RocketLeagueStatsAPI.git
17
17
  ```
18
18
 
19
19
  ## Rocket League setup
@@ -38,7 +38,6 @@ from rlstatsapi import StatsClient
38
38
 
39
39
  logging.basicConfig(level=logging.INFO)
40
40
 
41
-
42
41
  async def main() -> None:
43
42
  client = StatsClient(log_enabled=True)
44
43
  client.on_any(lambda msg: print(msg.event, msg.data))
@@ -49,19 +48,46 @@ async def main() -> None:
49
48
  finally:
50
49
  await client.disconnect()
51
50
 
51
+ asyncio.run(main())
52
+ ```
53
+
54
+ ## Example Event (Goals)
55
+
56
+ ```python
57
+ import asyncio
58
+ from rlstatsapi import StatsClient
59
+ from rlstatsapi.models import EventMessage
60
+ from rlstatsapi.types import GoalScoredPayload, cast_event_data
61
+
62
+ async def on_goal(msg: EventMessage) -> None:
63
+ data: GoalScoredPayload = cast_event_data("GoalScored", msg.data)
64
+ scorer = data.get("Scorer", {})
65
+ print("Goal by:", scorer.get("Name"))
66
+
67
+ async def main() -> None:
68
+ client = StatsClient()
69
+ client.on("GoalScored", on_goal)
70
+ await client.connect()
71
+ try:
72
+ await asyncio.Event().wait()
73
+ finally:
74
+ await client.disconnect()
52
75
 
53
76
  asyncio.run(main())
54
77
  ```
55
78
 
79
+
56
80
  ## Public API
57
81
 
58
- - `StatsClient(host="127.0.0.1", port=49123, reconnect=True, reconnect_delay=0.5, include_raw=False, queue_size=2048, connect_timeout=5.0, log_enabled=False)`
82
+ - `StatsClient(...)`
59
83
  - `connect()` / `disconnect()`
60
84
  - `on(event_name, handler)`
61
85
  - `on_any(handler)`
62
86
  - `events()`
87
+ - typed helpers: `EventName`, `TypedEventMessage[...]`, `cast_event_data(...)`
88
+
63
89
 
64
90
  ## Notes
65
91
 
66
92
  - Works for regular matches. Some fields like `MatchGuid` are only present in online/LAN contexts.
67
- - In current builds this endpoint may behave as plain TCP JSON stream instead of websocket upgrade. This library handles the TCP stream format.
93
+ - In current builds this endpoint may behave as plain TCP JSON stream instead of websocket upgrade. This library handles the TCP stream format.
@@ -1,30 +1,30 @@
1
- [build-system]
2
- requires = ["setuptools>=69", "wheel"]
3
- build-backend = "setuptools.build_meta"
4
-
5
- [project]
6
- name = "rlstatsapi"
7
- version = "0.1.1"
8
- description = "Rocket League Stats API client"
9
- readme = "README.md"
10
- requires-python = ">=3.11"
11
- license = { text = "MIT" }
12
- authors = [{ name = "Manuel Cabral" }]
13
- keywords = ["rocket-league", "stats", "socket", "asyncio"]
14
- classifiers = [
15
- "Programming Language :: Python :: 3",
16
- "Programming Language :: Python :: 3 :: Only",
17
- "License :: OSI Approved :: MIT License",
18
- "Operating System :: OS Independent",
19
- "Topic :: Games/Entertainment",
20
- "Topic :: Software Development :: Libraries",
21
- ]
22
-
23
- [project.urls]
24
- Homepage = "https://github.com/manucabral/RocketLeagueStatsAPI"
25
-
26
- [tool.setuptools]
27
- package-dir = {"" = "src"}
28
-
29
- [tool.setuptools.packages.find]
30
- where = ["src"]
1
+ [build-system]
2
+ requires = ["setuptools>=69", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "rlstatsapi"
7
+ version = "0.1.2"
8
+ description = "Rocket League Stats API client"
9
+ readme = "README.md"
10
+ requires-python = ">=3.11"
11
+ license = { text = "MIT" }
12
+ authors = [{ name = "Manuel Cabral" }]
13
+ keywords = ["rocket-league", "stats", "socket", "asyncio"]
14
+ classifiers = [
15
+ "Programming Language :: Python :: 3",
16
+ "Programming Language :: Python :: 3 :: Only",
17
+ "License :: OSI Approved :: MIT License",
18
+ "Operating System :: OS Independent",
19
+ "Topic :: Games/Entertainment",
20
+ "Topic :: Software Development :: Libraries",
21
+ ]
22
+
23
+ [project.urls]
24
+ Homepage = "https://github.com/manucabral/RocketLeagueStatsAPI"
25
+
26
+ [tool.setuptools]
27
+ package-dir = {"" = "src"}
28
+
29
+ [tool.setuptools.packages.find]
30
+ where = ["src"]
@@ -0,0 +1,25 @@
1
+ """rlstatsapi public API."""
2
+
3
+ from .client import StatsClient
4
+ from .events import KNOWN_EVENTS
5
+ from .models import EventMessage
6
+ from .types import (
7
+ EventName,
8
+ GoalScoredPayload,
9
+ KnownEventMessage,
10
+ TypedEventMessage,
11
+ UpdateStatePayload,
12
+ cast_event_data,
13
+ )
14
+
15
+ __all__ = [
16
+ "StatsClient",
17
+ "EventMessage",
18
+ "KNOWN_EVENTS",
19
+ "EventName",
20
+ "TypedEventMessage",
21
+ "KnownEventMessage",
22
+ "UpdateStatePayload",
23
+ "GoalScoredPayload",
24
+ "cast_event_data",
25
+ ]
@@ -181,4 +181,4 @@ def _parse_message_obj(
181
181
  if not isinstance(event, str) or not isinstance(data, dict):
182
182
  return None
183
183
 
184
- return EventMessage(event=event, data=data, raw=raw if include_raw else None)
184
+ return EventMessage(event=event, data=data, raw=raw if include_raw else None)
@@ -0,0 +1,373 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from typing import Any, Generic, Literal, Mapping, TypeAlias, TypeVar, TypedDict, cast, overload
5
+
6
+ EventName: TypeAlias = Literal[
7
+ "UpdateState",
8
+ "BallHit",
9
+ "ClockUpdatedSeconds",
10
+ "CountdownBegin",
11
+ "CrossbarHit",
12
+ "GoalReplayEnd",
13
+ "GoalReplayStart",
14
+ "GoalReplayWillEnd",
15
+ "GoalScored",
16
+ "MatchCreated",
17
+ "MatchInitialized",
18
+ "MatchDestroyed",
19
+ "MatchEnded",
20
+ "MatchPaused",
21
+ "MatchUnpaused",
22
+ "PodiumStart",
23
+ "ReplayCreated",
24
+ "RoundStarted",
25
+ "StatfeedEvent",
26
+ ]
27
+
28
+
29
+ class Vector3(TypedDict):
30
+ X: float
31
+ Y: float
32
+ Z: float
33
+
34
+
35
+ class PlayerRef(TypedDict):
36
+ Name: str
37
+ Shortcut: int
38
+ TeamNum: int
39
+
40
+
41
+ class TeamState(TypedDict):
42
+ Name: str
43
+ TeamNum: int
44
+ Score: int
45
+ ColorPrimary: str
46
+ ColorSecondary: str
47
+
48
+
49
+ class BallState(TypedDict):
50
+ Speed: float
51
+ TeamNum: int
52
+
53
+
54
+ class PlayerAttacker(TypedDict):
55
+ Name: str
56
+ Shortcut: int
57
+ TeamNum: int
58
+
59
+
60
+ class TargetRef(TypedDict):
61
+ Name: str
62
+ Shortcut: int
63
+ TeamNum: int
64
+
65
+
66
+ class UpdateStatePlayer(TypedDict, total=False):
67
+ Name: str
68
+ PrimaryId: str
69
+ Shortcut: int
70
+ TeamNum: int
71
+ Score: int
72
+ Goals: int
73
+ Shots: int
74
+ Assists: int
75
+ Saves: int
76
+ Touches: int
77
+ CarTouches: int
78
+ Demos: int
79
+ bHasCar: bool
80
+ Speed: float
81
+ Boost: int
82
+ bBoosting: bool
83
+ bOnGround: bool
84
+ bOnWall: bool
85
+ bPowersliding: bool
86
+ bDemolished: bool
87
+ Attacker: PlayerAttacker
88
+ bSupersonic: bool
89
+
90
+
91
+ class UpdateStateGame(TypedDict, total=False):
92
+ Teams: list[TeamState]
93
+ TimeSeconds: int
94
+ bOvertime: bool
95
+ Frame: int
96
+ Elapsed: float
97
+ Ball: BallState
98
+ bReplay: bool
99
+ bHasWinner: bool
100
+ Winner: str
101
+ Arena: str
102
+ bHasTarget: bool
103
+ Target: TargetRef
104
+
105
+
106
+ class UpdateStatePayload(TypedDict, total=False):
107
+ MatchGuid: str
108
+ Players: list[UpdateStatePlayer]
109
+ Game: UpdateStateGame
110
+
111
+
112
+ class BallHitBall(TypedDict):
113
+ PreHitSpeed: float
114
+ PostHitSpeed: float
115
+ Location: Vector3
116
+
117
+
118
+ class BallHitPayload(TypedDict, total=False):
119
+ MatchGuid: str
120
+ Players: list[PlayerRef]
121
+ Ball: BallHitBall
122
+
123
+
124
+ class ClockUpdatedSecondsPayload(TypedDict, total=False):
125
+ MatchGuid: str
126
+ TimeSeconds: int
127
+ bOvertime: bool
128
+
129
+
130
+ class CountdownBeginPayload(TypedDict, total=False):
131
+ MatchGuid: str
132
+
133
+
134
+ class BallLastTouch(TypedDict):
135
+ Player: PlayerRef
136
+ Speed: float
137
+
138
+
139
+ class CrossbarHitPayload(TypedDict, total=False):
140
+ MatchGuid: str
141
+ BallLocation: Vector3
142
+ BallSpeed: float
143
+ ImpactForce: float
144
+ BallLastTouch: BallLastTouch
145
+
146
+
147
+ class GoalReplayEndPayload(TypedDict, total=False):
148
+ MatchGuid: str
149
+
150
+
151
+ class GoalReplayStartPayload(TypedDict, total=False):
152
+ MatchGuid: str
153
+
154
+
155
+ class GoalReplayWillEndPayload(TypedDict, total=False):
156
+ MatchGuid: str
157
+
158
+
159
+ class GoalScoredPayload(TypedDict, total=False):
160
+ MatchGuid: str
161
+ GoalSpeed: float
162
+ GoalTime: float
163
+ ImpactLocation: Vector3
164
+ Scorer: PlayerRef
165
+ Assister: PlayerRef
166
+ BallLastTouch: BallLastTouch
167
+
168
+
169
+ class MatchCreatedPayload(TypedDict, total=False):
170
+ MatchGuid: str
171
+
172
+
173
+ class MatchInitializedPayload(TypedDict, total=False):
174
+ MatchGuid: str
175
+
176
+
177
+ class MatchDestroyedPayload(TypedDict, total=False):
178
+ MatchGuid: str
179
+
180
+
181
+ class MatchEndedPayload(TypedDict, total=False):
182
+ MatchGuid: str
183
+ WinnerTeamNum: int
184
+
185
+
186
+ class MatchPausedPayload(TypedDict, total=False):
187
+ MatchGuid: str
188
+
189
+
190
+ class MatchUnpausedPayload(TypedDict, total=False):
191
+ MatchGuid: str
192
+
193
+
194
+ class PodiumStartPayload(TypedDict, total=False):
195
+ MatchGuid: str
196
+
197
+
198
+ class ReplayCreatedPayload(TypedDict, total=False):
199
+ MatchGuid: str
200
+
201
+
202
+ class RoundStartedPayload(TypedDict, total=False):
203
+ MatchGuid: str
204
+
205
+
206
+ class StatfeedEventPayload(TypedDict, total=False):
207
+ MatchGuid: str
208
+ EventName: str
209
+ Type: str
210
+ MainTarget: PlayerRef
211
+ SecondaryTarget: PlayerRef
212
+
213
+
214
+ KnownEventPayload: TypeAlias = (
215
+ UpdateStatePayload
216
+ | BallHitPayload
217
+ | ClockUpdatedSecondsPayload
218
+ | CountdownBeginPayload
219
+ | CrossbarHitPayload
220
+ | GoalReplayEndPayload
221
+ | GoalReplayStartPayload
222
+ | GoalReplayWillEndPayload
223
+ | GoalScoredPayload
224
+ | MatchCreatedPayload
225
+ | MatchInitializedPayload
226
+ | MatchDestroyedPayload
227
+ | MatchEndedPayload
228
+ | MatchPausedPayload
229
+ | MatchUnpausedPayload
230
+ | PodiumStartPayload
231
+ | ReplayCreatedPayload
232
+ | RoundStartedPayload
233
+ | StatfeedEventPayload
234
+ )
235
+
236
+ KnownEventPayloadMap: TypeAlias = dict[EventName, KnownEventPayload]
237
+
238
+ TData = TypeVar("TData", bound=Mapping[str, Any])
239
+
240
+
241
+ @dataclass(slots=True)
242
+ class TypedEventMessage(Generic[TData]):
243
+ event: str
244
+ data: TData
245
+ raw: str | None = None
246
+
247
+
248
+ KnownEventMessage: TypeAlias = TypedEventMessage[KnownEventPayload]
249
+
250
+
251
+ @overload
252
+ def cast_event_data(
253
+ event_name: Literal["UpdateState"], data: Mapping[str, Any]
254
+ ) -> UpdateStatePayload: ...
255
+
256
+
257
+ @overload
258
+ def cast_event_data(
259
+ event_name: Literal["BallHit"], data: Mapping[str, Any]
260
+ ) -> BallHitPayload: ...
261
+
262
+
263
+ @overload
264
+ def cast_event_data(
265
+ event_name: Literal["ClockUpdatedSeconds"], data: Mapping[str, Any]
266
+ ) -> ClockUpdatedSecondsPayload: ...
267
+
268
+
269
+ @overload
270
+ def cast_event_data(
271
+ event_name: Literal["CountdownBegin"], data: Mapping[str, Any]
272
+ ) -> CountdownBeginPayload: ...
273
+
274
+
275
+ @overload
276
+ def cast_event_data(
277
+ event_name: Literal["CrossbarHit"], data: Mapping[str, Any]
278
+ ) -> CrossbarHitPayload: ...
279
+
280
+
281
+ @overload
282
+ def cast_event_data(
283
+ event_name: Literal["GoalReplayEnd"], data: Mapping[str, Any]
284
+ ) -> GoalReplayEndPayload: ...
285
+
286
+
287
+ @overload
288
+ def cast_event_data(
289
+ event_name: Literal["GoalReplayStart"], data: Mapping[str, Any]
290
+ ) -> GoalReplayStartPayload: ...
291
+
292
+
293
+ @overload
294
+ def cast_event_data(
295
+ event_name: Literal["GoalReplayWillEnd"], data: Mapping[str, Any]
296
+ ) -> GoalReplayWillEndPayload: ...
297
+
298
+
299
+ @overload
300
+ def cast_event_data(
301
+ event_name: Literal["GoalScored"], data: Mapping[str, Any]
302
+ ) -> GoalScoredPayload: ...
303
+
304
+
305
+ @overload
306
+ def cast_event_data(
307
+ event_name: Literal["MatchCreated"], data: Mapping[str, Any]
308
+ ) -> MatchCreatedPayload: ...
309
+
310
+
311
+ @overload
312
+ def cast_event_data(
313
+ event_name: Literal["MatchInitialized"], data: Mapping[str, Any]
314
+ ) -> MatchInitializedPayload: ...
315
+
316
+
317
+ @overload
318
+ def cast_event_data(
319
+ event_name: Literal["MatchDestroyed"], data: Mapping[str, Any]
320
+ ) -> MatchDestroyedPayload: ...
321
+
322
+
323
+ @overload
324
+ def cast_event_data(
325
+ event_name: Literal["MatchEnded"], data: Mapping[str, Any]
326
+ ) -> MatchEndedPayload: ...
327
+
328
+
329
+ @overload
330
+ def cast_event_data(
331
+ event_name: Literal["MatchPaused"], data: Mapping[str, Any]
332
+ ) -> MatchPausedPayload: ...
333
+
334
+
335
+ @overload
336
+ def cast_event_data(
337
+ event_name: Literal["MatchUnpaused"], data: Mapping[str, Any]
338
+ ) -> MatchUnpausedPayload: ...
339
+
340
+
341
+ @overload
342
+ def cast_event_data(
343
+ event_name: Literal["PodiumStart"], data: Mapping[str, Any]
344
+ ) -> PodiumStartPayload: ...
345
+
346
+
347
+ @overload
348
+ def cast_event_data(
349
+ event_name: Literal["ReplayCreated"], data: Mapping[str, Any]
350
+ ) -> ReplayCreatedPayload: ...
351
+
352
+
353
+ @overload
354
+ def cast_event_data(
355
+ event_name: Literal["RoundStarted"], data: Mapping[str, Any]
356
+ ) -> RoundStartedPayload: ...
357
+
358
+
359
+ @overload
360
+ def cast_event_data(
361
+ event_name: Literal["StatfeedEvent"], data: Mapping[str, Any]
362
+ ) -> StatfeedEventPayload: ...
363
+
364
+
365
+ @overload
366
+ def cast_event_data(event_name: str, data: Mapping[str, Any]) -> Mapping[str, Any]: ...
367
+
368
+
369
+ def cast_event_data(event_name: str, data: Mapping[str, Any]) -> Mapping[str, Any]:
370
+ """Type-only helper to narrow `data` based on event name."""
371
+
372
+ _ = event_name
373
+ return cast(Mapping[str, Any], data)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rlstatsapi
3
- Version: 0.1.1
3
+ Version: 0.1.2
4
4
  Summary: Rocket League Stats API client
5
5
  Author: Manuel Cabral
6
6
  License: MIT
@@ -32,7 +32,7 @@ pip install rlstatsapi
32
32
  From GitHub (latest `main`):
33
33
 
34
34
  ```bash
35
- pip install git+https://github.com/nania/RocketLeagueStatsAPI.git
35
+ pip install git+https://github.com/manucabral/RocketLeagueStatsAPI.git
36
36
  ```
37
37
 
38
38
  ## Rocket League setup
@@ -57,7 +57,6 @@ from rlstatsapi import StatsClient
57
57
 
58
58
  logging.basicConfig(level=logging.INFO)
59
59
 
60
-
61
60
  async def main() -> None:
62
61
  client = StatsClient(log_enabled=True)
63
62
  client.on_any(lambda msg: print(msg.event, msg.data))
@@ -68,17 +67,44 @@ async def main() -> None:
68
67
  finally:
69
68
  await client.disconnect()
70
69
 
70
+ asyncio.run(main())
71
+ ```
72
+
73
+ ## Example Event (Goals)
74
+
75
+ ```python
76
+ import asyncio
77
+ from rlstatsapi import StatsClient
78
+ from rlstatsapi.models import EventMessage
79
+ from rlstatsapi.types import GoalScoredPayload, cast_event_data
80
+
81
+ async def on_goal(msg: EventMessage) -> None:
82
+ data: GoalScoredPayload = cast_event_data("GoalScored", msg.data)
83
+ scorer = data.get("Scorer", {})
84
+ print("Goal by:", scorer.get("Name"))
85
+
86
+ async def main() -> None:
87
+ client = StatsClient()
88
+ client.on("GoalScored", on_goal)
89
+ await client.connect()
90
+ try:
91
+ await asyncio.Event().wait()
92
+ finally:
93
+ await client.disconnect()
71
94
 
72
95
  asyncio.run(main())
73
96
  ```
74
97
 
98
+
75
99
  ## Public API
76
100
 
77
- - `StatsClient(host="127.0.0.1", port=49123, reconnect=True, reconnect_delay=0.5, include_raw=False, queue_size=2048, connect_timeout=5.0, log_enabled=False)`
101
+ - `StatsClient(...)`
78
102
  - `connect()` / `disconnect()`
79
103
  - `on(event_name, handler)`
80
104
  - `on_any(handler)`
81
105
  - `events()`
106
+ - typed helpers: `EventName`, `TypedEventMessage[...]`, `cast_event_data(...)`
107
+
82
108
 
83
109
  ## Notes
84
110
 
@@ -5,6 +5,7 @@ src/rlstatsapi/__init__.py
5
5
  src/rlstatsapi/client.py
6
6
  src/rlstatsapi/events.py
7
7
  src/rlstatsapi/models.py
8
+ src/rlstatsapi/types.py
8
9
  src/rlstatsapi.egg-info/PKG-INFO
9
10
  src/rlstatsapi.egg-info/SOURCES.txt
10
11
  src/rlstatsapi.egg-info/dependency_links.txt
@@ -1,7 +0,0 @@
1
- """rlstatsapi public API."""
2
-
3
- from .client import StatsClient
4
- from .models import EventMessage
5
- from .events import KNOWN_EVENTS
6
-
7
- __all__ = ["StatsClient", "EventMessage", "KNOWN_EVENTS"]
File without changes
File without changes