rlstatsapi 0.1.3__tar.gz → 0.1.6__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.
Files changed (27) hide show
  1. {rlstatsapi-0.1.3 → rlstatsapi-0.1.6}/LICENSE +21 -21
  2. rlstatsapi-0.1.6/PKG-INFO +150 -0
  3. rlstatsapi-0.1.6/README.md +126 -0
  4. {rlstatsapi-0.1.3 → rlstatsapi-0.1.6}/pyproject.toml +47 -42
  5. {rlstatsapi-0.1.3 → rlstatsapi-0.1.6}/setup.cfg +4 -4
  6. {rlstatsapi-0.1.3 → rlstatsapi-0.1.6}/src/rlstatsapi/__init__.py +36 -3
  7. rlstatsapi-0.1.6/src/rlstatsapi/__main__.py +148 -0
  8. {rlstatsapi-0.1.3 → rlstatsapi-0.1.6}/src/rlstatsapi/client.py +234 -43
  9. rlstatsapi-0.1.6/src/rlstatsapi/config.py +265 -0
  10. rlstatsapi-0.1.6/src/rlstatsapi/models.py +92 -0
  11. rlstatsapi-0.1.6/src/rlstatsapi/state.py +164 -0
  12. {rlstatsapi-0.1.3 → rlstatsapi-0.1.6}/src/rlstatsapi/types.py +392 -383
  13. rlstatsapi-0.1.6/src/rlstatsapi.egg-info/PKG-INFO +150 -0
  14. {rlstatsapi-0.1.3 → rlstatsapi-0.1.6}/src/rlstatsapi.egg-info/SOURCES.txt +7 -1
  15. rlstatsapi-0.1.6/src/rlstatsapi.egg-info/entry_points.txt +2 -0
  16. {rlstatsapi-0.1.3 → rlstatsapi-0.1.6}/src/rlstatsapi.egg-info/requires.txt +1 -0
  17. {rlstatsapi-0.1.3 → rlstatsapi-0.1.6}/tests/test_client.py +1 -0
  18. rlstatsapi-0.1.6/tests/test_client_unit.py +340 -0
  19. rlstatsapi-0.1.6/tests/test_config.py +123 -0
  20. rlstatsapi-0.1.3/PKG-INFO +0 -194
  21. rlstatsapi-0.1.3/README.md +0 -170
  22. rlstatsapi-0.1.3/src/rlstatsapi/models.py +0 -13
  23. rlstatsapi-0.1.3/src/rlstatsapi.egg-info/PKG-INFO +0 -194
  24. {rlstatsapi-0.1.3 → rlstatsapi-0.1.6}/src/rlstatsapi/events.py +0 -0
  25. {rlstatsapi-0.1.3 → rlstatsapi-0.1.6}/src/rlstatsapi/py.typed +0 -0
  26. {rlstatsapi-0.1.3 → rlstatsapi-0.1.6}/src/rlstatsapi.egg-info/dependency_links.txt +0 -0
  27. {rlstatsapi-0.1.3 → rlstatsapi-0.1.6}/src/rlstatsapi.egg-info/top_level.txt +0 -0
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 Manu
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.
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Manu
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,150 @@
1
+ Metadata-Version: 2.4
2
+ Name: rlstatsapi
3
+ Version: 0.1.6
4
+ Summary: Rocket League Stats API client
5
+ Author: Manuel Cabral
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/manucabral/RocketLeagueStatsAPI
8
+ Project-URL: Repository, https://github.com/manucabral/RocketLeagueStatsAPI
9
+ Project-URL: Bug Tracker, https://github.com/manucabral/RocketLeagueStatsAPI/issues
10
+ Keywords: rocket-league,stats,socket,asyncio
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3 :: Only
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Topic :: Games/Entertainment
15
+ Classifier: Topic :: Software Development :: Libraries
16
+ Requires-Python: >=3.11
17
+ Description-Content-Type: text/markdown
18
+ License-File: LICENSE
19
+ Provides-Extra: dev
20
+ Requires-Dist: pytest>=8; extra == "dev"
21
+ Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
22
+ Requires-Dist: black; extra == "dev"
23
+ Dynamic: license-file
24
+
25
+ <img width="600" alt="rlstatsapi-logo" src="https://github.com/manucabral/RocketLeagueStatsAPI/blob/main/docs/assets/social-preview.png" />
26
+
27
+ # Rocket League Stats API
28
+ [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) [![PyPI version](https://img.shields.io/pypi/v/rlstatsapi.svg)](https://pypi.org/project/rlstatsapi/) [![Pylint](https://github.com/manucabral/RocketLeagueStatsAPI/actions/workflows/pylint.yml/badge.svg)](https://github.com/manucabral/RocketLeagueStatsAPI/actions/workflows/pylint.yml)
29
+ ![Downloads](https://img.shields.io/pypi/dm/rlstatsapi)
30
+
31
+ `rlstatsapi` is a small Python client for reading live Rocket League Stats API events over a local TCP socket.
32
+
33
+ - Full docs: [manucabral.github.io/RocketLeagueStatsAPI](https://manucabral.github.io/RocketLeagueStatsAPI/)
34
+ - Release history: [CHANGELOG.md](CHANGELOG.md)
35
+
36
+ ## Install
37
+
38
+ From [PyPI](https://pypi.org/project/rlstatsapi/):
39
+
40
+ ```bash
41
+ pip install rlstatsapi
42
+ ```
43
+
44
+ From GitHub:
45
+
46
+ ```bash
47
+ pip install git+https://github.com/manucabral/RocketLeagueStatsAPI.git
48
+ ```
49
+
50
+ ## Rocket League setup
51
+
52
+ Automatic setup from Python:
53
+
54
+ ```python
55
+ from rlstatsapi import configure_stats_api
56
+
57
+ configure_stats_api(enabled=True, port=49123, packet_send_rate=30)
58
+ ```
59
+
60
+ Manual setup:
61
+
62
+ `Documents\My Games\Rocket League\TAGame\Config\TAStatsAPI.ini`
63
+
64
+ ```ini
65
+ [TAGame.MatchStatsExporter_TA]
66
+ Port=49123
67
+ PacketSendRate=30
68
+ ```
69
+
70
+ In both cases, restart Rocket League after changing the config file.
71
+
72
+ Config discovery is currently Windows-first and targets common user config locations.
73
+
74
+ ## Quick start
75
+
76
+ ```python
77
+ import asyncio
78
+ from rlstatsapi import StatsClient
79
+
80
+
81
+ async def main() -> None:
82
+ async with StatsClient() as client:
83
+ client.on_any(lambda msg: print(msg.event, msg.data))
84
+ await asyncio.Event().wait()
85
+
86
+
87
+ asyncio.run(main())
88
+ ```
89
+
90
+ ## Demos
91
+
92
+ ### All events
93
+
94
+ ![All events demo](docs/videos/allevents.gif)
95
+
96
+ ### UI
97
+
98
+ ![UI demo](docs/videos/ui.gif)
99
+
100
+ ## CLI
101
+
102
+ ```bash
103
+ rlstatsapi status
104
+ rlstatsapi enable --port 49123 --rate 30
105
+ rlstatsapi listen --event GoalScored
106
+ ```
107
+
108
+ You can also listen to multiple events:
109
+
110
+ ```bash
111
+ rlstatsapi listen --event GoalScored --event MatchEnded --event MatchCreated
112
+ ```
113
+
114
+ ## Typing
115
+
116
+ For the best editor experience, use `cast_event_data(...)` or `msg.as_type(...)`.
117
+
118
+ ```python
119
+ from rlstatsapi.models import EventMessage
120
+ from rlstatsapi.types import GoalScoredPayload, cast_event_data
121
+
122
+
123
+ def on_goal(msg: EventMessage) -> None:
124
+ data: GoalScoredPayload = cast_event_data("GoalScored", msg.data)
125
+ print(data.get("Scorer", {}).get("Name"))
126
+ ```
127
+
128
+ ## More
129
+
130
+ - Full docs: [manucabral.github.io/RocketLeagueStatsAPI](https://manucabral.github.io/RocketLeagueStatsAPI/)
131
+ - Examples: [examples/](examples)
132
+ - Release history: [CHANGELOG.md](CHANGELOG.md)
133
+
134
+ ## Notes
135
+
136
+ - In current Rocket League builds this endpoint behaves like a TCP JSON stream in practice.
137
+ - Some fields, such as `MatchGuid`, are only present in online/LAN contexts.
138
+
139
+ ## Disclaimer
140
+
141
+ This project is an independent, community-made library and is **not affiliated with, endorsed by, or sponsored by Psyonix or Epic Games**.
142
+
143
+ Rocket League and related trademarks are the property of their respective owners.
144
+ Use this library at your own risk and in compliance with Rocket League’s terms and policies.
145
+
146
+ ## Contributors
147
+
148
+ <a href="https://github.com/manucabral/RocketLeagueStatsAPI/graphs/contributors">
149
+ <img src="https://contrib.rocks/image?repo=manucabral/RocketLeagueStatsAPI" alt="Contributors" />
150
+ </a>
@@ -0,0 +1,126 @@
1
+ <img width="600" alt="rlstatsapi-logo" src="https://github.com/manucabral/RocketLeagueStatsAPI/blob/main/docs/assets/social-preview.png" />
2
+
3
+ # Rocket League Stats API
4
+ [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) [![PyPI version](https://img.shields.io/pypi/v/rlstatsapi.svg)](https://pypi.org/project/rlstatsapi/) [![Pylint](https://github.com/manucabral/RocketLeagueStatsAPI/actions/workflows/pylint.yml/badge.svg)](https://github.com/manucabral/RocketLeagueStatsAPI/actions/workflows/pylint.yml)
5
+ ![Downloads](https://img.shields.io/pypi/dm/rlstatsapi)
6
+
7
+ `rlstatsapi` is a small Python client for reading live Rocket League Stats API events over a local TCP socket.
8
+
9
+ - Full docs: [manucabral.github.io/RocketLeagueStatsAPI](https://manucabral.github.io/RocketLeagueStatsAPI/)
10
+ - Release history: [CHANGELOG.md](CHANGELOG.md)
11
+
12
+ ## Install
13
+
14
+ From [PyPI](https://pypi.org/project/rlstatsapi/):
15
+
16
+ ```bash
17
+ pip install rlstatsapi
18
+ ```
19
+
20
+ From GitHub:
21
+
22
+ ```bash
23
+ pip install git+https://github.com/manucabral/RocketLeagueStatsAPI.git
24
+ ```
25
+
26
+ ## Rocket League setup
27
+
28
+ Automatic setup from Python:
29
+
30
+ ```python
31
+ from rlstatsapi import configure_stats_api
32
+
33
+ configure_stats_api(enabled=True, port=49123, packet_send_rate=30)
34
+ ```
35
+
36
+ Manual setup:
37
+
38
+ `Documents\My Games\Rocket League\TAGame\Config\TAStatsAPI.ini`
39
+
40
+ ```ini
41
+ [TAGame.MatchStatsExporter_TA]
42
+ Port=49123
43
+ PacketSendRate=30
44
+ ```
45
+
46
+ In both cases, restart Rocket League after changing the config file.
47
+
48
+ Config discovery is currently Windows-first and targets common user config locations.
49
+
50
+ ## Quick start
51
+
52
+ ```python
53
+ import asyncio
54
+ from rlstatsapi import StatsClient
55
+
56
+
57
+ async def main() -> None:
58
+ async with StatsClient() as client:
59
+ client.on_any(lambda msg: print(msg.event, msg.data))
60
+ await asyncio.Event().wait()
61
+
62
+
63
+ asyncio.run(main())
64
+ ```
65
+
66
+ ## Demos
67
+
68
+ ### All events
69
+
70
+ ![All events demo](docs/videos/allevents.gif)
71
+
72
+ ### UI
73
+
74
+ ![UI demo](docs/videos/ui.gif)
75
+
76
+ ## CLI
77
+
78
+ ```bash
79
+ rlstatsapi status
80
+ rlstatsapi enable --port 49123 --rate 30
81
+ rlstatsapi listen --event GoalScored
82
+ ```
83
+
84
+ You can also listen to multiple events:
85
+
86
+ ```bash
87
+ rlstatsapi listen --event GoalScored --event MatchEnded --event MatchCreated
88
+ ```
89
+
90
+ ## Typing
91
+
92
+ For the best editor experience, use `cast_event_data(...)` or `msg.as_type(...)`.
93
+
94
+ ```python
95
+ from rlstatsapi.models import EventMessage
96
+ from rlstatsapi.types import GoalScoredPayload, cast_event_data
97
+
98
+
99
+ def on_goal(msg: EventMessage) -> None:
100
+ data: GoalScoredPayload = cast_event_data("GoalScored", msg.data)
101
+ print(data.get("Scorer", {}).get("Name"))
102
+ ```
103
+
104
+ ## More
105
+
106
+ - Full docs: [manucabral.github.io/RocketLeagueStatsAPI](https://manucabral.github.io/RocketLeagueStatsAPI/)
107
+ - Examples: [examples/](examples)
108
+ - Release history: [CHANGELOG.md](CHANGELOG.md)
109
+
110
+ ## Notes
111
+
112
+ - In current Rocket League builds this endpoint behaves like a TCP JSON stream in practice.
113
+ - Some fields, such as `MatchGuid`, are only present in online/LAN contexts.
114
+
115
+ ## Disclaimer
116
+
117
+ This project is an independent, community-made library and is **not affiliated with, endorsed by, or sponsored by Psyonix or Epic Games**.
118
+
119
+ Rocket League and related trademarks are the property of their respective owners.
120
+ Use this library at your own risk and in compliance with Rocket League’s terms and policies.
121
+
122
+ ## Contributors
123
+
124
+ <a href="https://github.com/manucabral/RocketLeagueStatsAPI/graphs/contributors">
125
+ <img src="https://contrib.rocks/image?repo=manucabral/RocketLeagueStatsAPI" alt="Contributors" />
126
+ </a>
@@ -1,42 +1,47 @@
1
- [build-system]
2
- requires = ["setuptools>=69", "wheel"]
3
- build-backend = "setuptools.build_meta"
4
-
5
- [project]
6
- name = "rlstatsapi"
7
- version = "0.1.3"
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
- Repository = "https://github.com/manucabral/RocketLeagueStatsAPI"
26
- "Bug Tracker" = "https://github.com/manucabral/RocketLeagueStatsAPI/issues"
27
-
28
- [project.optional-dependencies]
29
- dev = ["pytest>=8", "pytest-asyncio>=0.23"]
30
-
31
- [tool.pytest.ini_options]
32
- asyncio_mode = "auto"
33
- testpaths = ["tests"]
34
-
35
- [tool.setuptools]
36
- package-dir = {"" = "src"}
37
-
38
- [tool.setuptools.packages.find]
39
- where = ["src"]
40
-
41
- [tool.setuptools.package-data]
42
- rlstatsapi = ["py.typed"]
1
+ [build-system]
2
+ requires = ["setuptools>=69", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "rlstatsapi"
7
+ version = "0.1.6"
8
+ description = "Rocket League Stats API client"
9
+ readme = "README.md"
10
+ requires-python = ">=3.11"
11
+ license = "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
+ "Operating System :: OS Independent",
18
+ "Topic :: Games/Entertainment",
19
+ "Topic :: Software Development :: Libraries",
20
+ ]
21
+
22
+ [project.urls]
23
+ Homepage = "https://github.com/manucabral/RocketLeagueStatsAPI"
24
+ Repository = "https://github.com/manucabral/RocketLeagueStatsAPI"
25
+ "Bug Tracker" = "https://github.com/manucabral/RocketLeagueStatsAPI/issues"
26
+
27
+ [project.scripts]
28
+ rlstatsapi = "rlstatsapi.__main__:main"
29
+
30
+ [project.optional-dependencies]
31
+ dev = ["pytest>=8", "pytest-asyncio>=0.23", "black"]
32
+
33
+ [tool.pytest.ini_options]
34
+ asyncio_mode = "auto"
35
+ testpaths = ["tests"]
36
+ markers = [
37
+ "integration: requires a live Rocket League exporter to be running",
38
+ ]
39
+
40
+ [tool.setuptools]
41
+ package-dir = {"" = "src"}
42
+
43
+ [tool.setuptools.packages.find]
44
+ where = ["src"]
45
+
46
+ [tool.setuptools.package-data]
47
+ rlstatsapi = ["py.typed"]
@@ -1,4 +1,4 @@
1
- [egg_info]
2
- tag_build =
3
- tag_date = 0
4
-
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -1,11 +1,29 @@
1
- """rlstatsapi public API."""
1
+ """
2
+ Public package surface for rlstatsapi.
3
+
4
+ Re-exports the client, envelope model, known event constants, and typed payload
5
+ helpers so consumers can import from `rlstatsapi` directly.
6
+ """
2
7
 
3
8
  from importlib.metadata import PackageNotFoundError
4
9
  from importlib.metadata import version as _pkg_version
5
10
 
6
- from .client import StatsClient
11
+ from .client import ConnectionState, StatsClient
12
+ from .config import (
13
+ DEFAULT_PACKET_SEND_RATE,
14
+ DEFAULT_STATS_API_FILENAME,
15
+ DEFAULT_STATS_API_PORT,
16
+ StatsAPIConfigStatus,
17
+ candidate_stats_api_paths,
18
+ configure_stats_api,
19
+ find_stats_api_config,
20
+ get_stats_api_status,
21
+ set_stats_api_enabled,
22
+ set_stats_api_port,
23
+ )
7
24
  from .events import KNOWN_EVENTS
8
- from .models import EventMessage
25
+ from .models import ClientMetrics, EventMessage, MatchStateSnapshot, PlayerSnapshot
26
+ from .state import MatchStateTracker
9
27
  from .types import (
10
28
  BallHitPayload,
11
29
  ClockUpdatedSecondsPayload,
@@ -40,8 +58,23 @@ except PackageNotFoundError:
40
58
  __all__ = [
41
59
  "__version__",
42
60
  "StatsClient",
61
+ "ConnectionState",
62
+ "StatsAPIConfigStatus",
43
63
  "EventMessage",
64
+ "ClientMetrics",
65
+ "MatchStateSnapshot",
66
+ "PlayerSnapshot",
67
+ "MatchStateTracker",
44
68
  "KNOWN_EVENTS",
69
+ "DEFAULT_PACKET_SEND_RATE",
70
+ "DEFAULT_STATS_API_FILENAME",
71
+ "DEFAULT_STATS_API_PORT",
72
+ "candidate_stats_api_paths",
73
+ "find_stats_api_config",
74
+ "get_stats_api_status",
75
+ "set_stats_api_enabled",
76
+ "set_stats_api_port",
77
+ "configure_stats_api",
45
78
  "EventName",
46
79
  "TypedEventMessage",
47
80
  "KnownEventMessage",
@@ -0,0 +1,148 @@
1
+ """Command-line entrypoint for common rlstatsapi setup and debugging tasks."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import asyncio
7
+ import json
8
+ from dataclasses import asdict
9
+ from pathlib import Path
10
+ from typing import Sequence
11
+
12
+ from .client import StatsClient
13
+ from .config import (
14
+ DEFAULT_PACKET_SEND_RATE,
15
+ DEFAULT_STATS_API_PORT,
16
+ configure_stats_api,
17
+ get_stats_api_status,
18
+ )
19
+
20
+
21
+ def main(argv: Sequence[str] | None = None) -> int:
22
+ """Parse CLI args and dispatch the requested command."""
23
+ parser = _build_parser()
24
+ args = parser.parse_args(argv)
25
+ return args.func(args)
26
+
27
+
28
+ def _build_parser() -> argparse.ArgumentParser:
29
+ """Build and return the top-level argument parser with all subcommands."""
30
+ parser = argparse.ArgumentParser(prog="rlstatsapi")
31
+ subparsers = parser.add_subparsers(dest="command", required=True)
32
+
33
+ status_parser = subparsers.add_parser(
34
+ "status",
35
+ help="Show the current TAStatsAPI.ini status.",
36
+ )
37
+ status_parser.add_argument(
38
+ "--path", type=Path, help="Explicit TAStatsAPI.ini path."
39
+ )
40
+ status_parser.set_defaults(func=_run_status)
41
+
42
+ enable_parser = subparsers.add_parser(
43
+ "enable",
44
+ help="Enable the Rocket League Stats API in TAStatsAPI.ini.",
45
+ )
46
+ enable_parser.add_argument(
47
+ "--path", type=Path, help="Explicit TAStatsAPI.ini path."
48
+ )
49
+ enable_parser.add_argument("--port", type=int, default=DEFAULT_STATS_API_PORT)
50
+ enable_parser.add_argument(
51
+ "--rate",
52
+ type=int,
53
+ default=DEFAULT_PACKET_SEND_RATE,
54
+ help="PacketSendRate to write when enabling the exporter.",
55
+ )
56
+ enable_parser.set_defaults(func=_run_enable)
57
+
58
+ disable_parser = subparsers.add_parser(
59
+ "disable",
60
+ help="Disable the Rocket League Stats API in TAStatsAPI.ini.",
61
+ )
62
+ disable_parser.add_argument(
63
+ "--path", type=Path, help="Explicit TAStatsAPI.ini path."
64
+ )
65
+ disable_parser.add_argument("--port", type=int, default=DEFAULT_STATS_API_PORT)
66
+ disable_parser.set_defaults(func=_run_disable)
67
+
68
+ listen_parser = subparsers.add_parser(
69
+ "listen",
70
+ help="Connect to the local exporter and print live events.",
71
+ )
72
+ listen_parser.add_argument("--host", default="127.0.0.1")
73
+ listen_parser.add_argument("--port", type=int, default=DEFAULT_STATS_API_PORT)
74
+ listen_parser.add_argument(
75
+ "--event",
76
+ action="append",
77
+ default=[],
78
+ help="Filter to one or more event names. Repeat the flag to add more.",
79
+ )
80
+ listen_parser.add_argument(
81
+ "--raw",
82
+ action="store_true",
83
+ help="Include the raw transport JSON when available.",
84
+ )
85
+ listen_parser.set_defaults(func=_run_listen)
86
+
87
+ return parser
88
+
89
+
90
+ def _run_status(args: argparse.Namespace) -> int:
91
+ """Print the current TAStatsAPI.ini config as JSON."""
92
+ status = get_stats_api_status(args.path)
93
+ print(json.dumps(asdict(status), indent=2))
94
+ return 0
95
+
96
+
97
+ def _run_enable(args: argparse.Namespace) -> int:
98
+ """Enable the Stats API exporter in TAStatsAPI.ini and print the result."""
99
+ status = configure_stats_api(
100
+ enabled=True,
101
+ port=args.port,
102
+ packet_send_rate=args.rate,
103
+ path=args.path,
104
+ )
105
+ print(json.dumps(asdict(status), indent=2))
106
+ print("Restart Rocket League after changing the config file.")
107
+ return 0
108
+
109
+
110
+ def _run_disable(args: argparse.Namespace) -> int:
111
+ """Disable the Stats API exporter in TAStatsAPI.ini and print the result."""
112
+ status = configure_stats_api(
113
+ enabled=False,
114
+ port=args.port,
115
+ packet_send_rate=DEFAULT_PACKET_SEND_RATE,
116
+ path=args.path,
117
+ )
118
+ print(json.dumps(asdict(status), indent=2))
119
+ print("Restart Rocket League after changing the config file.")
120
+ return 0
121
+
122
+
123
+ def _run_listen(args: argparse.Namespace) -> int:
124
+ """Bridge the sync CLI entry point into the async ``_listen`` coroutine."""
125
+ return asyncio.run(_listen(args))
126
+
127
+
128
+ async def _listen(args: argparse.Namespace) -> int:
129
+ """Connect to the local Stats API and stream events to stdout until Ctrl+C."""
130
+ async with StatsClient(
131
+ host=args.host,
132
+ port=args.port,
133
+ include_raw=args.raw,
134
+ ) as client:
135
+ print("Listening for Rocket League events. Press Ctrl+C to stop.")
136
+ async for message in client.events(*args.event):
137
+ payload = {
138
+ "event": message.event,
139
+ "data": message.data,
140
+ }
141
+ if args.raw:
142
+ payload["raw"] = message.raw
143
+ print(json.dumps(payload, indent=2, ensure_ascii=False))
144
+ return 0
145
+
146
+
147
+ if __name__ == "__main__":
148
+ raise SystemExit(main())