improve startup behaviour

This commit is contained in:
rambros 2026-02-24 21:53:44 +05:30
parent 478fa85b32
commit 7267253f8b
3 changed files with 128 additions and 28 deletions

View file

@ -1,18 +1,71 @@
import sys import sys
import asyncio import asyncio
import logging import logging
from pathlib import Path
from src.ui.app import run_cli from src.ui.app import run_cli
from src.core.configuration import load_config from src.core.configuration import load_config
def setup_logging(): def select_config():
from rich.console import Console
from rich.prompt import Prompt
from rich.panel import Panel
console = Console()
fluxer_exists = Path("fluxer.config.yaml").exists()
stoat_exists = Path("stoat.config.yaml").exists()
if fluxer_exists and not stoat_exists:
return "fluxer.config.yaml"
elif stoat_exists and not fluxer_exists:
return "stoat.config.yaml"
elif fluxer_exists and stoat_exists:
console.print(Panel.fit(
"[bold]Both Fluxer and Stoat configurations found.[/bold]\n"
"Which one do you want to use?",
title="[bold cyan]Configuration Selection[/bold cyan]"
))
console.print("(1) [bold blue]Fluxer[/bold blue]")
console.print("(2) [bold red]Stoat[/bold red]")
console.print("(Q) [bold dim]Quit[/bold dim]")
console.print("")
choice = Prompt.ask("Select an option [[bold cyan]1/2/Q[/bold cyan]]", choices=["1", "2", "Q", "q"], show_choices=False).upper()
if choice == "1":
return "fluxer.config.yaml"
elif choice == "2":
return "stoat.config.yaml"
else:
sys.exit(0)
else:
console.print(Panel.fit(
"[bold]First setup, Tool configuration[/bold]\n"
"Which platform do you want to migrate to?",
title="[bold cyan]Initial Setup[/bold cyan]"
))
console.print("(1) [bold blue]Fluxer[/bold blue]")
console.print("(2) [bold red]Stoat[/bold red]")
console.print("(Q) [bold dim]Quit[/bold dim]")
console.print("")
choice = Prompt.ask("Select an option [[bold cyan]1/2/Q[/bold cyan]]", choices=["1", "2", "Q", "q"], show_choices=False).upper()
if choice == "1":
return "fluxer.config.yaml"
elif choice == "2":
return "stoat.config.yaml"
else:
sys.exit(0)
def setup_logging(config_path):
try: try:
config = load_config() config = load_config(config_path)
log_level_str = config.migration.log_level.upper() log_level_str = config.migration.log_level.upper()
level = getattr(logging, log_level_str, logging.INFO) level = getattr(logging, log_level_str, logging.INFO)
except Exception: except Exception:
level = logging.INFO level = logging.INFO
handlers = [logging.FileHandler('fluxer.migration.log', mode='a')] platform = config_path.split('.')[0]
handlers = [logging.FileHandler(f'{platform}.migration.log', mode='a')]
if level == logging.DEBUG: if level == logging.DEBUG:
handlers.append(logging.StreamHandler(sys.stdout)) handlers.append(logging.StreamHandler(sys.stdout))
@ -87,9 +140,10 @@ def relaunch_in_terminal():
def main(): def main():
relaunch_in_terminal() relaunch_in_terminal()
setup_logging() config_path = select_config()
setup_logging(config_path)
try: try:
asyncio.run(run_cli()) asyncio.run(run_cli(config_path))
except KeyboardInterrupt: except KeyboardInterrupt:
print("\nOperation terminated by user.") print("\nOperation terminated by user.")
sys.exit(0) sys.exit(0)

View file

