bsm-cli 1.6.0b3__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_cli/__init__.py +0 -0
- bsm_cli/__main__.py +88 -0
- bsm_cli/account.py +75 -0
- bsm_cli/addon.py +362 -0
- bsm_cli/allowlist.py +229 -0
- bsm_cli/auth.py +106 -0
- bsm_cli/backup.py +263 -0
- bsm_cli/bans.py +190 -0
- bsm_cli/config.py +89 -0
- bsm_cli/content.py +21 -0
- bsm_cli/decorators.py +138 -0
- bsm_cli/main_menus.py +293 -0
- bsm_cli/permissions.py +191 -0
- bsm_cli/player.py +59 -0
- bsm_cli/plugins.py +296 -0
- bsm_cli/properties.py +197 -0
- bsm_cli/server.py +471 -0
- bsm_cli/system.py +142 -0
- bsm_cli/users.py +245 -0
- bsm_cli/world.py +171 -0
- bsm_cli-1.6.0b3.dist-info/METADATA +77 -0
- bsm_cli-1.6.0b3.dist-info/RECORD +26 -0
- bsm_cli-1.6.0b3.dist-info/WHEEL +5 -0
- bsm_cli-1.6.0b3.dist-info/entry_points.txt +2 -0
- bsm_cli-1.6.0b3.dist-info/licenses/LICENSE +21 -0
- bsm_cli-1.6.0b3.dist-info/top_level.txt +1 -0
bsm_cli/plugins.py
ADDED
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
import json
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
import questionary
|
|
5
|
+
|
|
6
|
+
from bsm_api_client.models import PluginStatusSetPayload, TriggerEventPayload
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def _print_plugin_table(plugins):
|
|
10
|
+
"""
|
|
11
|
+
Internal helper to print a formatted table of plugins, their statuses, and versions.
|
|
12
|
+
"""
|
|
13
|
+
if not plugins:
|
|
14
|
+
click.secho("No plugins found or configured.", fg="yellow")
|
|
15
|
+
return
|
|
16
|
+
|
|
17
|
+
click.secho("BSM API Client - Plugin Statuses & Versions", fg="magenta", bold=True)
|
|
18
|
+
|
|
19
|
+
plugin_names = list(plugins.keys())
|
|
20
|
+
versions = [config.get("version", "N/A") for config in plugins.values()]
|
|
21
|
+
|
|
22
|
+
max_name_len = max(len(name) for name in plugin_names) if plugin_names else 20
|
|
23
|
+
max_version_len = max(
|
|
24
|
+
(max(len(v) for v in versions) if versions else 0), len("Version")
|
|
25
|
+
)
|
|
26
|
+
max_status_len = len("Disabled")
|
|
27
|
+
|
|
28
|
+
header = f"{'Plugin Name':<{max_name_len}} | {'Status':<{max_status_len}} | {'Version':<{max_version_len}}"
|
|
29
|
+
click.secho(header, underline=True)
|
|
30
|
+
click.secho("-" * len(header))
|
|
31
|
+
|
|
32
|
+
for name, config in sorted(plugins.items()):
|
|
33
|
+
is_enabled = config.get("enabled", False)
|
|
34
|
+
version = config.get("version", "N/A")
|
|
35
|
+
|
|
36
|
+
status_str = "Enabled" if is_enabled else "Disabled"
|
|
37
|
+
status_color = "green" if is_enabled else "red"
|
|
38
|
+
|
|
39
|
+
click.echo(f"{name:<{max_name_len}} | ", nl=False)
|
|
40
|
+
click.secho(f"{status_str:<{max_status_len}}", fg=status_color, nl=False)
|
|
41
|
+
click.echo(f" | {version:<{max_version_len}}")
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
async def interactive_plugin_workflow(client): # noqa: C901
|
|
45
|
+
"""Guides the user through an interactive session to enable or disable plugins."""
|
|
46
|
+
try:
|
|
47
|
+
response = await client.async_get_plugin_statuses()
|
|
48
|
+
if response.status != "success":
|
|
49
|
+
click.secho(
|
|
50
|
+
f"Failed to retrieve plugin statuses: {response.message}", fg="red"
|
|
51
|
+
)
|
|
52
|
+
return
|
|
53
|
+
|
|
54
|
+
plugins = response.plugins
|
|
55
|
+
if not plugins:
|
|
56
|
+
click.secho("No plugins found or configured to edit.", fg="yellow")
|
|
57
|
+
return
|
|
58
|
+
|
|
59
|
+
_print_plugin_table(plugins)
|
|
60
|
+
click.echo()
|
|
61
|
+
|
|
62
|
+
while True:
|
|
63
|
+
click.clear()
|
|
64
|
+
click.secho("--- Manage Plugins ---", fg="magenta", bold=True)
|
|
65
|
+
|
|
66
|
+
response = await client.async_get_plugin_statuses()
|
|
67
|
+
if response.status != "success":
|
|
68
|
+
click.secho(
|
|
69
|
+
f"Failed to retrieve plugin statuses: {response.message}", fg="red"
|
|
70
|
+
)
|
|
71
|
+
return
|
|
72
|
+
|
|
73
|
+
plugins = response.plugins
|
|
74
|
+
if not plugins:
|
|
75
|
+
click.secho("No plugins found or configured to edit.", fg="yellow")
|
|
76
|
+
return
|
|
77
|
+
|
|
78
|
+
menu_choices = []
|
|
79
|
+
for name, config_dict in sorted(plugins.items()):
|
|
80
|
+
is_enabled = config_dict.get("enabled", False)
|
|
81
|
+
version = config_dict.get("version", "N/A")
|
|
82
|
+
status = "🟢" if is_enabled else "⚪"
|
|
83
|
+
menu_choices.append(
|
|
84
|
+
questionary.Choice(
|
|
85
|
+
title=f"{status} {name} (v{version})", value=name
|
|
86
|
+
)
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
menu_choices.extend(
|
|
90
|
+
[
|
|
91
|
+
questionary.Separator("--- Actions ---"),
|
|
92
|
+
questionary.Choice(title="Reload All Plugins", value="RELOAD"),
|
|
93
|
+
questionary.Choice(title="Back", value="BACK"),
|
|
94
|
+
]
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
choice = await questionary.select(
|
|
98
|
+
"Select a plugin to manage or an action:", choices=menu_choices
|
|
99
|
+
).ask_async()
|
|
100
|
+
|
|
101
|
+
if not choice or choice == "BACK":
|
|
102
|
+
return
|
|
103
|
+
|
|
104
|
+
if choice == "RELOAD":
|
|
105
|
+
click.secho("Reloading plugins...", fg="cyan")
|
|
106
|
+
try:
|
|
107
|
+
reload_response = await client.async_reload_plugins()
|
|
108
|
+
if reload_response.status == "success":
|
|
109
|
+
click.secho(reload_response.message, fg="green")
|
|
110
|
+
else:
|
|
111
|
+
click.secho(
|
|
112
|
+
f"Failed to reload plugins: {reload_response.message}",
|
|
113
|
+
fg="red",
|
|
114
|
+
)
|
|
115
|
+
except Exception as e_reload:
|
|
116
|
+
click.secho(f"Error reloading plugins: {e_reload}", fg="red")
|
|
117
|
+
click.pause()
|
|
118
|
+
continue
|
|
119
|
+
|
|
120
|
+
# Handle specific plugin
|
|
121
|
+
plugin_name = choice
|
|
122
|
+
config_dict = plugins.get(plugin_name)
|
|
123
|
+
|
|
124
|
+
if not config_dict:
|
|
125
|
+
continue
|
|
126
|
+
|
|
127
|
+
is_enabled = config_dict.get("enabled", False)
|
|
128
|
+
pack_menu = []
|
|
129
|
+
if is_enabled:
|
|
130
|
+
pack_menu.append("Disable")
|
|
131
|
+
else:
|
|
132
|
+
pack_menu.append("Enable")
|
|
133
|
+
|
|
134
|
+
pack_menu.append("Back")
|
|
135
|
+
|
|
136
|
+
action_choice = await questionary.select(
|
|
137
|
+
f"Actions for {plugin_name}:", choices=pack_menu
|
|
138
|
+
).ask_async()
|
|
139
|
+
|
|
140
|
+
if not action_choice or action_choice == "Back":
|
|
141
|
+
continue
|
|
142
|
+
|
|
143
|
+
if action_choice == "Enable":
|
|
144
|
+
payload = PluginStatusSetPayload(enabled=True)
|
|
145
|
+
res = await client.async_set_plugin_status(plugin_name, payload)
|
|
146
|
+
if res.status == "success":
|
|
147
|
+
click.secho(
|
|
148
|
+
f"Plugin '{plugin_name}' enabled successfully.", fg="green"
|
|
149
|
+
)
|
|
150
|
+
else:
|
|
151
|
+
click.secho(
|
|
152
|
+
f"Failed to enable plugin '{plugin_name}': {res.message}",
|
|
153
|
+
fg="red",
|
|
154
|
+
)
|
|
155
|
+
elif action_choice == "Disable":
|
|
156
|
+
payload = PluginStatusSetPayload(enabled=False)
|
|
157
|
+
res = await client.async_set_plugin_status(plugin_name, payload)
|
|
158
|
+
if res.status == "success":
|
|
159
|
+
click.secho(
|
|
160
|
+
f"Plugin '{plugin_name}' disabled successfully.", fg="green"
|
|
161
|
+
)
|
|
162
|
+
else:
|
|
163
|
+
click.secho(
|
|
164
|
+
f"Failed to disable plugin '{plugin_name}': {res.message}",
|
|
165
|
+
fg="red",
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
click.pause()
|
|
169
|
+
|
|
170
|
+
except Exception as e:
|
|
171
|
+
click.secho(f"An error occurred during plugin configuration: {e}", fg="red")
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
@click.group(invoke_without_command=True)
|
|
175
|
+
@click.pass_context
|
|
176
|
+
async def plugin(ctx):
|
|
177
|
+
"""Manages plugins."""
|
|
178
|
+
if ctx.invoked_subcommand is None:
|
|
179
|
+
client = ctx.obj.get("client")
|
|
180
|
+
if not client:
|
|
181
|
+
click.secho("You are not logged in.", fg="red")
|
|
182
|
+
return
|
|
183
|
+
await interactive_plugin_workflow(client)
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
@plugin.command("list")
|
|
187
|
+
@click.pass_context
|
|
188
|
+
async def list_plugins(ctx):
|
|
189
|
+
"""Lists all discoverable plugins."""
|
|
190
|
+
client = ctx.obj.get("client")
|
|
191
|
+
if not client:
|
|
192
|
+
click.secho("You are not logged in.", fg="red")
|
|
193
|
+
return
|
|
194
|
+
|
|
195
|
+
try:
|
|
196
|
+
response = await client.async_get_plugin_statuses()
|
|
197
|
+
if response.status == "success":
|
|
198
|
+
plugins = response.plugins
|
|
199
|
+
if not plugins:
|
|
200
|
+
click.secho("No plugins found.", fg="yellow")
|
|
201
|
+
return
|
|
202
|
+
|
|
203
|
+
_print_plugin_table(plugins)
|
|
204
|
+
else:
|
|
205
|
+
click.secho(f"Failed to list plugins: {response.message}", fg="red")
|
|
206
|
+
except Exception as e:
|
|
207
|
+
click.secho(f"An error occurred: {e}", fg="red")
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
@plugin.command("enable")
|
|
211
|
+
@click.argument("plugin_name")
|
|
212
|
+
@click.pass_context
|
|
213
|
+
async def enable_plugin(ctx, plugin_name: str):
|
|
214
|
+
"""Enables a plugin."""
|
|
215
|
+
client = ctx.obj.get("client")
|
|
216
|
+
if not client:
|
|
217
|
+
click.secho("You are not logged in.", fg="red")
|
|
218
|
+
return
|
|
219
|
+
|
|
220
|
+
try:
|
|
221
|
+
payload = PluginStatusSetPayload(enabled=True)
|
|
222
|
+
response = await client.async_set_plugin_status(plugin_name, payload)
|
|
223
|
+
if response.status == "success":
|
|
224
|
+
click.secho(f"Plugin '{plugin_name}' enabled successfully.", fg="green")
|
|
225
|
+
else:
|
|
226
|
+
click.secho(f"Failed to enable plugin: {response.message}", fg="red")
|
|
227
|
+
except Exception as e:
|
|
228
|
+
click.secho(f"An error occurred: {e}", fg="red")
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
@plugin.command("disable")
|
|
232
|
+
@click.argument("plugin_name")
|
|
233
|
+
@click.pass_context
|
|
234
|
+
async def disable_plugin(ctx, plugin_name: str):
|
|
235
|
+
"""Disables a plugin."""
|
|
236
|
+
client = ctx.obj.get("client")
|
|
237
|
+
if not client:
|
|
238
|
+
click.secho("You are not logged in.", fg="red")
|
|
239
|
+
return
|
|
240
|
+
|
|
241
|
+
try:
|
|
242
|
+
payload = PluginStatusSetPayload(enabled=False)
|
|
243
|
+
response = await client.async_set_plugin_status(plugin_name, payload)
|
|
244
|
+
if response.status == "success":
|
|
245
|
+
click.secho(f"Plugin '{plugin_name}' disabled successfully.", fg="green")
|
|
246
|
+
else:
|
|
247
|
+
click.secho(f"Failed to disable plugin: {response.message}", fg="red")
|
|
248
|
+
except Exception as e:
|
|
249
|
+
click.secho(f"An error occurred: {e}", fg="red")
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
@plugin.command("reload")
|
|
253
|
+
@click.pass_context
|
|
254
|
+
async def reload_plugins(ctx):
|
|
255
|
+
"""Reloads all plugins."""
|
|
256
|
+
client = ctx.obj.get("client")
|
|
257
|
+
if not client:
|
|
258
|
+
click.secho("You are not logged in.", fg="red")
|
|
259
|
+
return
|
|
260
|
+
|
|
261
|
+
try:
|
|
262
|
+
response = await client.async_reload_plugins()
|
|
263
|
+
if response.status == "success":
|
|
264
|
+
click.secho("Plugins reloaded successfully.", fg="green")
|
|
265
|
+
else:
|
|
266
|
+
click.secho(f"Failed to reload plugins: {response.message}", fg="red")
|
|
267
|
+
except Exception as e:
|
|
268
|
+
click.secho(f"An error occurred: {e}", fg="red")
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
@plugin.command("trigger-event")
|
|
272
|
+
@click.argument("event_name")
|
|
273
|
+
@click.option(
|
|
274
|
+
"--payload-json", help="Optional JSON string to use as the event payload."
|
|
275
|
+
)
|
|
276
|
+
@click.pass_context
|
|
277
|
+
async def trigger_event(ctx, event_name: str, payload_json: str):
|
|
278
|
+
"""Triggers a custom plugin event."""
|
|
279
|
+
client = ctx.obj.get("client")
|
|
280
|
+
if not client:
|
|
281
|
+
click.secho("You are not logged in.", fg="red")
|
|
282
|
+
return
|
|
283
|
+
|
|
284
|
+
try:
|
|
285
|
+
payload = None
|
|
286
|
+
if payload_json:
|
|
287
|
+
payload = json.loads(payload_json)
|
|
288
|
+
|
|
289
|
+
event_payload = TriggerEventPayload(event_name=event_name, payload=payload)
|
|
290
|
+
response = await client.async_trigger_plugin_event(event_payload)
|
|
291
|
+
if response.status == "success":
|
|
292
|
+
click.secho(f"Event '{event_name}' triggered successfully.", fg="green")
|
|
293
|
+
else:
|
|
294
|
+
click.secho(f"Failed to trigger event: {response.message}", fg="red")
|
|
295
|
+
except Exception as e:
|
|
296
|
+
click.secho(f"An error occurred: {e}", fg="red")
|
bsm_cli/properties.py
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import click
|
|
2
|
+
import questionary
|
|
3
|
+
|
|
4
|
+
from bsm_api_client.models import PropertiesPayload
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@click.group()
|
|
8
|
+
def properties():
|
|
9
|
+
"""Manages a server's server.properties file."""
|
|
10
|
+
pass
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@properties.command("get")
|
|
14
|
+
@click.option(
|
|
15
|
+
"-s",
|
|
16
|
+
"--server",
|
|
17
|
+
"server_name",
|
|
18
|
+
required=True,
|
|
19
|
+
help="The name of the target server.",
|
|
20
|
+
)
|
|
21
|
+
@click.option("-p", "--prop", "property_name", help="Display a single property value.")
|
|
22
|
+
@click.pass_context
|
|
23
|
+
async def get_props(ctx, server_name: str, property_name: str):
|
|
24
|
+
"""Displays server properties from a server's server.properties file."""
|
|
25
|
+
client = ctx.obj.get("client")
|
|
26
|
+
if not client:
|
|
27
|
+
click.secho("You are not logged in.", fg="red")
|
|
28
|
+
return
|
|
29
|
+
|
|
30
|
+
response = await client.async_get_server_properties(server_name)
|
|
31
|
+
|
|
32
|
+
if response.status == "success":
|
|
33
|
+
properties = response.properties
|
|
34
|
+
if property_name:
|
|
35
|
+
value = properties.get(property_name)
|
|
36
|
+
if value is not None:
|
|
37
|
+
click.echo(value)
|
|
38
|
+
else:
|
|
39
|
+
click.secho(f"Error: Property '{property_name}' not found.", fg="red")
|
|
40
|
+
else:
|
|
41
|
+
click.secho(f"\nProperties for '{server_name}':", bold=True)
|
|
42
|
+
max_key_len = max(len(k) for k in properties.keys()) if properties else 0
|
|
43
|
+
for key, value in sorted(properties.items()):
|
|
44
|
+
click.echo(f" {key:<{max_key_len}} = {value}")
|
|
45
|
+
else:
|
|
46
|
+
click.secho(f"Failed to get properties: {response.message}", fg="red")
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@properties.command("set")
|
|
50
|
+
@click.option(
|
|
51
|
+
"-s",
|
|
52
|
+
"--server",
|
|
53
|
+
"server_name",
|
|
54
|
+
required=True,
|
|
55
|
+
help="The name of the target server.",
|
|
56
|
+
)
|
|
57
|
+
@click.option(
|
|
58
|
+
"-p",
|
|
59
|
+
"--prop",
|
|
60
|
+
"properties",
|
|
61
|
+
multiple=True,
|
|
62
|
+
help="A 'key=value' pair to set. Use multiple times for multiple properties.",
|
|
63
|
+
)
|
|
64
|
+
@click.pass_context
|
|
65
|
+
async def set_props(ctx, server_name: str, properties: tuple[str]):
|
|
66
|
+
"""Sets one or more properties in a server's server.properties file."""
|
|
67
|
+
client = ctx.obj.get("client")
|
|
68
|
+
if not client:
|
|
69
|
+
click.secho("You are not logged in.", fg="red")
|
|
70
|
+
return
|
|
71
|
+
|
|
72
|
+
try:
|
|
73
|
+
if not properties:
|
|
74
|
+
click.secho(
|
|
75
|
+
f"No properties specified; starting interactive editor for '{server_name}'...",
|
|
76
|
+
fg="yellow",
|
|
77
|
+
)
|
|
78
|
+
await interactive_properties_workflow(client, server_name)
|
|
79
|
+
return
|
|
80
|
+
|
|
81
|
+
props_to_update = {}
|
|
82
|
+
for p in properties:
|
|
83
|
+
if "=" not in p:
|
|
84
|
+
click.secho(f"Error: Invalid format '{p}'. Use 'key=value'.", fg="red")
|
|
85
|
+
raise click.Abort()
|
|
86
|
+
key, value = p.split("=", 1)
|
|
87
|
+
props_to_update[key.strip()] = value.strip()
|
|
88
|
+
|
|
89
|
+
click.echo(
|
|
90
|
+
f"Updating {len(props_to_update)} propert(y/ies) for '{server_name}'..."
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
payload = PropertiesPayload(properties=props_to_update)
|
|
94
|
+
response = await client.async_update_server_properties(server_name, payload)
|
|
95
|
+
|
|
96
|
+
if response.status == "success":
|
|
97
|
+
click.secho("Properties updated successfully.", fg="green")
|
|
98
|
+
else:
|
|
99
|
+
click.secho(f"Failed to set properties: {response.message}", fg="red")
|
|
100
|
+
|
|
101
|
+
except Exception as e:
|
|
102
|
+
click.secho(f"An error occurred: {e}", fg="red")
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
async def interactive_properties_workflow(client, server_name: str): # noqa: C901
|
|
106
|
+
"""Guides a user through an interactive session to edit `server.properties`."""
|
|
107
|
+
click.secho("\n--- Interactive Server Properties Configuration ---", bold=True)
|
|
108
|
+
click.echo("Loading current server properties...")
|
|
109
|
+
|
|
110
|
+
properties_response = await client.async_get_server_properties(server_name)
|
|
111
|
+
if properties_response.status == "error":
|
|
112
|
+
click.secho(f"Error: {properties_response.message}", fg="red")
|
|
113
|
+
raise click.Abort()
|
|
114
|
+
|
|
115
|
+
current_properties = (
|
|
116
|
+
properties_response.properties if properties_response.properties else {}
|
|
117
|
+
)
|
|
118
|
+
changes = {}
|
|
119
|
+
|
|
120
|
+
async def _prompt(prop: str, message: str, prompter, **kwargs):
|
|
121
|
+
"""A nested helper to abstract the prompting and change-tracking logic."""
|
|
122
|
+
original_value = current_properties.get(prop)
|
|
123
|
+
|
|
124
|
+
if prompter == questionary.confirm:
|
|
125
|
+
default_bool = str(original_value).lower() == "true"
|
|
126
|
+
new_val = await prompter(
|
|
127
|
+
message, default=default_bool, **kwargs
|
|
128
|
+
).ask_async()
|
|
129
|
+
if new_val is None:
|
|
130
|
+
return
|
|
131
|
+
if new_val != default_bool:
|
|
132
|
+
changes[prop] = str(new_val).lower()
|
|
133
|
+
else:
|
|
134
|
+
new_val = await prompter(
|
|
135
|
+
message, default=str(original_value), **kwargs
|
|
136
|
+
).ask_async()
|
|
137
|
+
if new_val is None:
|
|
138
|
+
return
|
|
139
|
+
if new_val != original_value:
|
|
140
|
+
changes[prop] = new_val
|
|
141
|
+
|
|
142
|
+
await _prompt("server-name", "Server name (visible in LAN list):", questionary.text)
|
|
143
|
+
await _prompt("level-name", "World folder name:", questionary.text)
|
|
144
|
+
await _prompt(
|
|
145
|
+
"gamemode",
|
|
146
|
+
"Default gamemode:",
|
|
147
|
+
questionary.select,
|
|
148
|
+
choices=["survival", "creative", "adventure"],
|
|
149
|
+
)
|
|
150
|
+
await _prompt(
|
|
151
|
+
"difficulty",
|
|
152
|
+
"Game difficulty:",
|
|
153
|
+
questionary.select,
|
|
154
|
+
choices=["peaceful", "easy", "normal", "hard"],
|
|
155
|
+
)
|
|
156
|
+
await _prompt("allow-cheats", "Allow cheats:", questionary.confirm)
|
|
157
|
+
await _prompt("max-players", "Maximum players:", questionary.text)
|
|
158
|
+
await _prompt(
|
|
159
|
+
"online-mode", "Require Xbox Live authentication:", questionary.confirm
|
|
160
|
+
)
|
|
161
|
+
await _prompt("allow-list", "Enable allowlist:", questionary.confirm)
|
|
162
|
+
await _prompt(
|
|
163
|
+
"default-player-permission-level",
|
|
164
|
+
"Default permission for new players:",
|
|
165
|
+
questionary.select,
|
|
166
|
+
choices=["visitor", "member", "operator"],
|
|
167
|
+
)
|
|
168
|
+
await _prompt("view-distance", "View distance (chunks):", questionary.text)
|
|
169
|
+
await _prompt(
|
|
170
|
+
"tick-distance", "Tick simulation distance (chunks):", questionary.text
|
|
171
|
+
)
|
|
172
|
+
await _prompt(
|
|
173
|
+
"level-seed", "Level seed (leave blank for random):", questionary.text
|
|
174
|
+
)
|
|
175
|
+
await _prompt("texturepack-required", "Require texture packs:", questionary.confirm)
|
|
176
|
+
|
|
177
|
+
if not changes:
|
|
178
|
+
click.secho("\nNo properties were changed.", fg="cyan")
|
|
179
|
+
return
|
|
180
|
+
|
|
181
|
+
click.secho("\nApplying the following changes:", bold=True)
|
|
182
|
+
for key, value in changes.items():
|
|
183
|
+
original = current_properties.get(key, "not set")
|
|
184
|
+
click.echo(
|
|
185
|
+
f" - {key}: {click.style(original, fg='red')} -> {click.style(value, fg='green')}"
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
if not await questionary.confirm("Save these changes?", default=True).ask_async():
|
|
189
|
+
raise click.Abort()
|
|
190
|
+
|
|
191
|
+
payload = PropertiesPayload(properties=changes)
|
|
192
|
+
update_response = await client.async_update_server_properties(server_name, payload)
|
|
193
|
+
|
|
194
|
+
if update_response.status == "success":
|
|
195
|
+
click.secho("Server properties updated successfully.", fg="green")
|
|
196
|
+
else:
|
|
197
|
+
click.secho(f"Failed to update properties: {update_response.message}", fg="red")
|