mirror of
https://github.com/bombsquad-community/plugin-manager.git
synced 2025-10-08 14:54:36 +00:00
Merge pull request #395 from Loup-Garou911XD/main
added account switcher plugin
This commit is contained in:
commit
064c02e72e
2 changed files with 329 additions and 0 deletions
|
|
@ -2283,6 +2283,25 @@
|
||||||
"md5sum": "7d1f771d40103c4cd3f9719353516ba7"
|
"md5sum": "7d1f771d40103c4cd3f9719353516ba7"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"account_switcher": {
|
||||||
|
"description": "Switch between multiple accounts",
|
||||||
|
"external_url": "",
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "LoupGarou",
|
||||||
|
"email": "LoupGarou5418@outlook.com",
|
||||||
|
"discord": "loupgarou_"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"versions": {
|
||||||
|
"1.0.0": {
|
||||||
|
"api_version": 9,
|
||||||
|
"commit_sha": "f62ed2a",
|
||||||
|
"released_on": "06-09-2025",
|
||||||
|
"md5sum": "01c6cb1a3d3b525c87caed389c8d03ed"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
310
plugins/utilities/account_switcher.py
Normal file
310
plugins/utilities/account_switcher.py
Normal file
|
|
@ -0,0 +1,310 @@
|
||||||
|
""" Plugin by LoupGarou a.k.a Loup/Soup
|
||||||
|
Discord → loupgarou_
|
||||||
|
Switch between multiple accounts in easily
|
||||||
|
|
||||||
|
Feel free to let me know if you use this plugin,i love to hear that :)
|
||||||
|
|
||||||
|
Message me in discord if you find some bug
|
||||||
|
Use this code for your experiments or plugin but please dont rename this plugin and distribute with your name
|
||||||
|
"""
|
||||||
|
|
||||||
|
# ba_meta require api 9
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
import babase
|
||||||
|
import bauiv1 as bui
|
||||||
|
from bauiv1lib.confirm import ConfirmWindow
|
||||||
|
from bauiv1lib.account.settings import AccountSettingsWindow
|
||||||
|
from os import listdir, path, mkdir, remove
|
||||||
|
from shutil import copy, rmtree
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
UI_SCALE = 2.0 if (babase.app.ui_v1.uiscale == babase.UIScale.SMALL) else 1.0
|
||||||
|
|
||||||
|
ACCOUNT_FILES = ['.bsac2', '.bsuuid', 'config.json', '.config_prev.json']
|
||||||
|
plus = babase.app.plus
|
||||||
|
env = babase.app.env
|
||||||
|
USER_DIR = path.dirname(env.config_file_path)
|
||||||
|
ACCOUNTS_DIR = path.join(USER_DIR, 'account_switcher_profiles')
|
||||||
|
|
||||||
|
if not path.exists(ACCOUNTS_DIR):
|
||||||
|
mkdir(ACCOUNTS_DIR)
|
||||||
|
|
||||||
|
|
||||||
|
def print_msg(text: str, color=(0.3, 1, 0.3)):
|
||||||
|
bui.screenmessage(text, color=color)
|
||||||
|
|
||||||
|
|
||||||
|
class AccountSwitcherUI(bui.Window):
|
||||||
|
def __init__(self):
|
||||||
|
# Base dimensions; the final size is controlled by the scale property.
|
||||||
|
self._width = 600
|
||||||
|
self._height = 400
|
||||||
|
|
||||||
|
self._root_widget = bui.containerwidget(
|
||||||
|
size=(self._width, self._height),
|
||||||
|
scale=UI_SCALE, # Apply the global scale here
|
||||||
|
transition='in_right',
|
||||||
|
stack_offset=(0, 0)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Standard back/close button
|
||||||
|
self._back_button = bui.buttonwidget(
|
||||||
|
parent=self._root_widget,
|
||||||
|
position=(40, self._height - 60),
|
||||||
|
size=(40, 40),
|
||||||
|
scale=1.0,
|
||||||
|
label=babase.charstr(babase.SpecialChar.BACK),
|
||||||
|
button_type='backSmall',
|
||||||
|
on_activate_call=self._close,
|
||||||
|
)
|
||||||
|
bui.containerwidget(edit=self._root_widget, cancel_button=self._back_button)
|
||||||
|
|
||||||
|
# Title
|
||||||
|
bui.textwidget(
|
||||||
|
parent=self._root_widget,
|
||||||
|
position=(self._width * 0.5, self._height - 40),
|
||||||
|
size=(0, 0),
|
||||||
|
h_align='center',
|
||||||
|
v_align='center',
|
||||||
|
text='Account Switcher',
|
||||||
|
scale=1.5,
|
||||||
|
color=(0.8, 0.8, 0.9),
|
||||||
|
)
|
||||||
|
|
||||||
|
content_height = self._height - 80
|
||||||
|
content_y = 20
|
||||||
|
|
||||||
|
# Buttons stacked on the right
|
||||||
|
btn_col_width = 180
|
||||||
|
btn_col_x = self._width - btn_col_width - 20
|
||||||
|
btn_width = 160
|
||||||
|
btn_height = 60
|
||||||
|
btn_x = btn_col_x + (btn_col_width - btn_width) * 0.5
|
||||||
|
v_pos = content_y + content_height - btn_height
|
||||||
|
v_step = btn_height + 10
|
||||||
|
|
||||||
|
bui.buttonwidget(
|
||||||
|
parent=self._root_widget,
|
||||||
|
position=(btn_x, v_pos),
|
||||||
|
size=(btn_width, btn_height),
|
||||||
|
label='Save Current',
|
||||||
|
on_activate_call=self.save_current_account,
|
||||||
|
)
|
||||||
|
v_pos -= v_step
|
||||||
|
|
||||||
|
bui.buttonwidget(
|
||||||
|
parent=self._root_widget,
|
||||||
|
position=(btn_x, v_pos),
|
||||||
|
size=(btn_width, btn_height),
|
||||||
|
label='Add New Account',
|
||||||
|
on_activate_call=self.add_new_account,
|
||||||
|
)
|
||||||
|
v_pos -= v_step
|
||||||
|
|
||||||
|
bui.buttonwidget(
|
||||||
|
parent=self._root_widget,
|
||||||
|
position=(btn_x, v_pos),
|
||||||
|
size=(btn_width, btn_height),
|
||||||
|
label='Load Selected',
|
||||||
|
on_activate_call=self.load_selected_account,
|
||||||
|
)
|
||||||
|
v_pos -= v_step
|
||||||
|
|
||||||
|
bui.buttonwidget(
|
||||||
|
parent=self._root_widget,
|
||||||
|
position=(btn_x, v_pos),
|
||||||
|
size=(btn_width, btn_height),
|
||||||
|
label='Delete Selected',
|
||||||
|
on_activate_call=self.delete_selected_account,
|
||||||
|
)
|
||||||
|
|
||||||
|
# List box on the left
|
||||||
|
self.list_width = btn_col_x - 30
|
||||||
|
list_x = 20
|
||||||
|
|
||||||
|
scroll = bui.scrollwidget(
|
||||||
|
parent=self._root_widget,
|
||||||
|
position=(list_x, content_y),
|
||||||
|
size=(self.list_width, content_height),
|
||||||
|
)
|
||||||
|
bui.containerwidget(edit=scroll, claims_left_right=True)
|
||||||
|
|
||||||
|
self._list = bui.columnwidget(
|
||||||
|
parent=scroll,
|
||||||
|
background=False,
|
||||||
|
border=0,
|
||||||
|
)
|
||||||
|
|
||||||
|
self._selected_profile: Optional[str] = None
|
||||||
|
self._profile_widgets: list[bui.Widget] = []
|
||||||
|
|
||||||
|
self._refresh_account_list()
|
||||||
|
|
||||||
|
def _close(self) -> None:
|
||||||
|
bui.containerwidget(edit=self._root_widget, transition='out_right')
|
||||||
|
|
||||||
|
def _refresh_account_list(self):
|
||||||
|
for widget in self._profile_widgets:
|
||||||
|
widget.delete()
|
||||||
|
self._profile_widgets = []
|
||||||
|
|
||||||
|
profiles = sorted(listdir(ACCOUNTS_DIR))
|
||||||
|
for prof in profiles:
|
||||||
|
text_widget = bui.textwidget(
|
||||||
|
parent=self._list,
|
||||||
|
text=prof,
|
||||||
|
size=(self.list_width, 30),
|
||||||
|
color=(1, 1, 1),
|
||||||
|
selectable=True,
|
||||||
|
click_activate=True,
|
||||||
|
max_chars=40,
|
||||||
|
corner_scale=1.2,
|
||||||
|
)
|
||||||
|
self._profile_widgets.append(text_widget)
|
||||||
|
bui.textwidget(
|
||||||
|
edit=text_widget, on_activate_call=babase.Call(
|
||||||
|
self.on_select_profile, prof, text_widget)
|
||||||
|
)
|
||||||
|
|
||||||
|
def on_select_profile(self, profile_name: str, selected_widget: bui.Widget):
|
||||||
|
self._selected_profile = profile_name
|
||||||
|
for widget in self._profile_widgets:
|
||||||
|
bui.textwidget(edit=widget, color=(1, 1, 1))
|
||||||
|
bui.textwidget(edit=selected_widget, color=(1, 1, 0.2))
|
||||||
|
|
||||||
|
def get_current_account(self) -> Optional[str]:
|
||||||
|
if plus.get_v1_account_state() == 'signed_in':
|
||||||
|
return plus.get_v1_account_display_string()
|
||||||
|
return None
|
||||||
|
|
||||||
|
def save_current_account(self):
|
||||||
|
name = self.get_current_account()
|
||||||
|
if not name:
|
||||||
|
print_msg("No account signed in!", color=(1, 0, 0))
|
||||||
|
return
|
||||||
|
|
||||||
|
account_folder = path.join(ACCOUNTS_DIR, name)
|
||||||
|
if not path.exists(account_folder):
|
||||||
|
mkdir(account_folder)
|
||||||
|
|
||||||
|
for fname in ACCOUNT_FILES:
|
||||||
|
src = path.join(USER_DIR, fname)
|
||||||
|
if path.exists(src):
|
||||||
|
try:
|
||||||
|
copy(src, path.join(account_folder, fname))
|
||||||
|
except IOError as e:
|
||||||
|
print_msg(f"Error saving {fname}: {e}", color=(1, 0, 0))
|
||||||
|
|
||||||
|
print_msg(f"Saved current account as '{name}'")
|
||||||
|
self._refresh_account_list()
|
||||||
|
|
||||||
|
def add_new_account(self) -> None:
|
||||||
|
def do_action():
|
||||||
|
self.save_current_account()
|
||||||
|
for fname in ACCOUNT_FILES:
|
||||||
|
file_path = path.join(USER_DIR, fname)
|
||||||
|
if path.exists(file_path):
|
||||||
|
remove(file_path)
|
||||||
|
print_msg('Account files removed.')
|
||||||
|
|
||||||
|
ConfirmWindow(
|
||||||
|
text='This will save your current login and then shutdown the game.\nAre you sure?',
|
||||||
|
action=lambda: self.lock_call_exit(do_action),
|
||||||
|
ok_text='Confirm & Logout',
|
||||||
|
cancel_is_selected=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
def lock_call_exit(self, callable_action):
|
||||||
|
babase.suppress_config_and_state_writes()
|
||||||
|
callable_action()
|
||||||
|
babase.apptimer(1.5, babase.quit)
|
||||||
|
|
||||||
|
def load_selected_account(self):
|
||||||
|
if not self._selected_profile:
|
||||||
|
print_msg("No account selected to load!", color=(1, 0, 0))
|
||||||
|
return
|
||||||
|
account_folder = path.join(ACCOUNTS_DIR, self._selected_profile)
|
||||||
|
|
||||||
|
def do_switch():
|
||||||
|
for fname in ACCOUNT_FILES:
|
||||||
|
dest = path.join(USER_DIR, fname)
|
||||||
|
src = path.join(account_folder, fname)
|
||||||
|
if path.exists(dest):
|
||||||
|
remove(dest)
|
||||||
|
if path.exists(src):
|
||||||
|
copy(src, dest)
|
||||||
|
print_msg(f"Loaded account {self._selected_profile}")
|
||||||
|
|
||||||
|
ConfirmWindow(
|
||||||
|
text=f"Load account {self._selected_profile}?\nGame will shut down.",
|
||||||
|
action=lambda: self.lock_call_exit(do_switch),
|
||||||
|
cancel_is_selected=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
def delete_selected_account(self):
|
||||||
|
if not self._selected_profile:
|
||||||
|
print_msg("No account selected to delete!", color=(1, 0, 0))
|
||||||
|
return
|
||||||
|
account_folder = path.join(ACCOUNTS_DIR, self._selected_profile)
|
||||||
|
|
||||||
|
def do_delete():
|
||||||
|
if path.exists(account_folder):
|
||||||
|
rmtree(account_folder)
|
||||||
|
print_msg(f"Deleted account '{self._selected_profile}'", color=(1, 0.5, 0.5))
|
||||||
|
self._selected_profile = None
|
||||||
|
self._refresh_account_list()
|
||||||
|
|
||||||
|
ConfirmWindow(
|
||||||
|
text=f"Delete account '{self._selected_profile}' permanently?",
|
||||||
|
action=do_delete,
|
||||||
|
cancel_is_selected=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# --- Monkey-Patching ---
|
||||||
|
_original_account_settings_init = AccountSettingsWindow.__init__
|
||||||
|
_original_on_adapter_sign_in_result = AccountSettingsWindow._on_adapter_sign_in_result
|
||||||
|
|
||||||
|
|
||||||
|
def new_account_settings_init(self, *args, **kwargs):
|
||||||
|
_original_account_settings_init(self, *args, **kwargs)
|
||||||
|
button_width = 350
|
||||||
|
# Use a lambda to create an instance of the class when the button is pressed.
|
||||||
|
bui.buttonwidget(
|
||||||
|
parent=self._subcontainer,
|
||||||
|
position=((self._sub_width - button_width) * 0.5, -25),
|
||||||
|
size=(button_width, 60),
|
||||||
|
label='Switch Accounts...',
|
||||||
|
on_activate_call=lambda: AccountSwitcherUI()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def new_on_adapter_sign_in_result(self, result: str) -> None:
|
||||||
|
# First, call the original method to ensure default behavior runs.
|
||||||
|
_original_on_adapter_sign_in_result(self, result)
|
||||||
|
print(result)
|
||||||
|
# Now, "capture" the result with our custom logic.
|
||||||
|
if result == 'success':
|
||||||
|
print_msg('Sign-in Successful!', color=(0, 1, 0))
|
||||||
|
# You could add other logic here, like automatically saving the new account.
|
||||||
|
elif result != 'cancel': # Don't show a message on user cancellation.
|
||||||
|
print_msg(f'Sign-in failed: {result}', color=(1, 0, 0))
|
||||||
|
|
||||||
|
# ba_meta export babase.Plugin
|
||||||
|
|
||||||
|
|
||||||
|
class EntryPoint(babase.Plugin):
|
||||||
|
def on_app_running(self):
|
||||||
|
# Apply both monkey-patches when the app runs.
|
||||||
|
AccountSettingsWindow.__init__ = new_account_settings_init
|
||||||
|
AccountSettingsWindow._on_adapter_sign_in_result = new_on_adapter_sign_in_result
|
||||||
|
|
||||||
|
def has_settings_ui(self):
|
||||||
|
return True
|
||||||
|
|
||||||
|
def show_settings_ui(self, button=None):
|
||||||
|
AccountSwitcherUI()
|
||||||
Loading…
Add table
Add a link
Reference in a new issue