update config to support multiple targets simultaneously
This commit is contained in:
parent
806966450b
commit
4b6e916425
4 changed files with 115 additions and 47 deletions
|
|
@ -8,9 +8,12 @@ class AppConfig(BaseModel):
|
|||
discord_server_id: Optional[str] = Field(default=None)
|
||||
tool_mode: str = Field(default="direct_transfer") # direct_transfer | backup_transfer | backup_only
|
||||
target_platform: str = Field(default="fluxer") # fluxer | stoat | none
|
||||
target_bot_token: Optional[str] = Field(default=None)
|
||||
target_server_id: Optional[str] = Field(default=None)
|
||||
target_api_url: Optional[str] = Field(default=None)
|
||||
fluxer_bot_token: Optional[str] = Field(default=None)
|
||||
fluxer_server_id: Optional[str] = Field(default=None)
|
||||
fluxer_api_url: Optional[str] = Field(default=None)
|
||||
stoat_bot_token: Optional[str] = Field(default=None)
|
||||
stoat_server_id: Optional[str] = Field(default=None)
|
||||
stoat_api_url: Optional[str] = Field(default=None)
|
||||
anonymize_users: bool = Field(default=False)
|
||||
log_level: str = Field(default="DEBUG")
|
||||
|
||||
|
|
@ -27,28 +30,20 @@ class AppConfig(BaseModel):
|
|||
return self.target_platform == "stoat"
|
||||
|
||||
@property
|
||||
def fluxer_bot_token(self) -> Optional[str]:
|
||||
return self.target_bot_token if self.target_platform == "fluxer" else None
|
||||
def target_bot_token(self) -> Optional[str]:
|
||||
return self.fluxer_bot_token if self.target_platform == "fluxer" else self.stoat_bot_token
|
||||
|
||||
@property
|
||||
def target_server_id(self) -> Optional[str]:
|
||||
return self.fluxer_server_id if self.target_platform == "fluxer" else self.stoat_server_id
|
||||
|
||||
@property
|
||||
def target_api_url(self) -> Optional[str]:
|
||||
return self.fluxer_api_url if self.target_platform == "fluxer" else self.stoat_api_url
|
||||
|
||||
@property
|
||||
def fluxer_community_id(self) -> Optional[str]:
|
||||
return self.target_server_id if self.target_platform == "fluxer" else None
|
||||
|
||||
@property
|
||||
def fluxer_api_url(self) -> Optional[str]:
|
||||
return self.target_api_url if self.target_platform == "fluxer" else None
|
||||
|
||||
@property
|
||||
def stoat_bot_token(self) -> Optional[str]:
|
||||
return self.target_bot_token if self.target_platform == "stoat" else None
|
||||
|
||||
@property
|
||||
def stoat_server_id(self) -> Optional[str]:
|
||||
return self.target_server_id if self.target_platform == "stoat" else None
|
||||
|
||||
@property
|
||||
def stoat_api_url(self) -> Optional[str]:
|
||||
return self.target_api_url if self.target_platform == "stoat" else None
|
||||
return self.fluxer_server_id
|
||||
|
||||
def load_config(config_path: Union[str, Path] = "config.yaml", create_if_missing: bool = True) -> AppConfig:
|
||||
path = Path(config_path)
|
||||
|
|
@ -67,22 +62,22 @@ def load_config(config_path: Union[str, Path] = "config.yaml", create_if_missing
|
|||
if not data:
|
||||
raise ValueError("Configuration file is empty or invalid YAML.")
|
||||
|
||||
# ── migrate legacy configs that still have separate fluxer/stoat fields ──
|
||||
if "fluxer_bot_token" in data or "stoat_bot_token" in data:
|
||||
if data.get("fluxer_bot_token") and data["fluxer_bot_token"] not in ("FLUXER_BOT_TOKEN", None):
|
||||
data.setdefault("target_platform", "fluxer")
|
||||
data.setdefault("target_bot_token", data["fluxer_bot_token"])
|
||||
data.setdefault("target_server_id", data.get("fluxer_community_id"))
|
||||
data.setdefault("target_api_url", data.get("fluxer_api_url") or "default")
|
||||
elif data.get("stoat_bot_token") and data["stoat_bot_token"] not in ("STOAT_BOT_TOKEN", None):
|
||||
data.setdefault("target_platform", "stoat")
|
||||
data.setdefault("target_bot_token", data["stoat_bot_token"])
|
||||
data.setdefault("target_server_id", data.get("stoat_server_id"))
|
||||
data.setdefault("target_api_url", data.get("stoat_api_url") or "default")
|
||||
# Remove legacy keys so they don't conflict with the model
|
||||
for key in ("fluxer_bot_token", "fluxer_community_id", "fluxer_api_url",
|
||||
"stoat_bot_token", "stoat_server_id", "stoat_api_url",
|
||||
"use_fluxer", "use_stoat"):
|
||||
# ── migrate legacy configs that used single target fields ──
|
||||
if "fluxer_community_id" in data:
|
||||
data.setdefault("fluxer_server_id", data.pop("fluxer_community_id"))
|
||||
|
||||
if "target_bot_token" in data or "target_server_id" in data:
|
||||
platform = data.get("target_platform", "fluxer")
|
||||
if platform == "fluxer":
|
||||
data.setdefault("fluxer_bot_token", data.get("target_bot_token"))
|
||||
data.setdefault("fluxer_server_id", data.get("target_server_id"))
|
||||
data.setdefault("fluxer_api_url", data.get("target_api_url"))
|
||||
elif platform == "stoat":
|
||||
data.setdefault("stoat_bot_token", data.get("target_bot_token"))
|
||||
data.setdefault("stoat_server_id", data.get("target_server_id"))
|
||||
data.setdefault("stoat_api_url", data.get("target_api_url"))
|
||||
|
||||
for key in ("target_bot_token", "target_server_id", "target_api_url", "use_fluxer", "use_stoat"):
|
||||
data.pop(key, None)
|
||||
|
||||
return AppConfig(**data)
|
||||
|
|
|
|||
|
|
@ -14,10 +14,9 @@ class MigrationDatabase:
|
|||
Replaces the memory-bloated and O(N^2) JSON persistence for messages.
|
||||
"""
|
||||
|
||||
_local = threading.local()
|
||||
|
||||
def __init__(self, db_path: Path):
|
||||
self.db_path = db_path
|
||||
self._local = threading.local()
|
||||
self._init_db()
|
||||
|
||||
def _get_conn(self) -> sqlite3.Connection:
|
||||
|
|
|
|||
|
|
@ -134,7 +134,7 @@ class ConfigSelectionScreen(Screen):
|
|||
yield Header(show_clock=True)
|
||||
with Center():
|
||||
with Container(id="config_sel_container"):
|
||||
yield Label(f"{get_app_version()} — Select Configuration", id="config_sel_title")
|
||||
yield Label(f"Reaper Configs", id="config_sel_title")
|
||||
with VerticalScroll(id="config_list_container"):
|
||||
yield ListView(id="config_list")
|
||||
with Horizontal(id="config_sel_actions"):
|
||||
|
|
@ -449,15 +449,35 @@ class ConfigScreen(Screen):
|
|||
coro = StoatWriter.fetch_guilds(token, api_url)
|
||||
else: return
|
||||
|
||||
# Use the platform-specific saved ID so we don't cross-contaminate
|
||||
if platform == "fluxer":
|
||||
saved_id = self.config.fluxer_server_id
|
||||
elif platform == "stoat":
|
||||
saved_id = self.config.stoat_server_id
|
||||
else:
|
||||
saved_id = None
|
||||
|
||||
await self._fetch_and_populate(
|
||||
coro,
|
||||
"#btn_fetch_target_servers",
|
||||
"#inp_target_server",
|
||||
f"No {platform} servers found.",
|
||||
self.config.target_server_id,
|
||||
saved_id,
|
||||
initial
|
||||
)
|
||||
|
||||
# Guard: if the user switched platforms while the fetch was in-flight,
|
||||
# discard the stale results so they don't contaminate the wrong platform.
|
||||
try:
|
||||
if self._get_selected_platform() != platform:
|
||||
select = self.query_one("#inp_target_server", Select)
|
||||
select.set_options([])
|
||||
select.value = Select.BLANK
|
||||
select.prompt = "Validate Bot Token"
|
||||
self.query_one("#btn_fetch_target_servers", Button).variant = "primary"
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def on_button_pressed(self, event: Button.Pressed) -> None:
|
||||
if event.button.id == "btn_fetch_guilds":
|
||||
|
|
@ -498,6 +518,30 @@ class ConfigScreen(Screen):
|
|||
def on_radio_set_changed(self, event: RadioSet.Changed) -> None:
|
||||
if event.radio_set.id == "mode_radio":
|
||||
self._toggle_target_section()
|
||||
elif event.radio_set.id == "plat_radio":
|
||||
plat = self._get_selected_platform()
|
||||
try:
|
||||
inp_token = self.query_one("#inp_target_token", Input)
|
||||
inp_api = self.query_one("#inp_target_api", Input)
|
||||
|
||||
if plat == "fluxer":
|
||||
inp_token.value = self.config.fluxer_bot_token or ""
|
||||
api_val = self.config.fluxer_api_url
|
||||
inp_api.value = api_val if (api_val and api_val != "default") else ""
|
||||
elif plat == "stoat":
|
||||
inp_token.value = self.config.stoat_bot_token or ""
|
||||
api_val = self.config.stoat_api_url
|
||||
inp_api.value = api_val if (api_val and api_val != "default") else ""
|
||||
|
||||
select = self.query_one("#inp_target_server", Select)
|
||||
select.set_options([])
|
||||
select.value = Select.BLANK
|
||||
select.prompt = "Validate Bot Token"
|
||||
self.query_one("#btn_fetch_target_servers", Button).variant = "primary"
|
||||
|
||||
if inp_token.value:
|
||||
self.run_worker(self._do_fetch_target_servers(initial=True))
|
||||
except Exception: pass
|
||||
|
||||
# ── save / start ─────────────────────────────────────────────────────
|
||||
|
||||
|
|
@ -514,15 +558,26 @@ class ConfigScreen(Screen):
|
|||
|
||||
# 3. Target Section
|
||||
if self.config.tool_mode != "backup_only":
|
||||
self.config.target_platform = self._get_selected_platform()
|
||||
self.config.target_bot_token = self.query_one("#inp_target_token", Input).value.strip() or None
|
||||
plat = self._get_selected_platform()
|
||||
self.config.target_platform = plat
|
||||
token_val = self.query_one("#inp_target_token", Input).value.strip() or None
|
||||
|
||||
t_select = self.query_one("#inp_target_server", Select)
|
||||
if t_select.value not in (Select.BLANK, Select.NULL):
|
||||
self.config.target_server_id = str(t_select.value)
|
||||
|
||||
target_api = self.query_one("#inp_target_api", Input).value.strip()
|
||||
self.config.target_api_url = target_api or None
|
||||
api_val = target_api or None
|
||||
|
||||
if plat == "fluxer":
|
||||
self.config.fluxer_bot_token = token_val
|
||||
# Only update server_id if user actually selected something
|
||||
if t_select.value not in (Select.BLANK, Select.NULL):
|
||||
self.config.fluxer_server_id = str(t_select.value)
|
||||
self.config.fluxer_api_url = api_val
|
||||
elif plat == "stoat":
|
||||
self.config.stoat_bot_token = token_val
|
||||
if t_select.value not in (Select.BLANK, Select.NULL):
|
||||
self.config.stoat_server_id = str(t_select.value)
|
||||
self.config.stoat_api_url = api_val
|
||||
|
||||
self.config.anonymize_users = self.query_one("#inp_anonymize_users", Switch).value
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -339,6 +339,25 @@ class OperationPane(Container):
|
|||
|
||||
@work(exclusive=True)
|
||||
async def run_validate(self) -> None:
|
||||
try:
|
||||
plat = "Fluxer" if self.target_platform == "fluxer" else "Stoat"
|
||||
self.query_one("#op_lbl_t_header", Label).update(plat)
|
||||
self.query_one("#op_lbl_d_server", Label).update("Server: [yellow]Validating...[/yellow]")
|
||||
self.query_one("#op_lbl_d_bot", Label).update("Source: [yellow]Validating...[/yellow]" if self.view_mode == "backup" else "Bot: [yellow]Validating...[/yellow]")
|
||||
self.query_one("#op_lbl_d_status", Label).update("Status: [yellow]Validating...[/yellow]")
|
||||
self.query_one("#op_lbl_t_comm", Label).update("Community: [yellow]Validating...[/yellow]")
|
||||
self.query_one("#op_lbl_t_bot", Label).update("Bot: [yellow]Validating...[/yellow]")
|
||||
self.query_one("#op_lbl_t_status", Label).update("Status: [yellow]Validating...[/yellow]")
|
||||
# Disable all operation buttons while validation is in progress
|
||||
if self.view_mode == "shuttle":
|
||||
for bid in ("#op_clone", "#op_sync", "#op_messages", "#op_danger"):
|
||||
self.query_one(bid, Button).disabled = True
|
||||
elif self.view_mode == "backup":
|
||||
for bid in ("#op_backup_msgs", "#op_backup_sync"):
|
||||
self.query_one(bid, Button).disabled = True
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
self.validation_results = {
|
||||
"discord_token": False, "discord_bot_name": None,
|
||||
"discord_server": False, "discord_server_name": None,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue