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/__init__.py
ADDED
|
File without changes
|
bsm_cli/__main__.py
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
try:
|
|
2
|
+
import click
|
|
3
|
+
|
|
4
|
+
except ImportError:
|
|
5
|
+
print(
|
|
6
|
+
"Please install the required dependencies with `pip install bsm-api-client[cli]`"
|
|
7
|
+
)
|
|
8
|
+
exit(1)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
from contextlib import asynccontextmanager
|
|
12
|
+
|
|
13
|
+
from bsm_cli.account import account
|
|
14
|
+
from bsm_cli.addon import addon
|
|
15
|
+
from bsm_cli.allowlist import allowlist
|
|
16
|
+
from bsm_cli.auth import auth
|
|
17
|
+
from bsm_cli.backup import backup
|
|
18
|
+
from bsm_cli.bans import bans
|
|
19
|
+
from bsm_cli.config import Config
|
|
20
|
+
from bsm_cli.content import content
|
|
21
|
+
from bsm_cli.decorators import AsyncGroup
|
|
22
|
+
from bsm_cli.main_menus import main_menu
|
|
23
|
+
from bsm_cli.permissions import permissions
|
|
24
|
+
from bsm_cli.player import player
|
|
25
|
+
from bsm_cli.plugins import plugin
|
|
26
|
+
from bsm_cli.properties import properties
|
|
27
|
+
from bsm_cli.server import server
|
|
28
|
+
from bsm_cli.system import system
|
|
29
|
+
from bsm_cli.users import users
|
|
30
|
+
from bsm_cli.world import world
|
|
31
|
+
|
|
32
|
+
from bsm_api_client import BedrockServerManagerApi
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@click.group(cls=AsyncGroup, invoke_without_command=True)
|
|
36
|
+
@click.pass_context
|
|
37
|
+
def cli(ctx):
|
|
38
|
+
"""A CLI for managing Bedrock servers."""
|
|
39
|
+
ctx.obj["cli"] = cli
|
|
40
|
+
if ctx.invoked_subcommand is None:
|
|
41
|
+
return main_menu(ctx)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@cli.context
|
|
45
|
+
@asynccontextmanager
|
|
46
|
+
async def cli_context(ctx):
|
|
47
|
+
config = Config()
|
|
48
|
+
ctx.obj["config"] = config
|
|
49
|
+
|
|
50
|
+
try:
|
|
51
|
+
client = BedrockServerManagerApi(
|
|
52
|
+
base_url=config.base_url,
|
|
53
|
+
jwt_token=config.jwt_token,
|
|
54
|
+
verify_ssl=config.verify_ssl,
|
|
55
|
+
)
|
|
56
|
+
except ValueError as e:
|
|
57
|
+
# Ignore AuthError when logging out or auth group is called
|
|
58
|
+
if ctx.invoked_subcommand == auth or ctx.invoked_subcommand is None:
|
|
59
|
+
client = None
|
|
60
|
+
else:
|
|
61
|
+
raise e
|
|
62
|
+
ctx.obj["client"] = client
|
|
63
|
+
|
|
64
|
+
try:
|
|
65
|
+
yield
|
|
66
|
+
finally:
|
|
67
|
+
if ctx.obj.get("client"):
|
|
68
|
+
await ctx.obj["client"].close()
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
cli.add_command(auth)
|
|
72
|
+
cli.add_command(server)
|
|
73
|
+
cli.add_command(addon)
|
|
74
|
+
cli.add_command(backup)
|
|
75
|
+
cli.add_command(player)
|
|
76
|
+
cli.add_command(plugin)
|
|
77
|
+
cli.add_command(allowlist)
|
|
78
|
+
cli.add_command(bans)
|
|
79
|
+
cli.add_command(permissions)
|
|
80
|
+
cli.add_command(properties)
|
|
81
|
+
cli.add_command(system)
|
|
82
|
+
cli.add_command(world)
|
|
83
|
+
cli.add_command(account)
|
|
84
|
+
cli.add_command(content)
|
|
85
|
+
cli.add_command(users)
|
|
86
|
+
|
|
87
|
+
if __name__ == "__main__":
|
|
88
|
+
cli()
|
bsm_cli/account.py
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# src/bsm_cli/account.py
|
|
2
|
+
"""CLI commands for account management."""
|
|
3
|
+
|
|
4
|
+
import click
|
|
5
|
+
from bsm_cli.decorators import pass_async_context
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@click.group()
|
|
9
|
+
def account():
|
|
10
|
+
"""Commands for managing your account."""
|
|
11
|
+
pass
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@account.command()
|
|
15
|
+
@pass_async_context
|
|
16
|
+
async def details(ctx):
|
|
17
|
+
"""Get your account details."""
|
|
18
|
+
client = ctx.obj["client"]
|
|
19
|
+
details = await client.async_get_account_details()
|
|
20
|
+
click.echo(details.model_dump_json(indent=2))
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@account.command()
|
|
24
|
+
@click.option("--theme", prompt="Theme name", help="The name of the theme to set.")
|
|
25
|
+
@pass_async_context
|
|
26
|
+
async def update_theme(ctx, theme):
|
|
27
|
+
"""Update your theme."""
|
|
28
|
+
from bsm_api_client.models import ThemeUpdatePayload
|
|
29
|
+
|
|
30
|
+
client = ctx.obj["client"]
|
|
31
|
+
payload = ThemeUpdatePayload(theme=theme)
|
|
32
|
+
response = await client.async_update_theme(payload)
|
|
33
|
+
click.echo(response.model_dump_json(indent=2))
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@account.command()
|
|
37
|
+
@click.option("--full-name", prompt="Full Name", help="Your full name.")
|
|
38
|
+
@click.option("--email", prompt="Email", help="Your email address.")
|
|
39
|
+
@pass_async_context
|
|
40
|
+
async def update_profile(ctx, full_name, email):
|
|
41
|
+
"""Update your profile."""
|
|
42
|
+
from bsm_api_client.models import ProfileUpdatePayload
|
|
43
|
+
|
|
44
|
+
client = ctx.obj["client"]
|
|
45
|
+
payload = ProfileUpdatePayload(full_name=full_name, email=email)
|
|
46
|
+
response = await client.async_update_profile(payload)
|
|
47
|
+
click.echo(response.model_dump_json(indent=2))
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@account.command()
|
|
51
|
+
@click.option(
|
|
52
|
+
"--current-password",
|
|
53
|
+
prompt=True,
|
|
54
|
+
hide_input=True,
|
|
55
|
+
confirmation_prompt=False,
|
|
56
|
+
help="Your current password.",
|
|
57
|
+
)
|
|
58
|
+
@click.option(
|
|
59
|
+
"--new-password",
|
|
60
|
+
prompt=True,
|
|
61
|
+
hide_input=True,
|
|
62
|
+
confirmation_prompt=True,
|
|
63
|
+
help="Your new password.",
|
|
64
|
+
)
|
|
65
|
+
@pass_async_context
|
|
66
|
+
async def change_password(ctx, current_password, new_password):
|
|
67
|
+
"""Change your password."""
|
|
68
|
+
from bsm_api_client.models import ChangePasswordPayload
|
|
69
|
+
|
|
70
|
+
client = ctx.obj["client"]
|
|
71
|
+
payload = ChangePasswordPayload(
|
|
72
|
+
current_password=current_password, new_password=new_password
|
|
73
|
+
)
|
|
74
|
+
response = await client.async_change_password(payload)
|
|
75
|
+
click.echo(response.model_dump_json(indent=2))
|
bsm_cli/addon.py
ADDED
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
import questionary
|
|
5
|
+
from bsm_cli.decorators import monitor_task, pass_async_context
|
|
6
|
+
from questionary import Separator
|
|
7
|
+
|
|
8
|
+
from bsm_api_client.models import (
|
|
9
|
+
AddonActionPayload,
|
|
10
|
+
AddonReorderPayload,
|
|
11
|
+
AddonSubpackPayload,
|
|
12
|
+
FileNamePayload,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@click.group()
|
|
17
|
+
def addon():
|
|
18
|
+
"""Manages server addons."""
|
|
19
|
+
pass
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@addon.command("install")
|
|
23
|
+
@click.option(
|
|
24
|
+
"-s", "--server", "server_name", required=True, help="Name of the target server."
|
|
25
|
+
)
|
|
26
|
+
@click.option(
|
|
27
|
+
"-f",
|
|
28
|
+
"--file",
|
|
29
|
+
"addon_file_path",
|
|
30
|
+
type=click.Path(exists=True, dir_okay=False, resolve_path=True),
|
|
31
|
+
help="Path to the addon file (.mcpack, .mcaddon); skips interactive menu.",
|
|
32
|
+
)
|
|
33
|
+
@pass_async_context
|
|
34
|
+
async def install_addon(ctx, server_name: str, addon_file_path: str):
|
|
35
|
+
"""Installs a behavior or resource pack addon to a specified server."""
|
|
36
|
+
client = ctx.obj.get("client")
|
|
37
|
+
if not client:
|
|
38
|
+
click.secho("You are not logged in.", fg="red")
|
|
39
|
+
return
|
|
40
|
+
|
|
41
|
+
try:
|
|
42
|
+
selected_addon_path = addon_file_path
|
|
43
|
+
|
|
44
|
+
if not selected_addon_path:
|
|
45
|
+
click.secho(
|
|
46
|
+
f"Entering interactive addon installation for server: {server_name}",
|
|
47
|
+
fg="yellow",
|
|
48
|
+
)
|
|
49
|
+
response = await client.async_get_content_addons()
|
|
50
|
+
available_files = response.files
|
|
51
|
+
|
|
52
|
+
if not available_files:
|
|
53
|
+
click.secho(
|
|
54
|
+
"No addon files found in the content/addons directory. Nothing to install.",
|
|
55
|
+
fg="yellow",
|
|
56
|
+
)
|
|
57
|
+
return
|
|
58
|
+
|
|
59
|
+
file_map = {os.path.basename(f): f for f in available_files}
|
|
60
|
+
choices = sorted(list(file_map.keys())) + ["Cancel"]
|
|
61
|
+
selection = await questionary.select(
|
|
62
|
+
"Select an addon to install:", choices=choices
|
|
63
|
+
).ask_async()
|
|
64
|
+
|
|
65
|
+
if not selection or selection == "Cancel":
|
|
66
|
+
raise click.Abort()
|
|
67
|
+
selected_addon_path = file_map[selection]
|
|
68
|
+
|
|
69
|
+
addon_filename = os.path.basename(selected_addon_path)
|
|
70
|
+
click.echo(f"Installing addon '{addon_filename}' to server '{server_name}'...")
|
|
71
|
+
|
|
72
|
+
payload = FileNamePayload(filename=addon_filename)
|
|
73
|
+
response = await client.async_install_server_addon(server_name, payload)
|
|
74
|
+
if response.task_id:
|
|
75
|
+
await monitor_task(
|
|
76
|
+
client,
|
|
77
|
+
response.task_id,
|
|
78
|
+
f"Addon '{addon_filename}' installed successfully",
|
|
79
|
+
"Failed to install addon",
|
|
80
|
+
)
|
|
81
|
+
elif response.status == "success":
|
|
82
|
+
click.secho(f"Addon '{addon_filename}' installed successfully.", fg="green")
|
|
83
|
+
else:
|
|
84
|
+
click.secho(f"Failed to install addon: {response.message}", fg="red")
|
|
85
|
+
|
|
86
|
+
except Exception as e:
|
|
87
|
+
click.secho(f"An error occurred: {e}", fg="red")
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
@addon.command("manage")
|
|
91
|
+
@click.option(
|
|
92
|
+
"-s", "--server", "server_name", required=True, help="Name of the target server."
|
|
93
|
+
)
|
|
94
|
+
@pass_async_context
|
|
95
|
+
async def manage_addons(ctx, server_name: str): # noqa: C901
|
|
96
|
+
"""Interactively manages installed addons on a specified server."""
|
|
97
|
+
client = ctx.obj.get("client")
|
|
98
|
+
if not client:
|
|
99
|
+
click.secho("You are not logged in.", fg="red")
|
|
100
|
+
return
|
|
101
|
+
|
|
102
|
+
while True:
|
|
103
|
+
try:
|
|
104
|
+
response = await client.async_get_server_addons(server_name)
|
|
105
|
+
addons = response.addons
|
|
106
|
+
if not addons:
|
|
107
|
+
click.secho(
|
|
108
|
+
"Failed to fetch installed addons or no addons returned.", fg="red"
|
|
109
|
+
)
|
|
110
|
+
return
|
|
111
|
+
|
|
112
|
+
bp = addons.behavior_packs or []
|
|
113
|
+
rp = addons.resource_packs or []
|
|
114
|
+
|
|
115
|
+
click.clear()
|
|
116
|
+
click.secho(
|
|
117
|
+
f"--- Manage Addons for {server_name} ---", fg="magenta", bold=True
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
from questionary import Choice
|
|
121
|
+
|
|
122
|
+
menu_choices: list[Choice | Separator | str] = [
|
|
123
|
+
Separator("--- Behavior Packs ---")
|
|
124
|
+
]
|
|
125
|
+
|
|
126
|
+
for idx, pack in enumerate(bp, 1):
|
|
127
|
+
status = "🟢" if pack.status == "ACTIVE" else "⚪"
|
|
128
|
+
menu_choices.append(
|
|
129
|
+
Choice(
|
|
130
|
+
f"{idx}. {status} [BP] {pack.name} (v{'.'.join(map(str, pack.version))})",
|
|
131
|
+
value=f"{idx}. {status} [BP] {pack.name} (v{'.'.join(map(str, pack.version))})",
|
|
132
|
+
)
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
menu_choices.append(Separator("--- Resource Packs ---"))
|
|
136
|
+
for idx, pack in enumerate(rp, 1):
|
|
137
|
+
status = "🟢" if pack.status == "ACTIVE" else "⚪"
|
|
138
|
+
menu_choices.append(
|
|
139
|
+
Choice(
|
|
140
|
+
f"{idx}. {status} [RP] {pack.name} (v{'.'.join(map(str, pack.version))})",
|
|
141
|
+
value=f"{idx}. {status} [RP] {pack.name} (v{'.'.join(map(str, pack.version))})",
|
|
142
|
+
)
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
menu_choices.extend(
|
|
146
|
+
[
|
|
147
|
+
Separator("--- Actions ---"),
|
|
148
|
+
Choice("Reorder Behavior Packs", value="Reorder Behavior Packs"),
|
|
149
|
+
Choice("Reorder Resource Packs", value="Reorder Resource Packs"),
|
|
150
|
+
Choice("Back", value="Back"),
|
|
151
|
+
]
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
choice = await questionary.select(
|
|
155
|
+
"Select an addon to manage or an action:", choices=menu_choices
|
|
156
|
+
).ask_async()
|
|
157
|
+
|
|
158
|
+
if not choice or choice == "Back":
|
|
159
|
+
return
|
|
160
|
+
|
|
161
|
+
if choice == "Reorder Behavior Packs":
|
|
162
|
+
active_bp = [p for p in bp if p.status == "ACTIVE"]
|
|
163
|
+
if not active_bp:
|
|
164
|
+
click.secho("No active behavior packs to reorder.", fg="yellow")
|
|
165
|
+
await questionary.press_any_key_to_continue().ask_async()
|
|
166
|
+
continue
|
|
167
|
+
|
|
168
|
+
from typing import List
|
|
169
|
+
|
|
170
|
+
ordered_uuids: List[str] = []
|
|
171
|
+
remaining_bp = list(active_bp)
|
|
172
|
+
|
|
173
|
+
while remaining_bp:
|
|
174
|
+
choices = [f"{p.name} ({p.uuid})" for p in remaining_bp]
|
|
175
|
+
selected_str = await questionary.select(
|
|
176
|
+
f"Select pack for position {len(ordered_uuids) + 1}:",
|
|
177
|
+
choices=choices,
|
|
178
|
+
).ask_async()
|
|
179
|
+
if not selected_str:
|
|
180
|
+
break
|
|
181
|
+
|
|
182
|
+
uuid = selected_str.split("(")[-1].replace(")", "")
|
|
183
|
+
ordered_uuids.append(uuid)
|
|
184
|
+
remaining_bp = [p for p in remaining_bp if p.uuid != uuid]
|
|
185
|
+
|
|
186
|
+
if len(ordered_uuids) == len(active_bp):
|
|
187
|
+
payload = AddonReorderPayload(
|
|
188
|
+
pack_type="behavior", uuids=ordered_uuids
|
|
189
|
+
)
|
|
190
|
+
res = await client.async_reorder_server_addon(server_name, payload)
|
|
191
|
+
if res.task_id:
|
|
192
|
+
await monitor_task(
|
|
193
|
+
client,
|
|
194
|
+
res.task_id,
|
|
195
|
+
"Reorder successful",
|
|
196
|
+
"Failed to reorder",
|
|
197
|
+
)
|
|
198
|
+
else:
|
|
199
|
+
click.secho("Reordered successfully.", fg="green")
|
|
200
|
+
else:
|
|
201
|
+
click.secho("Reorder cancelled.", fg="yellow")
|
|
202
|
+
|
|
203
|
+
await questionary.press_any_key_to_continue().ask_async()
|
|
204
|
+
continue
|
|
205
|
+
|
|
206
|
+
if choice == "Reorder Resource Packs":
|
|
207
|
+
active_rp = [p for p in rp if p.status == "ACTIVE"]
|
|
208
|
+
if not active_rp:
|
|
209
|
+
click.secho("No active resource packs to reorder.", fg="yellow")
|
|
210
|
+
await questionary.press_any_key_to_continue().ask_async()
|
|
211
|
+
continue
|
|
212
|
+
|
|
213
|
+
ordered_uuids = []
|
|
214
|
+
remaining_rp = list(active_rp)
|
|
215
|
+
|
|
216
|
+
while remaining_rp:
|
|
217
|
+
choices = [f"{p.name} ({p.uuid})" for p in remaining_rp]
|
|
218
|
+
selected_str = await questionary.select(
|
|
219
|
+
f"Select pack for position {len(ordered_uuids) + 1}:",
|
|
220
|
+
choices=choices,
|
|
221
|
+
).ask_async()
|
|
222
|
+
if not selected_str:
|
|
223
|
+
break
|
|
224
|
+
|
|
225
|
+
uuid = selected_str.split("(")[-1].replace(")", "")
|
|
226
|
+
ordered_uuids.append(uuid)
|
|
227
|
+
remaining_rp = [p for p in remaining_rp if p.uuid != uuid]
|
|
228
|
+
|
|
229
|
+
if len(ordered_uuids) == len(active_rp):
|
|
230
|
+
payload = AddonReorderPayload(
|
|
231
|
+
pack_type="resource", uuids=ordered_uuids
|
|
232
|
+
)
|
|
233
|
+
res = await client.async_reorder_server_addon(server_name, payload)
|
|
234
|
+
if res.task_id:
|
|
235
|
+
await monitor_task(
|
|
236
|
+
client,
|
|
237
|
+
res.task_id,
|
|
238
|
+
"Reorder successful",
|
|
239
|
+
"Failed to reorder",
|
|
240
|
+
)
|
|
241
|
+
else:
|
|
242
|
+
click.secho("Reordered successfully.", fg="green")
|
|
243
|
+
else:
|
|
244
|
+
click.secho("Reorder cancelled.", fg="yellow")
|
|
245
|
+
|
|
246
|
+
await questionary.press_any_key_to_continue().ask_async()
|
|
247
|
+
continue
|
|
248
|
+
|
|
249
|
+
# Handle specific pack
|
|
250
|
+
is_bp = "[BP]" in choice
|
|
251
|
+
pack_type = "behavior" if is_bp else "resource"
|
|
252
|
+
pack_name = choice.split("] ")[1].split(" (v")[0]
|
|
253
|
+
|
|
254
|
+
pack = next((p for p in (bp if is_bp else rp) if p.name == pack_name), None)
|
|
255
|
+
|
|
256
|
+
if not pack:
|
|
257
|
+
continue
|
|
258
|
+
|
|
259
|
+
pack_menu = []
|
|
260
|
+
if pack.status == "ACTIVE":
|
|
261
|
+
pack_menu.append("Disable")
|
|
262
|
+
if pack.subpacks:
|
|
263
|
+
pack_menu.append("Change Subpack")
|
|
264
|
+
else:
|
|
265
|
+
pack_menu.append("Enable")
|
|
266
|
+
|
|
267
|
+
pack_menu.extend(["Uninstall", "Back"])
|
|
268
|
+
|
|
269
|
+
action_choice = await questionary.select(
|
|
270
|
+
f"Actions for {pack.name}:", choices=pack_menu
|
|
271
|
+
).ask_async()
|
|
272
|
+
|
|
273
|
+
if not action_choice or action_choice == "Back":
|
|
274
|
+
continue
|
|
275
|
+
|
|
276
|
+
if action_choice == "Enable":
|
|
277
|
+
res = await client.async_enable_server_addon(
|
|
278
|
+
server_name,
|
|
279
|
+
AddonActionPayload(pack_uuid=pack.uuid, pack_type=pack_type),
|
|
280
|
+
)
|
|
281
|
+
if res.task_id:
|
|
282
|
+
await monitor_task(
|
|
283
|
+
client, res.task_id, "Enabled successfully", "Failed to enable"
|
|
284
|
+
)
|
|
285
|
+
elif action_choice == "Disable":
|
|
286
|
+
res = await client.async_disable_server_addon(
|
|
287
|
+
server_name,
|
|
288
|
+
AddonActionPayload(pack_uuid=pack.uuid, pack_type=pack_type),
|
|
289
|
+
)
|
|
290
|
+
if res.task_id:
|
|
291
|
+
await monitor_task(
|
|
292
|
+
client,
|
|
293
|
+
res.task_id,
|
|
294
|
+
"Disabled successfully",
|
|
295
|
+
"Failed to disable",
|
|
296
|
+
)
|
|
297
|
+
elif action_choice == "Uninstall":
|
|
298
|
+
if await questionary.confirm(
|
|
299
|
+
f"Are you sure you want to uninstall {pack.name}?"
|
|
300
|
+
).ask_async():
|
|
301
|
+
res = await client.async_uninstall_server_addon(
|
|
302
|
+
server_name,
|
|
303
|
+
AddonActionPayload(pack_uuid=pack.uuid, pack_type=pack_type),
|
|
304
|
+
)
|
|
305
|
+
if res.task_id:
|
|
306
|
+
await monitor_task(
|
|
307
|
+
client,
|
|
308
|
+
res.task_id,
|
|
309
|
+
"Uninstalled successfully",
|
|
310
|
+
"Failed to uninstall",
|
|
311
|
+
)
|
|
312
|
+
elif action_choice == "Change Subpack":
|
|
313
|
+
subpack_choices = []
|
|
314
|
+
default_sp = None
|
|
315
|
+
for sp in pack.subpacks:
|
|
316
|
+
sp_name = sp.get("name") or sp.get("folder_name")
|
|
317
|
+
if pack.active_subpack == sp.get("folder_name"):
|
|
318
|
+
sp_name += " (Active)"
|
|
319
|
+
default_sp = sp_name
|
|
320
|
+
subpack_choices.append(sp_name)
|
|
321
|
+
|
|
322
|
+
sp_choice = await questionary.select(
|
|
323
|
+
"Select new subpack:", choices=subpack_choices, default=default_sp
|
|
324
|
+
).ask_async()
|
|
325
|
+
|
|
326
|
+
if sp_choice:
|
|
327
|
+
clean_sp_choice = sp_choice.replace(" (Active)", "")
|
|
328
|
+
selected_sp = next(
|
|
329
|
+
(
|
|
330
|
+
sp
|
|
331
|
+
for sp in pack.subpacks
|
|
332
|
+
if sp.get("name") == clean_sp_choice
|
|
333
|
+
or sp.get("folder_name") == clean_sp_choice
|
|
334
|
+
),
|
|
335
|
+
None,
|
|
336
|
+
)
|
|
337
|
+
if selected_sp:
|
|
338
|
+
subpack_payload = AddonSubpackPayload(
|
|
339
|
+
pack_uuid=pack.uuid,
|
|
340
|
+
pack_type=pack_type,
|
|
341
|
+
subpack_name=selected_sp.get("folder_name"),
|
|
342
|
+
)
|
|
343
|
+
setattr(
|
|
344
|
+
subpack_payload,
|
|
345
|
+
f"subpack_{pack.uuid}",
|
|
346
|
+
selected_sp.get("folder_name"),
|
|
347
|
+
)
|
|
348
|
+
|
|
349
|
+
res = await client.async_update_server_addon_subpack(
|
|
350
|
+
server_name, subpack_payload
|
|
351
|
+
)
|
|
352
|
+
if res.task_id:
|
|
353
|
+
await monitor_task(
|
|
354
|
+
client,
|
|
355
|
+
res.task_id,
|
|
356
|
+
"Subpack updated successfully",
|
|
357
|
+
"Failed to update subpack",
|
|
358
|
+
)
|
|
359
|
+
|
|
360
|
+
except Exception as e:
|
|
361
|
+
click.secho(f"An error occurred: {e}", fg="red")
|
|
362
|
+
await questionary.press_any_key_to_continue().ask_async()
|