From 8513d4b96f0a2fa7a6096b25220ad355cd5b01f7 Mon Sep 17 00:00:00 2001 From: Kingston-SCYD Date: Fri, 5 Jun 2026 19:09:36 -0500 Subject: [PATCH] Add morshu server start/stop/status bot commands --- ttstoy/ttstoy.py | 101 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 100 insertions(+), 1 deletion(-) diff --git a/ttstoy/ttstoy.py b/ttstoy/ttstoy.py index 3a063ce..0212d11 100644 --- a/ttstoy/ttstoy.py +++ b/ttstoy/ttstoy.py @@ -314,8 +314,11 @@ class TtsToy(Cog): self.dectalk_process = None self.dectalk_port = 3001 - # Morshu TTS engine + # Morshu TTS engine + server self.morshu = Morshu() if MORSHU_AVAILABLE else None + self.morshu_dir = Path(__file__).resolve().parent / "morshu-server" + self.morshu_process = None + self.morshu_port = 3002 # TTS queue system - one queue per guild self.tts_queues = {} # guild_id -> asyncio.Queue @@ -512,6 +515,51 @@ class TtsToy(Cog): except Exception as e: log.exception(f"Error stopping DECTalk server: {e}") + async def _start_morshu_server(self) -> bool: + """Start the built-in Morshu TTS server.""" + try: + if self.morshu_process and self.morshu_process.poll() is None: + log.info("Morshu server already running") + return True + + server_py = self.morshu_dir / "server.py" + if not server_py.exists(): + log.error(f"Morshu server not found: {server_py}") + return False + + self.morshu_process = await asyncio.to_thread( + subprocess.Popen, + ["python3", "server.py"], + cwd=str(self.morshu_dir), + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True + ) + + await asyncio.sleep(3) + + if self.morshu_process.poll() is not None: + log.error("Morshu server failed to start") + return False + + log.info(f"Morshu server started on port {self.morshu_port}") + return True + except Exception as e: + log.exception(f"Failed to start Morshu server: {e}") + return False + + async def _stop_morshu_server(self): + """Stop the built-in Morshu server.""" + if self.morshu_process and self.morshu_process.poll() is None: + try: + self.morshu_process.terminate() + await asyncio.sleep(1) + if self.morshu_process.poll() is None: + self.morshu_process.kill() + log.info("Morshu server stopped") + except Exception as e: + log.exception(f"Error stopping Morshu server: {e}") + async def _get_effective_minimax_voice(self, user: discord.abc.User) -> str: mode = await self._get_tts_mode() @@ -2069,6 +2117,57 @@ class TtsToy(Cog): else: await ctx.send(f"You'll need to manually start DECTalk with `{ctx.clean_prefix}ttstoy dectalkstart`") + @ttstoy_group.command(name="morshustart") + @commands.is_owner() + async def morshu_start(self, ctx: commands.Context): + """Start the built-in Morshu TTS server.""" + await ctx.send("🚀 Starting Morshu server...") + + success = await self._start_morshu_server() + + if success: + await ctx.send( + f"✅ Morshu server started on port {self.morshu_port}!\n" + f"Switch to Morshu mode: `{ctx.clean_prefix}ttstoy mode morshu`" + ) + else: + await ctx.send("❌ Failed to start Morshu server. Check bot logs.") + + @ttstoy_group.command(name="morshustop") + @commands.is_owner() + async def morshu_stop(self, ctx: commands.Context): + """Stop the built-in Morshu server.""" + if not self.morshu_process or self.morshu_process.poll() is not None: + return await ctx.send("❌ Morshu server is not running") + + await self._stop_morshu_server() + await ctx.send("✅ Morshu server stopped") + + @ttstoy_group.command(name="morshustatus") + @commands.is_owner() + async def morshu_status(self, ctx: commands.Context): + """Check Morshu server status.""" + process_running = self.morshu_process and self.morshu_process.poll() is None + + api_responding = False + try: + r = requests.get(f"http://127.0.0.1:{self.morshu_port}/health", timeout=2) + api_responding = r.status_code == 200 + except: + pass + + lines = [ + "**Morshu Server Status**", + f"Process Running: {'✅ Yes' if process_running else '❌ No'}", + f"API Responding: {'✅ Yes' if api_responding else '❌ No'}", + f"URL: `http://127.0.0.1:{self.morshu_port}`", + ] + + if not process_running: + lines.append(f"\nStart with: `{ctx.clean_prefix}ttstoy morshustart`") + + await ctx.send("\n".join(lines)) + @ttstoy_group.command(name="accessibility") @commands.is_owner() async def accessibility_toggle(self, ctx: commands.Context):