@ -55,9 +55,10 @@ console = Console()
class MigrationCLI: class MigrationCLI:
"""Standard CLI app to manage the Discord to Fluxer migration.""" """Standard CLI app to manage the Discord to Fluxer migration."""
def __init__(self): def __init__(self, config_path="fluxer.config.yaml"):
self.config_path = config_path
try: try:
self.config = load_config() self.config = load_config(self.config_path)
except Exception as e: except Exception as e:
console.print(f"[bold red]Failed to load config: {e}[/bold red]") console.print(f"[bold red]Failed to load config: {e}[/bold red]")
sys.exit(1) sys.exit(1)
@ -83,20 +84,38 @@ class MigrationCLI:
} }
self.tokens_valid = False self.tokens_valid = False
discord_task = asyncio.create_task(self.engine.discord_reader.validate()) d_token = self.config.discord_bot_token
fluxer_task = asyncio.create_task(self.engine.fluxer_writer.validate()) f_token = self.config.fluxer_bot_token
discord_dummy = d_token in ["YOUR_DISCORD_TOKEN", "DISCORD_BOT_TOKEN", ""]
fluxer_dummy = f_token in ["YOUR_FLUXER_TOKEN", "FLUXER_BOT_TOKEN", "YOUR_STOAT_TOKEN", "STOAT_BOT_TOKEN", ""]
discord_task = None
if not discord_dummy:
discord_task = asyncio.create_task(self.engine.discord_reader.validate())
fluxer_task = None
if not fluxer_dummy:
fluxer_task = asyncio.create_task(self.engine.fluxer_writer.validate())
tasks_to_wait = [t for t in [discord_task, fluxer_task] if t is not None]
try: try:
with console.status("[yellow]Validating tokens...[/yellow]"): with console.status("[yellow]Validating tokens...[/yellow]"):
start_time = asyncio.get_event_loop().time() start_time = asyncio.get_event_loop().time()
done, pending = await asyncio.wait( done = set()
[discord_task, fluxer_task], pending = set()
timeout=10.0, if tasks_to_wait:
return_when=asyncio.ALL_COMPLETED done, pending = await asyncio.wait(
) tasks_to_wait,
timeout=10.0,
return_when=asyncio.ALL_COMPLETED
)
# Process Discord Result # Process Discord Result
if discord_task in done: if discord_dummy:
console.print("[bold yellow]Discord setup incomplete (Using default token).[/bold yellow]")
elif discord_task in done:
try: try:
res = discord_task.result() res = discord_task.result()
self.validation_results["discord_token"] = res.get("token", False) self.validation_results["discord_token"] = res.get("token", False)
@ -120,7 +139,9 @@ class MigrationCLI:
discord_task.cancel() discord_task.cancel()
# Process Fluxer Result # Process Fluxer Result
if fluxer_task in done: if fluxer_dummy:
console.print("[bold yellow]Target Platform setup incomplete (Using default token).[/bold yellow]")
elif fluxer_task in done:
try: try:
res = fluxer_task.result() res = fluxer_task.result()
self.validation_results["fluxer_token"] = res.get("token", False) self.validation_results["fluxer_token"] = res.get("token", False)
@ -169,11 +190,26 @@ class MigrationCLI:
finally: finally:
# Ensure tasks are cleaned up # Ensure tasks are cleaned up
for t in [discord_task, fluxer_task]: for t in [discord_task, fluxer_task]:
if not t.done(): t.cancel() if t is not None and not t.done(): t.cancel()
async def run(self): async def run(self):
await self.validate_config() if self.config.discord_bot_token == "YOUR_DISCORD_TOKEN":
console.print("\n[bold yellow]First time setup detected. Redirecting to configuration...[/bold yellow]")
self.validation_results = {
"discord_token": False, "discord_bot_name": None,
"discord_server": False, "discord_server_name": None,
"discord_intents": {}, "discord_permissions": {},
"fluxer_token": False, "fluxer_bot_name": None,
"fluxer_community": False, "fluxer_community_name": None,
"fluxer_permissions": {},
"discord_timeout": False, "fluxer_timeout": False
}
self.tokens_valid = False
self.permissions_complete = False
await self.edit_configuration(skip_validation=True)
else:
await self.validate_config()
while True: while True:
console.print("") console.print("")
console.print(Panel.fit("Fluxer Reaper", style="bold blue")) console.print(Panel.fit("Fluxer Reaper", style="bold blue"))
@ -225,8 +261,9 @@ class MigrationCLI:
await self.engine.close_connections() await self.engine.close_connections()
break break
async def edit_configuration(self): async def edit_configuration(self, skip_validation=False):
await self.validate_config() if not skip_validation:
await self.validate_config()
# Display Required Permissions FIRST # Display Required Permissions FIRST
def fmt(name, val): def fmt(name, val):
@ -264,8 +301,8 @@ class MigrationCLI:
return status return status
console.print(f"Discord Bot Token {get_status_str(self.validation_results.get('discord_token', False), self.validation_results.get('discord_bot_name'))}") console.print(f"Discord Bot Token {get_status_str(self.validation_results.get('discord_token', False), self.validation_results.get('discord_bot_name'))}")
console.print(f"Fluxer Bot Token {get_status_str(self.validation_results.get('fluxer_token', False), self.validation_results.get('fluxer_bot_name'))}")
console.print(f"Discord Server ID {get_status_str(self.validation_results.get('discord_server', False), self.validation_results.get('discord_server_name'))}") console.print(f"Discord Server ID {get_status_str(self.validation_results.get('discord_server', False), self.validation_results.get('discord_server_name'))}")
console.print(f"Fluxer Bot Token {get_status_str(self.validation_results.get('fluxer_token', False), self.validation_results.get('fluxer_bot_name'))}")
console.print(f"Fluxer Community ID {get_status_str(self.validation_results.get('fluxer_community', False), self.validation_results.get('fluxer_community_name'))}") console.print(f"Fluxer Community ID {get_status_str(self.validation_results.get('fluxer_community', False), self.validation_results.get('fluxer_community_name'))}")
console.print("\n(1) Edit tokens") console.print("\n(1) Edit tokens")
@ -284,8 +321,8 @@ class MigrationCLI:
console.print("\n[bold]Configuration Editor[/bold] (leave blank to keep current)") console.print("\n[bold]Configuration Editor[/bold] (leave blank to keep current)")
d_token = Prompt.ask("Discord Bot Token", default=self.config.discord_bot_token) d_token = Prompt.ask("Discord Bot Token", default=self.config.discord_bot_token)
f_token = Prompt.ask("Fluxer Bot Token", default=self.config.fluxer_bot_token)
d_server = Prompt.ask("Discord Server ID", default=self.config.discord_server_id) d_server = Prompt.ask("Discord Server ID", default=self.config.discord_server_id)
f_token = Prompt.ask("Fluxer Bot Token", default=self.config.fluxer_bot_token)
f_comm = Prompt.ask("Fluxer Community ID", default=self.config.fluxer_community_id) f_comm = Prompt.ask("Fluxer Community ID", default=self.config.fluxer_community_id)
# Only rewrite if changed # Only rewrite if changed
@ -299,7 +336,7 @@ class MigrationCLI:
self.config.discord_server_id = d_server self.config.discord_server_id = d_server
self.config.fluxer_community_id = f_comm self.config.fluxer_community_id = f_comm
save_config(self.config) save_config(self.config, self.config_path)
# Recreate engine with new config # Recreate engine with new config
self.engine = MigrationContext(self.config) self.engine = MigrationContext(self.config)
@ -308,11 +345,11 @@ class MigrationCLI:
await self.update_validation_status() await self.update_validation_status()
console.print(f"\nDiscord Bot Token {get_status_str(self.validation_results.get('discord_token', False))}") console.print(f"\nDiscord Bot Token {get_status_str(self.validation_results.get('discord_token', False))}")
console.print(f"Fluxer Bot Token {get_status_str(self.validation_results.get('fluxer_token', False))}")
console.print(f"Discord Server ID {get_status_str(self.validation_results.get('discord_server', False))}") console.print(f"Discord Server ID {get_status_str(self.validation_results.get('discord_server', False))}")
console.print(f"Fluxer Bot Token {get_status_str(self.validation_results.get('fluxer_token', False))}")
console.print(f"Fluxer Community ID {get_status_str(self.validation_results.get('fluxer_community', False))}") console.print(f"Fluxer Community ID {get_status_str(self.validation_results.get('fluxer_community', False))}")
console.print("[bold green]Configuration updated and saved to fluxer.config.yaml![/bold green]") console.print(f"[bold green]Configuration updated and saved to {self.config_path}![/bold green]")
else: else:
console.print("[yellow]No changes made.[/yellow]") console.print("[yellow]No changes made.[/yellow]")
@ -1290,6 +1327,6 @@ class MigrationCLI:
async def run_cli(): async def run_cli(config_path="fluxer.config.yaml"):
cli = MigrationCLI() cli = MigrationCLI(config_path)
await cli.run() await cli.run()

View file

@ -0,0 +1,9 @@
discord_bot_token: DISCORD_BOT_TOKEN # Token used to connect the Discord Bot
discord_server_id: 'DISCORD_SERVER_ID' # ID of the source Discord Server
stoat_bot_token: STOAT_BOT_TOKEN # Token used to connect the Stoat Bot
stoat_server_id: 'STOAT_SERVER_ID' # ID of the target Stoat Server
stoat_api_url: 'default' # URL of the Stoat API (default is https://api.stoat.chat)
migration:
batch_size: 50
rate_limit_delay_seconds: 2
log_level: INFO