bsm-api-client 1.0.0__py3-none-any.whl
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.
- bsm_api_client/__init__.py +40 -0
- bsm_api_client/api_client.py +33 -0
- bsm_api_client/client/__init__.py +1 -0
- bsm_api_client/client/_content_methods.py +321 -0
- bsm_api_client/client/_manager_methods.py +146 -0
- bsm_api_client/client/_scheduler_methods.py +252 -0
- bsm_api_client/client/_server_action_methods.py +352 -0
- bsm_api_client/client/_server_info_methods.py +356 -0
- bsm_api_client/client_base.py +471 -0
- bsm_api_client/exceptions.py +102 -0
- bsm_api_client-1.0.0.dist-info/METADATA +127 -0
- bsm_api_client-1.0.0.dist-info/RECORD +15 -0
- bsm_api_client-1.0.0.dist-info/WHEEL +5 -0
- bsm_api_client-1.0.0.dist-info/licenses/LICENSE +21 -0
- bsm_api_client-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# src/bsm_api_client/__init__.py
|
|
2
|
+
"""Python client library for the Bedrock Server Manager API."""
|
|
3
|
+
import logging
|
|
4
|
+
from importlib import metadata
|
|
5
|
+
|
|
6
|
+
from .exceptions import (
|
|
7
|
+
APIError,
|
|
8
|
+
AuthError,
|
|
9
|
+
NotFoundError,
|
|
10
|
+
ServerNotFoundError,
|
|
11
|
+
ServerNotRunningError,
|
|
12
|
+
CannotConnectError,
|
|
13
|
+
InvalidInputError,
|
|
14
|
+
OperationFailedError,
|
|
15
|
+
APIServerSideError,
|
|
16
|
+
)
|
|
17
|
+
from .api_client import BedrockServerManagerApi
|
|
18
|
+
|
|
19
|
+
__all__ = [
|
|
20
|
+
"BedrockServerManagerApi",
|
|
21
|
+
"APIError",
|
|
22
|
+
"AuthError",
|
|
23
|
+
"ServerNotFoundError",
|
|
24
|
+
"ServerNotRunningError",
|
|
25
|
+
"CannotConnectError",
|
|
26
|
+
"InvalidInputError",
|
|
27
|
+
"OperationFailedError",
|
|
28
|
+
"APIServerSideError",
|
|
29
|
+
"__version__",
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
try:
|
|
33
|
+
__version__ = metadata.version(__name__)
|
|
34
|
+
except metadata.PackageNotFoundError:
|
|
35
|
+
__version__ = "0.0.0"
|
|
36
|
+
|
|
37
|
+
# Add a NullHandler to the root logger of the library.
|
|
38
|
+
# This prevents log messages from being output by default if the
|
|
39
|
+
# consuming application/script doesn't configure logging.
|
|
40
|
+
logging.getLogger(__name__).addHandler(logging.NullHandler())
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# src/bsm_api_client/client.py
|
|
2
|
+
"""Main API client class for Bedrock Server Manager.
|
|
3
|
+
Combines the base client logic with specific endpoint method mixins.
|
|
4
|
+
"""
|
|
5
|
+
import logging
|
|
6
|
+
from .client_base import ClientBase
|
|
7
|
+
from .client._manager_methods import ManagerMethodsMixin
|
|
8
|
+
from .client._server_info_methods import ServerInfoMethodsMixin
|
|
9
|
+
from .client._server_action_methods import ServerActionMethodsMixin
|
|
10
|
+
from .client._content_methods import ContentMethodsMixin
|
|
11
|
+
from .client._scheduler_methods import SchedulerMethodsMixin
|
|
12
|
+
|
|
13
|
+
_LOGGER = logging.getLogger(__name__.split(".")[0] + ".client")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class BedrockServerManagerApi(
|
|
17
|
+
ClientBase,
|
|
18
|
+
ManagerMethodsMixin,
|
|
19
|
+
ServerInfoMethodsMixin,
|
|
20
|
+
ServerActionMethodsMixin,
|
|
21
|
+
ContentMethodsMixin,
|
|
22
|
+
SchedulerMethodsMixin,
|
|
23
|
+
):
|
|
24
|
+
"""
|
|
25
|
+
API Client for the Bedrock Server Manager.
|
|
26
|
+
|
|
27
|
+
This class combines the base connection/authentication logic with
|
|
28
|
+
methods for interacting with various API endpoints, organized via mixins.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
# __init__ is inherited from ClientBase.
|
|
32
|
+
# All async API methods are inherited from mixins.
|
|
33
|
+
pass
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# src/bsm_api_client/client/__init__.py
|
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
# src/bsm_api_client/client/_content_methods.py
|
|
2
|
+
"""Mixin class containing content management methods (backups, worlds, addons)."""
|
|
3
|
+
import logging
|
|
4
|
+
from typing import Any, Dict, Optional, List, TYPE_CHECKING
|
|
5
|
+
|
|
6
|
+
if TYPE_CHECKING:
|
|
7
|
+
from ..client_base import ClientBase
|
|
8
|
+
|
|
9
|
+
_LOGGER = logging.getLogger(__name__.split(".")[0] + ".client.content")
|
|
10
|
+
|
|
11
|
+
# Define allowed types for validation to avoid magic strings
|
|
12
|
+
ALLOWED_BACKUP_LIST_TYPES = ["world", "properties", "allowlist", "permissions"]
|
|
13
|
+
ALLOWED_BACKUP_ACTION_TYPES = ["world", "config", "all"]
|
|
14
|
+
ALLOWED_RESTORE_TYPES = ["world", "config"]
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class ContentMethodsMixin:
|
|
18
|
+
"""Mixin for content management endpoints (backups, worlds, addons)."""
|
|
19
|
+
|
|
20
|
+
_request: callable
|
|
21
|
+
if TYPE_CHECKING:
|
|
22
|
+
|
|
23
|
+
async def _request(
|
|
24
|
+
self: "ClientBase",
|
|
25
|
+
method: str,
|
|
26
|
+
path: str,
|
|
27
|
+
json_data: Optional[Dict[str, Any]] = None,
|
|
28
|
+
params: Optional[Dict[str, Any]] = None,
|
|
29
|
+
authenticated: bool = True,
|
|
30
|
+
is_retry: bool = False,
|
|
31
|
+
) -> Any: ...
|
|
32
|
+
|
|
33
|
+
async def async_list_server_backups(
|
|
34
|
+
self, server_name: str, backup_type: str
|
|
35
|
+
) -> Dict[str, Any]:
|
|
36
|
+
"""
|
|
37
|
+
Lists backup filenames for a specific server and backup type.
|
|
38
|
+
|
|
39
|
+
Corresponds to `GET /api/server/{server_name}/backups/list/{backup_type}`.
|
|
40
|
+
Requires authentication.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
server_name: The name of the server.
|
|
44
|
+
backup_type: The type of backups to list (e.g., "world", "properties", "allowlist", "permissions", "all").
|
|
45
|
+
"""
|
|
46
|
+
bt_lower = backup_type.lower()
|
|
47
|
+
if bt_lower not in ALLOWED_BACKUP_LIST_TYPES:
|
|
48
|
+
_LOGGER.error(
|
|
49
|
+
"Invalid backup_type '%s' for listing backups. Allowed: %s",
|
|
50
|
+
backup_type,
|
|
51
|
+
ALLOWED_BACKUP_LIST_TYPES,
|
|
52
|
+
)
|
|
53
|
+
raise ValueError(
|
|
54
|
+
f"Invalid backup_type '{backup_type}' provided. Allowed types are: {', '.join(ALLOWED_BACKUP_LIST_TYPES)}"
|
|
55
|
+
)
|
|
56
|
+
_LOGGER.debug(
|
|
57
|
+
"Fetching '%s' backups list for server '%s'", bt_lower, server_name
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
return await self._request(
|
|
61
|
+
"GET",
|
|
62
|
+
f"/server/{server_name}/backups/list/{bt_lower}",
|
|
63
|
+
authenticated=True,
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
async def async_get_content_worlds(self) -> Dict[str, Any]:
|
|
67
|
+
"""
|
|
68
|
+
Lists available world template files (.mcworld) from the manager's content directory.
|
|
69
|
+
|
|
70
|
+
Corresponds to `GET /api/content/worlds`.
|
|
71
|
+
Requires authentication.
|
|
72
|
+
"""
|
|
73
|
+
_LOGGER.debug("Fetching available world files from /content/worlds")
|
|
74
|
+
return await self._request("GET", "/content/worlds", authenticated=True)
|
|
75
|
+
|
|
76
|
+
async def async_get_content_addons(self) -> Dict[str, Any]:
|
|
77
|
+
"""
|
|
78
|
+
Lists available addon files (.mcpack, .mcaddon) from the manager's content directory.
|
|
79
|
+
|
|
80
|
+
Corresponds to `GET /api/content/addons`.
|
|
81
|
+
Requires authentication.
|
|
82
|
+
"""
|
|
83
|
+
_LOGGER.debug("Fetching available addon files from /content/addons")
|
|
84
|
+
return await self._request("GET", "/content/addons", authenticated=True)
|
|
85
|
+
|
|
86
|
+
async def async_trigger_server_backup(
|
|
87
|
+
self,
|
|
88
|
+
server_name: str,
|
|
89
|
+
backup_type: str = "all",
|
|
90
|
+
file_to_backup: Optional[str] = None,
|
|
91
|
+
) -> Dict[str, Any]:
|
|
92
|
+
"""
|
|
93
|
+
Triggers a backup operation for a specific server.
|
|
94
|
+
|
|
95
|
+
Corresponds to `POST /api/server/{server_name}/backup/action`.
|
|
96
|
+
Requires authentication.
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
server_name: The name of the server to back up.
|
|
100
|
+
backup_type: Type of backup ("world", "config", "all"). Defaults to "all".
|
|
101
|
+
file_to_backup: Required if backup_type is "config". Specifies the config file.
|
|
102
|
+
"""
|
|
103
|
+
bt_lower = backup_type.lower()
|
|
104
|
+
if bt_lower not in ALLOWED_BACKUP_ACTION_TYPES:
|
|
105
|
+
_LOGGER.error(
|
|
106
|
+
"Invalid backup_type '%s' for triggering backup. Allowed: %s",
|
|
107
|
+
backup_type,
|
|
108
|
+
ALLOWED_BACKUP_ACTION_TYPES,
|
|
109
|
+
)
|
|
110
|
+
raise ValueError(
|
|
111
|
+
f"Invalid backup_type '{backup_type}' provided. Allowed types are: {', '.join(ALLOWED_BACKUP_ACTION_TYPES)}"
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
_LOGGER.info(
|
|
115
|
+
"Triggering backup for server '%s', type: %s, file: %s",
|
|
116
|
+
server_name,
|
|
117
|
+
bt_lower,
|
|
118
|
+
file_to_backup or "N/A",
|
|
119
|
+
)
|
|
120
|
+
payload: Dict[str, str] = {"backup_type": bt_lower}
|
|
121
|
+
if bt_lower == "config":
|
|
122
|
+
if not file_to_backup:
|
|
123
|
+
raise ValueError(
|
|
124
|
+
"file_to_backup is required when backup_type is 'config'"
|
|
125
|
+
)
|
|
126
|
+
payload["file_to_backup"] = file_to_backup
|
|
127
|
+
elif file_to_backup:
|
|
128
|
+
_LOGGER.warning(
|
|
129
|
+
"file_to_backup ('%s') provided but will be ignored for backup_type '%s'",
|
|
130
|
+
file_to_backup,
|
|
131
|
+
bt_lower,
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
return await self._request(
|
|
135
|
+
"POST",
|
|
136
|
+
f"/server/{server_name}/backup/action",
|
|
137
|
+
json_data=payload,
|
|
138
|
+
authenticated=True,
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
async def async_export_server_world(self, server_name: str) -> Dict[str, Any]:
|
|
142
|
+
"""
|
|
143
|
+
Exports the current world of a server to a .mcworld file in the content directory.
|
|
144
|
+
|
|
145
|
+
Corresponds to `POST /api/server/{server_name}/world/export`.
|
|
146
|
+
Requires authentication.
|
|
147
|
+
|
|
148
|
+
Args:
|
|
149
|
+
server_name: The name of the server whose world to export.
|
|
150
|
+
"""
|
|
151
|
+
_LOGGER.info("Triggering world export for server '%s'", server_name)
|
|
152
|
+
return await self._request(
|
|
153
|
+
"POST",
|
|
154
|
+
f"/server/{server_name}/world/export",
|
|
155
|
+
json_data=None,
|
|
156
|
+
authenticated=True,
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
async def async_reset_server_world(self, server_name: str) -> Dict[str, Any]:
|
|
160
|
+
"""
|
|
161
|
+
Resets the current world of a server.
|
|
162
|
+
|
|
163
|
+
Corresponds to `DELETE /api/server/{server_name}/world/reset`.
|
|
164
|
+
Requires authentication.
|
|
165
|
+
|
|
166
|
+
Args:
|
|
167
|
+
server_name: The name of the server whose world to export.
|
|
168
|
+
"""
|
|
169
|
+
_LOGGER.warning("Triggering world reset for server '%s'", server_name)
|
|
170
|
+
return await self._request(
|
|
171
|
+
"DELETE",
|
|
172
|
+
f"/server/{server_name}/world/reset",
|
|
173
|
+
json_data=None,
|
|
174
|
+
authenticated=True,
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
async def async_prune_server_backups(
|
|
178
|
+
self, server_name: str, keep: Optional[int] = None
|
|
179
|
+
) -> Dict[str, Any]:
|
|
180
|
+
"""
|
|
181
|
+
Prunes older backups for a specific server.
|
|
182
|
+
|
|
183
|
+
Corresponds to `POST /api/server/{server_name}/backups/prune`.
|
|
184
|
+
Requires authentication.
|
|
185
|
+
|
|
186
|
+
Args:
|
|
187
|
+
server_name: The name of the server whose backups to prune.
|
|
188
|
+
keep: The number of recent backups of each type to retain.
|
|
189
|
+
If None, uses the manager's default setting.
|
|
190
|
+
"""
|
|
191
|
+
_LOGGER.info(
|
|
192
|
+
"Triggering backup pruning for server '%s', keep: %s",
|
|
193
|
+
server_name,
|
|
194
|
+
keep if keep is not None else "manager default",
|
|
195
|
+
)
|
|
196
|
+
payload: Optional[Dict[str, Any]] = None
|
|
197
|
+
if keep is not None:
|
|
198
|
+
if not isinstance(keep, int) or keep < 0:
|
|
199
|
+
raise ValueError("keep must be a non-negative integer if provided.")
|
|
200
|
+
payload = {"keep": keep}
|
|
201
|
+
|
|
202
|
+
return await self._request(
|
|
203
|
+
"POST",
|
|
204
|
+
f"/server/{server_name}/backups/prune",
|
|
205
|
+
json_data=payload,
|
|
206
|
+
authenticated=True,
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
async def async_restore_server_backup(
|
|
210
|
+
self, server_name: str, restore_type: str, backup_file: str
|
|
211
|
+
) -> Dict[str, Any]:
|
|
212
|
+
"""
|
|
213
|
+
Restores a server's world or a specific configuration file from a backup.
|
|
214
|
+
|
|
215
|
+
Corresponds to `POST /api/server/{server_name}/restore/action`.
|
|
216
|
+
Requires authentication.
|
|
217
|
+
|
|
218
|
+
Args:
|
|
219
|
+
server_name: The name of the server.
|
|
220
|
+
restore_type: Type of restore ("world" or "config").
|
|
221
|
+
backup_file: The filename of the backup to restore (relative to server's backup dir).
|
|
222
|
+
"""
|
|
223
|
+
rt_lower = restore_type.lower()
|
|
224
|
+
if rt_lower not in ALLOWED_RESTORE_TYPES:
|
|
225
|
+
_LOGGER.error(
|
|
226
|
+
"Invalid restore_type '%s'. Allowed: %s",
|
|
227
|
+
restore_type,
|
|
228
|
+
ALLOWED_RESTORE_TYPES,
|
|
229
|
+
)
|
|
230
|
+
raise ValueError(
|
|
231
|
+
f"Invalid restore_type '{restore_type}' provided. Allowed types are: {', '.join(ALLOWED_RESTORE_TYPES)}"
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
_LOGGER.info(
|
|
235
|
+
"Requesting restore for server '%s', type: %s, file: '%s'",
|
|
236
|
+
server_name,
|
|
237
|
+
rt_lower,
|
|
238
|
+
backup_file,
|
|
239
|
+
)
|
|
240
|
+
payload = {"restore_type": rt_lower, "backup_file": backup_file}
|
|
241
|
+
|
|
242
|
+
return await self._request(
|
|
243
|
+
"POST",
|
|
244
|
+
f"/server/{server_name}/restore/action",
|
|
245
|
+
json_data=payload,
|
|
246
|
+
authenticated=True,
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
async def async_restore_server_latest_all(self, server_name: str) -> Dict[str, Any]:
|
|
250
|
+
"""
|
|
251
|
+
Restores the server's world AND standard configuration files from their latest backups.
|
|
252
|
+
|
|
253
|
+
Corresponds to `POST /api/server/{server_name}/restore/all`.
|
|
254
|
+
Requires authentication.
|
|
255
|
+
|
|
256
|
+
Args:
|
|
257
|
+
server_name: The name of the server to restore.
|
|
258
|
+
"""
|
|
259
|
+
_LOGGER.info(
|
|
260
|
+
"Requesting restore of latest 'all' backup for server '%s'", server_name
|
|
261
|
+
)
|
|
262
|
+
return await self._request(
|
|
263
|
+
"POST",
|
|
264
|
+
f"/server/{server_name}/restore/all",
|
|
265
|
+
json_data=None,
|
|
266
|
+
authenticated=True,
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
async def async_install_server_world(
|
|
270
|
+
self, server_name: str, filename: str
|
|
271
|
+
) -> Dict[str, Any]:
|
|
272
|
+
"""
|
|
273
|
+
Installs a world from a .mcworld file (from content directory) to a server.
|
|
274
|
+
|
|
275
|
+
Corresponds to `POST /api/server/{server_name}/world/install`.
|
|
276
|
+
Requires authentication.
|
|
277
|
+
|
|
278
|
+
Args:
|
|
279
|
+
server_name: The name of the server.
|
|
280
|
+
filename: The name of the .mcworld file (relative to content/worlds dir).
|
|
281
|
+
"""
|
|
282
|
+
_LOGGER.info(
|
|
283
|
+
"Requesting world install for server '%s' from file '%s'",
|
|
284
|
+
server_name,
|
|
285
|
+
filename,
|
|
286
|
+
)
|
|
287
|
+
payload = {"filename": filename}
|
|
288
|
+
|
|
289
|
+
return await self._request(
|
|
290
|
+
"POST",
|
|
291
|
+
f"/server/{server_name}/world/install",
|
|
292
|
+
json_data=payload,
|
|
293
|
+
authenticated=True,
|
|
294
|
+
)
|
|
295
|
+
|
|
296
|
+
async def async_install_server_addon(
|
|
297
|
+
self, server_name: str, filename: str
|
|
298
|
+
) -> Dict[str, Any]:
|
|
299
|
+
"""
|
|
300
|
+
Installs an addon (.mcaddon or .mcpack file from content directory) to a server.
|
|
301
|
+
|
|
302
|
+
Corresponds to `POST /api/server/{server_name}/addon/install`.
|
|
303
|
+
Requires authentication.
|
|
304
|
+
|
|
305
|
+
Args:
|
|
306
|
+
server_name: The name of the server.
|
|
307
|
+
filename: The name of the addon file (relative to content/addons dir).
|
|
308
|
+
"""
|
|
309
|
+
_LOGGER.info(
|
|
310
|
+
"Requesting addon install for server '%s' from file '%s'",
|
|
311
|
+
server_name,
|
|
312
|
+
filename,
|
|
313
|
+
)
|
|
314
|
+
payload = {"filename": filename}
|
|
315
|
+
|
|
316
|
+
return await self._request(
|
|
317
|
+
"POST",
|
|
318
|
+
f"/server/{server_name}/addon/install",
|
|
319
|
+
json_data=payload,
|
|
320
|
+
authenticated=True,
|
|
321
|
+
)
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# src/bsm_api_client/client/_manager_methods.py
|
|
2
|
+
"""Mixin class containing manager-level API methods."""
|
|
3
|
+
import logging
|
|
4
|
+
from typing import Any, Dict, Optional, List, TYPE_CHECKING
|
|
5
|
+
|
|
6
|
+
if TYPE_CHECKING:
|
|
7
|
+
from ..client_base import ClientBase
|
|
8
|
+
|
|
9
|
+
_LOGGER = logging.getLogger(__name__.split(".")[0] + ".client.manager")
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ManagerMethodsMixin:
|
|
13
|
+
"""Mixin for manager-level endpoints."""
|
|
14
|
+
|
|
15
|
+
_request: callable
|
|
16
|
+
if TYPE_CHECKING:
|
|
17
|
+
|
|
18
|
+
async def _request(
|
|
19
|
+
self: "ClientBase",
|
|
20
|
+
method: str,
|
|
21
|
+
path: str,
|
|
22
|
+
json_data: Optional[Dict[str, Any]] = None,
|
|
23
|
+
params: Optional[Dict[str, Any]] = None,
|
|
24
|
+
authenticated: bool = True,
|
|
25
|
+
is_retry: bool = False,
|
|
26
|
+
) -> Any: ...
|
|
27
|
+
|
|
28
|
+
async def async_get_info(self) -> Dict[str, Any]:
|
|
29
|
+
"""
|
|
30
|
+
Gets system and application information from the manager.
|
|
31
|
+
|
|
32
|
+
Corresponds to `GET /api/info`.
|
|
33
|
+
Requires no authentication.
|
|
34
|
+
"""
|
|
35
|
+
_LOGGER.debug("Fetching manager system and application information from /info")
|
|
36
|
+
return await self._request(method="GET", path="/info", authenticated=False)
|
|
37
|
+
|
|
38
|
+
async def async_scan_players(self) -> Dict[str, Any]:
|
|
39
|
+
"""
|
|
40
|
+
Triggers scanning of player logs across all servers.
|
|
41
|
+
|
|
42
|
+
Corresponds to `POST /api/players/scan`.
|
|
43
|
+
Requires authentication.
|
|
44
|
+
"""
|
|
45
|
+
_LOGGER.info("Triggering player log scan")
|
|
46
|
+
return await self._request(
|
|
47
|
+
method="POST", path="/players/scan", authenticated=True
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
async def async_get_players(self) -> Dict[str, Any]:
|
|
51
|
+
"""
|
|
52
|
+
Gets the global list of known players (name and XUID).
|
|
53
|
+
|
|
54
|
+
Corresponds to `GET /api/players/get`.
|
|
55
|
+
Requires authentication.
|
|
56
|
+
"""
|
|
57
|
+
_LOGGER.debug("Fetching global player list from /players/get")
|
|
58
|
+
return await self._request(
|
|
59
|
+
method="GET", path="/players/get", authenticated=True
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
async def async_add_players(self, players_data: List[str]) -> Dict[str, Any]:
|
|
63
|
+
"""
|
|
64
|
+
Adds or updates players in the global list.
|
|
65
|
+
Each string in `players_data` should be in "PlayerName:PlayerXUID" format.
|
|
66
|
+
|
|
67
|
+
Corresponds to `POST /api/players/add`.
|
|
68
|
+
Requires authentication.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
players_data: A list of player strings to add or update.
|
|
72
|
+
Example: ["Steve:2535460987654321", "Alex:2535461234567890"]
|
|
73
|
+
"""
|
|
74
|
+
_LOGGER.info("Adding/updating global players: %s", players_data)
|
|
75
|
+
payload = {"players": players_data}
|
|
76
|
+
return await self._request(
|
|
77
|
+
method="POST",
|
|
78
|
+
path="/players/add",
|
|
79
|
+
json_data=payload,
|
|
80
|
+
authenticated=True,
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
async def async_prune_downloads(
|
|
84
|
+
self, directory: str, keep: Optional[int] = None
|
|
85
|
+
) -> Dict[str, Any]:
|
|
86
|
+
"""
|
|
87
|
+
Triggers pruning of downloaded server archives in a specified directory.
|
|
88
|
+
|
|
89
|
+
Corresponds to `POST /api/downloads/prune`.
|
|
90
|
+
Requires authentication.
|
|
91
|
+
|
|
92
|
+
Args:
|
|
93
|
+
directory: The absolute path to the directory to prune.
|
|
94
|
+
keep: The number of newest files to retain. If None, uses server default.
|
|
95
|
+
"""
|
|
96
|
+
_LOGGER.info(
|
|
97
|
+
"Triggering download cache prune for directory '%s', keep: %s",
|
|
98
|
+
directory,
|
|
99
|
+
keep if keep is not None else "server default",
|
|
100
|
+
)
|
|
101
|
+
payload: Dict[str, Any] = {"directory": directory}
|
|
102
|
+
if keep is not None:
|
|
103
|
+
payload["keep"] = keep
|
|
104
|
+
|
|
105
|
+
return await self._request(
|
|
106
|
+
method="POST",
|
|
107
|
+
path="/downloads/prune",
|
|
108
|
+
json_data=payload,
|
|
109
|
+
authenticated=True,
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
async def async_install_new_server(
|
|
113
|
+
self, server_name: str, server_version: str, overwrite: bool = False
|
|
114
|
+
) -> Dict[str, Any]:
|
|
115
|
+
"""
|
|
116
|
+
Requests installation of a new Bedrock server instance.
|
|
117
|
+
The response may indicate success or that confirmation is needed if overwrite is false
|
|
118
|
+
and the server already exists.
|
|
119
|
+
|
|
120
|
+
Corresponds to `POST /api/server/install`.
|
|
121
|
+
Requires authentication.
|
|
122
|
+
|
|
123
|
+
Args:
|
|
124
|
+
server_name: The desired unique name for the new server.
|
|
125
|
+
server_version: The version to install (e.g., "LATEST", "PREVIEW", "1.20.81.01").
|
|
126
|
+
overwrite: If True, will delete existing server data if a server with the
|
|
127
|
+
same name already exists. Defaults to False.
|
|
128
|
+
"""
|
|
129
|
+
_LOGGER.info(
|
|
130
|
+
"Requesting installation for server '%s', version: '%s', overwrite: %s",
|
|
131
|
+
server_name,
|
|
132
|
+
server_version,
|
|
133
|
+
overwrite,
|
|
134
|
+
)
|
|
135
|
+
payload = {
|
|
136
|
+
"server_name": server_name,
|
|
137
|
+
"server_version": server_version,
|
|
138
|
+
"overwrite": overwrite,
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return await self._request(
|
|
142
|
+
method="POST",
|
|
143
|
+
path="/server/install",
|
|
144
|
+
json_data=payload,
|
|
145
|
+
authenticated=True,
|
|
146
|
+
)
|