Fix voice portal: WebSocket routing, Caddy keepalive, audio pipeline

- Fix app.py: @app.get -> @app.websocket for /ws/voice route (was returning 403)
- Fix app.py: create static_dir before mounting it (AttributeError on startup)
- Fix voice.html: AudioWorkletNode constructor (was AudioWorkletProcessor)
- Fix voice.html: use ScriptProcessor directly (more reliable)
- Fix voice.html: send Float32 directly (server expects float32, was sending Int16)
- Fix voice.html: auto-detect ws/wss protocol from page URL
- Add Caddy reverse proxy keepalive pings every 15s to prevent timeout
- Add detailed message type logging in WebSocket receive loop
- Strip Jarvis/Sage personas, rename bot to MoltMic
- Add /moltmic voice slash command for portal URL
- Update portal URL to https://voice.jezzahehn.com
This commit is contained in:
Jezza Hehn 2026-04-10 04:47:31 +00:00
parent bc580861dd
commit 3450e57ca6
6 changed files with 122 additions and 41 deletions

View file

@ -1,18 +1,18 @@
"""Jarvis Voice Bot - Discord Integration"""
"""MoltMic - OpenClaw Voice Bot"""
from .bot import JarvisVoiceBot, create_bot, run_bot
from .bot import MoltMicBot, create_bot, run_bot
from .voice_session import VoiceSession, VoiceSessionManager
from .audio_bridge import AudioBridge, PipelineAudioSource
from .commands import VoiceBotCommands, setup_commands
from .commands import MoltMicCommands, setup_commands
__all__ = [
"JarvisVoiceBot",
"MoltMicBot",
"create_bot",
"run_bot",
"VoiceSession",
"VoiceSessionManager",
"AudioBridge",
"PipelineAudioSource",
"VoiceBotCommands",
"MoltMicCommands",
"setup_commands",
]

View file

@ -20,8 +20,8 @@ from .vad_receiver import VADAudioReceiver
logger = get_logger(__name__)
class JarvisVoiceBot(discord.Client):
"""Discord bot for voice interaction with AI agents."""
class MoltMicBot(discord.Client):
"""MoltMic - Discord voice bot for OpenClaw."""
def __init__(
self,
@ -479,7 +479,7 @@ async def create_bot(
stt_transcriber=None,
orchestrator=None,
audio_output_callbacks=None,
) -> JarvisVoiceBot:
) -> MoltMicBot:
"""
Create and initialize the Discord bot.
@ -494,7 +494,7 @@ async def create_bot(
Returns:
Initialized bot instance
"""
bot = JarvisVoiceBot(
bot = MoltMicBot(
config=config,
openclaw_config=openclaw_config,
tts_synthesizer=tts_synthesizer,

View file

@ -94,6 +94,43 @@ class MoltMicCommands(app_commands.Group):
logger.exception(f"Status error: {e}")
await interaction.followup.send("❌ Error.", ephemeral=True)
@app_commands.command(name="voice", description="Open voice portal in browser")
async def voice(self, interaction: discord.Interaction):
"""Generate a voice portal URL for browser-based speech."""
await interaction.response.defer(thinking=True)
try:
# Import here to avoid circular dependency
from server.voice_ws import create_session_id
session_id = create_session_id()
portal_url = f"https://voice.jezzahehn.com/voice?session={session_id}"
embed = discord.Embed(
title="🎙️ Voice Portal",
description="Click below to open the voice portal in your browser",
color=discord.Color.blue()
)
embed.add_field(
name="Portal URL",
value=f"[Open Voice Portal]({portal_url})",
inline=False
)
embed.add_field(
name="Instructions",
value="1. Click the link above\n2. Allow microphone access\n3. Start talking! The bot will listen and respond.",
inline=False
)
embed.set_footer(text="The bot will start listening when you connect")
await interaction.followup.send(embed=embed)
logger.info(f"Voice portal created for session {session_id}")
except Exception as e:
logger.exception(f"Voice portal error: {e}")
await interaction.followup.send("❌ Failed to create voice portal.", ephemeral=True)
async def setup_commands(bot):
"""Register slash commands."""