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/users.py
ADDED
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import click
|
|
2
|
+
import questionary
|
|
3
|
+
from bsm_cli.decorators import pass_async_context
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
async def interactive_user_workflow(ctx, client): # noqa: C901
|
|
7
|
+
"""Interactive menu for managing users."""
|
|
8
|
+
while True:
|
|
9
|
+
try:
|
|
10
|
+
users_list = await client.async_get_users()
|
|
11
|
+
click.clear()
|
|
12
|
+
click.secho("--- Manage Users ---", fg="magenta", bold=True)
|
|
13
|
+
for user in users_list:
|
|
14
|
+
click.echo(
|
|
15
|
+
f"ID: {user.id} | Username: {user.username} | Role: {user.role} | Active: {user.is_active} | Type: {user.identity_type}"
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
choices = [
|
|
19
|
+
"List Users",
|
|
20
|
+
"Delete User",
|
|
21
|
+
"Set User Role",
|
|
22
|
+
"Enable User",
|
|
23
|
+
"Disable User",
|
|
24
|
+
"Generate Invite Link",
|
|
25
|
+
"Back",
|
|
26
|
+
]
|
|
27
|
+
choice = await questionary.select(
|
|
28
|
+
"\nChoose an action:",
|
|
29
|
+
choices=choices,
|
|
30
|
+
use_indicator=True,
|
|
31
|
+
).ask_async()
|
|
32
|
+
|
|
33
|
+
if choice is None or choice == "Back":
|
|
34
|
+
return
|
|
35
|
+
|
|
36
|
+
if choice == "List Users":
|
|
37
|
+
click.echo("\nUsers List:")
|
|
38
|
+
for user in users_list:
|
|
39
|
+
click.echo(
|
|
40
|
+
f"ID: {user.id} | Username: {user.username} | Role: {user.role} | Active: {user.is_active} | Type: {user.identity_type}"
|
|
41
|
+
)
|
|
42
|
+
await questionary.press_any_key_to_continue().ask_async()
|
|
43
|
+
|
|
44
|
+
elif choice == "Delete User":
|
|
45
|
+
if not users_list:
|
|
46
|
+
click.secho("No users found.", fg="yellow")
|
|
47
|
+
await questionary.press_any_key_to_continue().ask_async()
|
|
48
|
+
continue
|
|
49
|
+
|
|
50
|
+
user_choices = [
|
|
51
|
+
questionary.Choice(title=f"{u.username} (ID: {u.id})", value=u.id)
|
|
52
|
+
for u in users_list
|
|
53
|
+
]
|
|
54
|
+
user_choices.append(questionary.Choice(title="Cancel", value="Cancel"))
|
|
55
|
+
|
|
56
|
+
user_id = await questionary.select(
|
|
57
|
+
"Select user to delete:", choices=user_choices
|
|
58
|
+
).ask_async()
|
|
59
|
+
|
|
60
|
+
if user_id and user_id != "Cancel":
|
|
61
|
+
confirm = await questionary.confirm(
|
|
62
|
+
f"Are you sure you want to delete user {user_id}?"
|
|
63
|
+
).ask_async()
|
|
64
|
+
if confirm:
|
|
65
|
+
response = await client.async_delete_user(user_id)
|
|
66
|
+
click.echo(response.model_dump_json(indent=2))
|
|
67
|
+
await questionary.press_any_key_to_continue().ask_async()
|
|
68
|
+
|
|
69
|
+
elif choice == "Set User Role":
|
|
70
|
+
if not users_list:
|
|
71
|
+
click.secho("No users found.", fg="yellow")
|
|
72
|
+
await questionary.press_any_key_to_continue().ask_async()
|
|
73
|
+
continue
|
|
74
|
+
|
|
75
|
+
user_choices = [
|
|
76
|
+
questionary.Choice(title=f"{u.username} (ID: {u.id})", value=u.id)
|
|
77
|
+
for u in users_list
|
|
78
|
+
]
|
|
79
|
+
user_choices.append(questionary.Choice(title="Cancel", value="Cancel"))
|
|
80
|
+
|
|
81
|
+
user_id = await questionary.select(
|
|
82
|
+
"Select user to modify:", choices=user_choices
|
|
83
|
+
).ask_async()
|
|
84
|
+
|
|
85
|
+
if user_id and user_id != "Cancel":
|
|
86
|
+
role = await questionary.select(
|
|
87
|
+
"Select new role:",
|
|
88
|
+
choices=["user", "moderator", "admin"],
|
|
89
|
+
).ask_async()
|
|
90
|
+
if role:
|
|
91
|
+
response = await client.async_update_user_role(user_id, role)
|
|
92
|
+
click.echo(response.model_dump_json(indent=2))
|
|
93
|
+
await questionary.press_any_key_to_continue().ask_async()
|
|
94
|
+
|
|
95
|
+
elif choice == "Enable User":
|
|
96
|
+
if not users_list:
|
|
97
|
+
click.secho("No users found.", fg="yellow")
|
|
98
|
+
await questionary.press_any_key_to_continue().ask_async()
|
|
99
|
+
continue
|
|
100
|
+
|
|
101
|
+
disabled_users = [u for u in users_list if not u.is_active]
|
|
102
|
+
if not disabled_users:
|
|
103
|
+
click.secho("No disabled users found.", fg="yellow")
|
|
104
|
+
await questionary.press_any_key_to_continue().ask_async()
|
|
105
|
+
continue
|
|
106
|
+
|
|
107
|
+
user_choices = [
|
|
108
|
+
questionary.Choice(title=f"{u.username} (ID: {u.id})", value=u.id)
|
|
109
|
+
for u in disabled_users
|
|
110
|
+
]
|
|
111
|
+
user_choices.append(questionary.Choice(title="Cancel", value="Cancel"))
|
|
112
|
+
|
|
113
|
+
user_id = await questionary.select(
|
|
114
|
+
"Select user to enable:", choices=user_choices
|
|
115
|
+
).ask_async()
|
|
116
|
+
|
|
117
|
+
if user_id and user_id != "Cancel":
|
|
118
|
+
response = await client.async_enable_user(user_id)
|
|
119
|
+
click.echo(response.model_dump_json(indent=2))
|
|
120
|
+
await questionary.press_any_key_to_continue().ask_async()
|
|
121
|
+
|
|
122
|
+
elif choice == "Disable User":
|
|
123
|
+
if not users_list:
|
|
124
|
+
click.secho("No users found.", fg="yellow")
|
|
125
|
+
await questionary.press_any_key_to_continue().ask_async()
|
|
126
|
+
continue
|
|
127
|
+
|
|
128
|
+
enabled_users = [u for u in users_list if u.is_active]
|
|
129
|
+
if not enabled_users:
|
|
130
|
+
click.secho("No enabled users found.", fg="yellow")
|
|
131
|
+
await questionary.press_any_key_to_continue().ask_async()
|
|
132
|
+
continue
|
|
133
|
+
|
|
134
|
+
user_choices = [
|
|
135
|
+
questionary.Choice(title=f"{u.username} (ID: {u.id})", value=u.id)
|
|
136
|
+
for u in enabled_users
|
|
137
|
+
]
|
|
138
|
+
user_choices.append(questionary.Choice(title="Cancel", value="Cancel"))
|
|
139
|
+
|
|
140
|
+
user_id = await questionary.select(
|
|
141
|
+
"Select user to disable:", choices=user_choices
|
|
142
|
+
).ask_async()
|
|
143
|
+
|
|
144
|
+
if user_id and user_id != "Cancel":
|
|
145
|
+
response = await client.async_disable_user(user_id)
|
|
146
|
+
click.echo(response.model_dump_json(indent=2))
|
|
147
|
+
await questionary.press_any_key_to_continue().ask_async()
|
|
148
|
+
|
|
149
|
+
elif choice == "Generate Invite Link":
|
|
150
|
+
role = await questionary.select(
|
|
151
|
+
"Select role for invite:",
|
|
152
|
+
choices=["user", "moderator", "admin"],
|
|
153
|
+
).ask_async()
|
|
154
|
+
if role:
|
|
155
|
+
response = await client.async_generate_invite_token(role)
|
|
156
|
+
click.echo(f"Invite Link: {response.registration_url}")
|
|
157
|
+
await questionary.press_any_key_to_continue().ask_async()
|
|
158
|
+
|
|
159
|
+
except Exception as e:
|
|
160
|
+
click.secho(f"An error occurred: {e}", fg="red")
|
|
161
|
+
await questionary.press_any_key_to_continue().ask_async()
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
@click.group(invoke_without_command=True)
|
|
165
|
+
@click.pass_context
|
|
166
|
+
async def users(ctx):
|
|
167
|
+
"""Commands for managing users."""
|
|
168
|
+
if ctx.invoked_subcommand is None:
|
|
169
|
+
client = ctx.obj.get("client")
|
|
170
|
+
if not client:
|
|
171
|
+
click.secho("You are not logged in.", fg="red")
|
|
172
|
+
return
|
|
173
|
+
await interactive_user_workflow(ctx, client)
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
@users.command()
|
|
177
|
+
@pass_async_context
|
|
178
|
+
async def list(ctx):
|
|
179
|
+
"""List all users."""
|
|
180
|
+
client = ctx.obj["client"]
|
|
181
|
+
users = await client.async_get_users()
|
|
182
|
+
for user in users:
|
|
183
|
+
click.echo(
|
|
184
|
+
f"ID: {user.id} | Username: {user.username} | Role: {user.role} | Active: {user.is_active} | Type: {user.identity_type}"
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
@users.command()
|
|
189
|
+
@click.argument("user_id", type=int)
|
|
190
|
+
@click.option("--yes", is_flag=True, help="Skip confirmation prompt")
|
|
191
|
+
@pass_async_context
|
|
192
|
+
async def delete(ctx, user_id: int, yes: bool):
|
|
193
|
+
"""Delete a user."""
|
|
194
|
+
client = ctx.obj["client"]
|
|
195
|
+
if not yes:
|
|
196
|
+
confirm = await questionary.confirm(
|
|
197
|
+
f"Are you sure you want to delete user {user_id}?"
|
|
198
|
+
).ask_async()
|
|
199
|
+
if not confirm:
|
|
200
|
+
click.echo("Aborted.")
|
|
201
|
+
return
|
|
202
|
+
|
|
203
|
+
response = await client.async_delete_user(user_id)
|
|
204
|
+
click.echo(response.model_dump_json(indent=2))
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
@users.command()
|
|
208
|
+
@click.argument("user_id", type=int)
|
|
209
|
+
@click.argument("role", type=click.Choice(["user", "moderator", "admin"]))
|
|
210
|
+
@pass_async_context
|
|
211
|
+
async def set_role(ctx, user_id: int, role: str):
|
|
212
|
+
"""Set a user's role."""
|
|
213
|
+
client = ctx.obj["client"]
|
|
214
|
+
response = await client.async_update_user_role(user_id, role)
|
|
215
|
+
click.echo(response.model_dump_json(indent=2))
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
@users.command()
|
|
219
|
+
@click.argument("user_id", type=int)
|
|
220
|
+
@pass_async_context
|
|
221
|
+
async def enable(ctx, user_id: int):
|
|
222
|
+
"""Enable a user account."""
|
|
223
|
+
client = ctx.obj["client"]
|
|
224
|
+
response = await client.async_enable_user(user_id)
|
|
225
|
+
click.echo(response.model_dump_json(indent=2))
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
@users.command()
|
|
229
|
+
@click.argument("user_id", type=int)
|
|
230
|
+
@pass_async_context
|
|
231
|
+
async def disable(ctx, user_id: int):
|
|
232
|
+
"""Disable a user account."""
|
|
233
|
+
client = ctx.obj["client"]
|
|
234
|
+
response = await client.async_disable_user(user_id)
|
|
235
|
+
click.echo(response.model_dump_json(indent=2))
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
@users.command()
|
|
239
|
+
@click.argument("role", type=click.Choice(["user", "moderator", "admin"]))
|
|
240
|
+
@pass_async_context
|
|
241
|
+
async def invite(ctx, role: str):
|
|
242
|
+
"""Generate an invite link for a new user."""
|
|
243
|
+
client = ctx.obj["client"]
|
|
244
|
+
response = await client.async_generate_invite_token(role)
|
|
245
|
+
click.echo(f"Invite Link: {response.registration_url}")
|
bsm_cli/world.py
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
import questionary
|
|
5
|
+
from bsm_cli.decorators import monitor_task, pass_async_context
|
|
6
|
+
|
|
7
|
+
from bsm_api_client.models import FileNamePayload
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@click.group()
|
|
11
|
+
def world():
|
|
12
|
+
"""Manages server worlds."""
|
|
13
|
+
pass
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@world.command("install")
|
|
17
|
+
@click.option(
|
|
18
|
+
"-s", "--server", "server_name", required=True, help="Name of the target server."
|
|
19
|
+
)
|
|
20
|
+
@click.option(
|
|
21
|
+
"-f",
|
|
22
|
+
"--file",
|
|
23
|
+
"world_file_path",
|
|
24
|
+
type=click.Path(exists=True, dir_okay=False, resolve_path=True),
|
|
25
|
+
help="Path to the .mcworld file to install. Skips interactive menu.",
|
|
26
|
+
)
|
|
27
|
+
@pass_async_context
|
|
28
|
+
async def install_world(ctx, server_name: str, world_file_path: str):
|
|
29
|
+
"""Installs a world from a .mcworld file, replacing the server's current world."""
|
|
30
|
+
client = ctx.obj.get("client")
|
|
31
|
+
if not client:
|
|
32
|
+
click.secho("You are not logged in.", fg="red")
|
|
33
|
+
return
|
|
34
|
+
|
|
35
|
+
try:
|
|
36
|
+
selected_file = world_file_path
|
|
37
|
+
|
|
38
|
+
if not selected_file:
|
|
39
|
+
click.secho(
|
|
40
|
+
f"Entering interactive world installation for server: {server_name}",
|
|
41
|
+
fg="yellow",
|
|
42
|
+
)
|
|
43
|
+
response = await client.async_get_content_worlds()
|
|
44
|
+
available_files = response.files
|
|
45
|
+
|
|
46
|
+
if not available_files:
|
|
47
|
+
click.secho(
|
|
48
|
+
"No .mcworld files found in the content/worlds directory. Nothing to install.",
|
|
49
|
+
fg="yellow",
|
|
50
|
+
)
|
|
51
|
+
return
|
|
52
|
+
|
|
53
|
+
file_map = {os.path.basename(f): f for f in available_files}
|
|
54
|
+
choices = sorted(list(file_map.keys())) + ["Cancel"]
|
|
55
|
+
selection = await questionary.select(
|
|
56
|
+
"Select a world to install:", choices=choices
|
|
57
|
+
).ask_async()
|
|
58
|
+
|
|
59
|
+
if not selection or selection == "Cancel":
|
|
60
|
+
raise click.Abort()
|
|
61
|
+
selected_file = file_map[selection]
|
|
62
|
+
|
|
63
|
+
filename = os.path.basename(selected_file)
|
|
64
|
+
click.secho(
|
|
65
|
+
f"\nWARNING: Installing '{filename}' will REPLACE the current world data for server '{server_name}'.",
|
|
66
|
+
fg="red",
|
|
67
|
+
bold=True,
|
|
68
|
+
)
|
|
69
|
+
if not await questionary.confirm(
|
|
70
|
+
"This action cannot be undone. Are you sure?", default=False
|
|
71
|
+
).ask_async():
|
|
72
|
+
raise click.Abort()
|
|
73
|
+
|
|
74
|
+
click.echo(f"Installing world '{filename}'...")
|
|
75
|
+
payload = FileNamePayload(filename=filename)
|
|
76
|
+
response = await client.async_install_server_world(server_name, payload)
|
|
77
|
+
|
|
78
|
+
if response.task_id:
|
|
79
|
+
await monitor_task(
|
|
80
|
+
client,
|
|
81
|
+
response.task_id,
|
|
82
|
+
f"World '{filename}' installed successfully",
|
|
83
|
+
"Failed to install world",
|
|
84
|
+
)
|
|
85
|
+
elif response.status == "success":
|
|
86
|
+
click.secho(f"World '{filename}' installed successfully.", fg="green")
|
|
87
|
+
else:
|
|
88
|
+
click.secho(f"Failed to install world: {response.message}", fg="red")
|
|
89
|
+
|
|
90
|
+
except Exception as e:
|
|
91
|
+
click.secho(f"An error occurred: {e}", fg="red")
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
@world.command("export")
|
|
95
|
+
@click.option(
|
|
96
|
+
"-s",
|
|
97
|
+
"--server",
|
|
98
|
+
"server_name",
|
|
99
|
+
required=True,
|
|
100
|
+
help="Name of the server whose world to export.",
|
|
101
|
+
)
|
|
102
|
+
@pass_async_context
|
|
103
|
+
async def export_world(ctx, server_name: str):
|
|
104
|
+
"""Exports the server's current active world to a .mcworld file."""
|
|
105
|
+
client = ctx.obj.get("client")
|
|
106
|
+
if not client:
|
|
107
|
+
click.secho("You are not logged in.", fg="red")
|
|
108
|
+
return
|
|
109
|
+
|
|
110
|
+
click.echo(f"Attempting to export world for server '{server_name}'...")
|
|
111
|
+
try:
|
|
112
|
+
response = await client.async_export_server_world(server_name)
|
|
113
|
+
if response.task_id:
|
|
114
|
+
await monitor_task(
|
|
115
|
+
client,
|
|
116
|
+
response.task_id,
|
|
117
|
+
"World exported successfully",
|
|
118
|
+
"Failed to export world",
|
|
119
|
+
)
|
|
120
|
+
elif response.status == "success":
|
|
121
|
+
click.secho("World exported successfully.", fg="green")
|
|
122
|
+
else:
|
|
123
|
+
click.secho(f"Failed to export world: {response.message}", fg="red")
|
|
124
|
+
except Exception as e:
|
|
125
|
+
click.secho(f"An error occurred during export: {e}", fg="red")
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
@world.command("reset")
|
|
129
|
+
@click.option(
|
|
130
|
+
"-s",
|
|
131
|
+
"--server",
|
|
132
|
+
"server_name",
|
|
133
|
+
required=True,
|
|
134
|
+
help="Name of the server whose world to reset.",
|
|
135
|
+
)
|
|
136
|
+
@click.option("-y", "--yes", is_flag=True, help="Bypass the confirmation prompt.")
|
|
137
|
+
@pass_async_context
|
|
138
|
+
async def reset_world(ctx, server_name: str, yes: bool):
|
|
139
|
+
"""Deletes the current active world data for a server."""
|
|
140
|
+
client = ctx.obj.get("client")
|
|
141
|
+
if not client:
|
|
142
|
+
click.secho("You are not logged in.", fg="red")
|
|
143
|
+
return
|
|
144
|
+
|
|
145
|
+
if not yes:
|
|
146
|
+
click.secho(
|
|
147
|
+
f"WARNING: This will permanently delete the current world for server '{server_name}'.",
|
|
148
|
+
fg="red",
|
|
149
|
+
bold=True,
|
|
150
|
+
)
|
|
151
|
+
click.confirm(
|
|
152
|
+
"This action cannot be undone. Are you sure you want to reset the world?",
|
|
153
|
+
abort=True,
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
click.echo(f"Resetting world for server '{server_name}'...")
|
|
157
|
+
try:
|
|
158
|
+
response = await client.async_reset_server_world(server_name)
|
|
159
|
+
if response.task_id:
|
|
160
|
+
await monitor_task(
|
|
161
|
+
client,
|
|
162
|
+
response.task_id,
|
|
163
|
+
"World has been reset successfully",
|
|
164
|
+
"Failed to reset world",
|
|
165
|
+
)
|
|
166
|
+
elif response.status == "success":
|
|
167
|
+
click.secho("World has been reset successfully.", fg="green")
|
|
168
|
+
else:
|
|
169
|
+
click.secho(f"Failed to reset world: {response.message}", fg="red")
|
|
170
|
+
except Exception as e:
|
|
171
|
+
click.secho(f"An error occurred during reset: {e}", fg="red")
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: bsm-cli
|
|
3
|
+
Version: 1.6.0b3
|
|
4
|
+
Summary: A CLI for managing Bedrock servers via BSM API
|
|
5
|
+
Author-email: DMedina559 <dmedina559-github@outlook.com>
|
|
6
|
+
Project-URL: Homepage, https://github.com/DMedina559/bsm-api-client
|
|
7
|
+
Keywords: minecraft,bedrock,server,manager,cli
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Requires-Python: >=3.11
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
License-File: LICENSE
|
|
17
|
+
Requires-Dist: click<8.5,>=8.2.0
|
|
18
|
+
Requires-Dist: questionary<2.2,>=2.1.0
|
|
19
|
+
Requires-Dist: bsm-api-client
|
|
20
|
+
Provides-Extra: dev
|
|
21
|
+
Requires-Dist: pytest<9.2,>=8.4.0; extra == "dev"
|
|
22
|
+
Requires-Dist: pytest-mock<3.16,>=3.14.0; extra == "dev"
|
|
23
|
+
Requires-Dist: pytest-asyncio<1.5,>=1.1.0; extra == "dev"
|
|
24
|
+
Requires-Dist: black<26.6,>=25.1.0; extra == "dev"
|
|
25
|
+
Requires-Dist: flake8<7.4,>=7.3.0; extra == "dev"
|
|
26
|
+
Requires-Dist: isort<8.1,>=5.13.0; extra == "dev"
|
|
27
|
+
Requires-Dist: mypy<2.2,>=1.10.0; extra == "dev"
|
|
28
|
+
Requires-Dist: bedrock-server-manager<3.11.0,>=3.10.0b3; extra == "dev"
|
|
29
|
+
Requires-Dist: pre-commit<4.7,>=4.6.0; extra == "dev"
|
|
30
|
+
Dynamic: license-file
|
|
31
|
+
|
|
32
|
+
<div style="text-align: center;">
|
|
33
|
+
<img src="https://raw.githubusercontent.com/DMedina559/bsm-frontend/main/frontend/public/image/icon/favicon.svg" alt="BSM Logo" width="150">
|
|
34
|
+
</div>
|
|
35
|
+
|
|
36
|
+
# bsm-cli
|
|
37
|
+
|
|
38
|
+
<p align="center">
|
|
39
|
+
<a href="https://github.com/DMedina559/bsm-api-client/releases">
|
|
40
|
+
<img alt="Stable" src="https://img.shields.io/github/v/release/DMedina559/bsm-api-client?label=Stable&color=blue">
|
|
41
|
+
</a>
|
|
42
|
+
<a href="https://github.com/DMedina559/bsm-api-client/releases">
|
|
43
|
+
<img alt="Pre-Release" src="https://img.shields.io/github/v/release/DMedina559/bsm-api-client?include_prereleases&label=Pre-Release&color=red">
|
|
44
|
+
</a>
|
|
45
|
+
<a href="https://github.com/DMedina559/bsm-api-client/actions">
|
|
46
|
+
<img alt="Tests" src="https://img.shields.io/github/actions/workflow/status/DMedina559/bsm-api-client/build-test.yml?label=Tests&event=push">
|
|
47
|
+
</a>
|
|
48
|
+
</p>
|
|
49
|
+
|
|
50
|
+
## Introduction
|
|
51
|
+
|
|
52
|
+
`bsm-cli` is a command-line interface tool for managing Minecraft Bedrock Dedicated Servers via the Bedrock Server Manager API.
|
|
53
|
+
|
|
54
|
+
## Features
|
|
55
|
+
|
|
56
|
+
* Full CLI interface using `click` and `questionary`.
|
|
57
|
+
* Interactive menus for server management.
|
|
58
|
+
* Realtime server state updates via WebSocket.
|
|
59
|
+
* Seamlessly manages configuration for server and backups.
|
|
60
|
+
|
|
61
|
+
## Installation
|
|
62
|
+
|
|
63
|
+
Install the library using pip:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
pip install bsm-cli
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Quick Start
|
|
70
|
+
|
|
71
|
+
You can invoke the CLI using:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
bsm-cli
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Which will trigger the interactive menu allowing you to manage and configure your Bedrock Dedicated Servers.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
bsm_cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
bsm_cli/__main__.py,sha256=Jb5mbgqzvXtgoy2qW8yvNPqMnq7jbBJfPJurnOUxszc,2221
|
|
3
|
+
bsm_cli/account.py,sha256=xnaFNhuskKt3biYnwO6pePkGb_iYGKOE5PVsMD6SHu4,2156
|
|
4
|
+
bsm_cli/addon.py,sha256=xfq_zEfS5OreoRnhSLSmP5y_sx6zkCOqzLkdd-8KdKs,14072
|
|
5
|
+
bsm_cli/allowlist.py,sha256=fqyaa8YwsCV9KuI3Lh79vmCoUr5KqgFFbPd2skxx72k,7814
|
|
6
|
+
bsm_cli/auth.py,sha256=AJK7daic0TbM15GFn77bn9WdFuH14fZtR3nfLJRvLCM,3012
|
|
7
|
+
bsm_cli/backup.py,sha256=-GpRTX2AnEGYBVXxlciKKOMmjn6Y32Ei5y7g6JlFOrU,8792
|
|
8
|
+
bsm_cli/bans.py,sha256=9ohs2ofAvXT022hWyE5Ibbj6rrzELua88FGFFlp_KY0,6024
|
|
9
|
+
bsm_cli/config.py,sha256=iRrilB7b9g6P79O4pznBz--M3d8555j1qAQXsXn-jVI,2528
|
|
10
|
+
bsm_cli/content.py,sha256=F631Dpryi_UiFnkhCk-aVbrNlDyyaS7LjOCFunMgbQg,513
|
|
11
|
+
bsm_cli/decorators.py,sha256=9-bsWWCOUQUDrX7oRRD4gvzqKRPVOUXcSFgGrvxtM_U,5031
|
|
12
|
+
bsm_cli/main_menus.py,sha256=XuuTU49_Ypo1lT1AuUbwu643LDCyrIdSU3hCox5yjn4,10546
|
|
13
|
+
bsm_cli/permissions.py,sha256=5DQ0-TFERdwjDWBFPC8ZyEWLnsAPgjKO4vaxqSrb2zU,6356
|
|
14
|
+
bsm_cli/player.py,sha256=vQygt9j8SAgAZKydz5lWJkEiPWkLJ3u2hYGvBjQxdc4,1922
|
|
15
|
+
bsm_cli/plugins.py,sha256=gMEqRgX8pe_4fg6UtVLGtybLOmZu-CaJj_yC5xi-vTY,10400
|
|
16
|
+
bsm_cli/properties.py,sha256=eoGQE_-g3Y9GbM_nbb3hClH1SPd8poV6pWXERT2Kwe0,7021
|
|
17
|
+
bsm_cli/server.py,sha256=GfQ1fl-Z6u6409_HwUaS3jvEqbULLwqjqADxbDJ63Wc,17309
|
|
18
|
+
bsm_cli/system.py,sha256=y1XAbiWv0TaGpbE0LMfkMVfyY18L8RoMWVS3eijyLNM,4886
|
|
19
|
+
bsm_cli/users.py,sha256=lg2dkaUyay9F1rJnYY2XgLeW0MX9PI74vSTK3n4_sm0,9550
|
|
20
|
+
bsm_cli/world.py,sha256=AszZrZmWhJD_xioasZldxfLWfCxjqwLhn38KQhz0zgI,5653
|
|
21
|
+
bsm_cli-1.6.0b3.dist-info/licenses/LICENSE,sha256=dsRN6HXDmbpw1K4fTVXgOrzgsWZOjJ03O09ZkjQUR_A,1062
|
|
22
|
+
bsm_cli-1.6.0b3.dist-info/METADATA,sha256=BIdWVEXn7n94OsYZrKW8kEPil-mpucZNAGgxeH58i5g,2782
|
|
23
|
+
bsm_cli-1.6.0b3.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
24
|
+
bsm_cli-1.6.0b3.dist-info/entry_points.txt,sha256=VvvRZ2PKz2fxBr36ZXiG1Fu84KVMgAr7Vtsq8X3IQr4,49
|
|
25
|
+
bsm_cli-1.6.0b3.dist-info/top_level.txt,sha256=b9po-lHYQ4duM9sNLw8dxxhhj3ow8cTBHAy61cIs4Ys,8
|
|
26
|
+
bsm_cli-1.6.0b3.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Danny
|
|
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 @@
|
|
|
1
|
+
bsm_cli
|