style: remove em/en dashes from command help and log text
This commit is contained in:
+46
-46
@@ -122,7 +122,7 @@ def _save_emoji_sfx_map(mapping: dict):
|
|||||||
json.dump(mapping, f, ensure_ascii=False, indent=2)
|
json.dump(mapping, f, ensure_ascii=False, indent=2)
|
||||||
|
|
||||||
|
|
||||||
# Live reference — all code reads from this dict; _reload_emoji_sfx_map() refreshes it.
|
# Live reference - all code reads from this dict; _reload_emoji_sfx_map() refreshes it.
|
||||||
EMOJI_SFX_FOLDERS: dict = _load_emoji_sfx_map()
|
EMOJI_SFX_FOLDERS: dict = _load_emoji_sfx_map()
|
||||||
|
|
||||||
|
|
||||||
@@ -457,7 +457,7 @@ class TtsToy(Cog):
|
|||||||
def _kill_leftover_webui(self):
|
def _kill_leftover_webui(self):
|
||||||
"""Terminate any web UI subprocess left over from before the web UI
|
"""Terminate any web UI subprocess left over from before the web UI
|
||||||
moved out of the bot. It ran from <cog_dir>/webui/app.py, which no
|
moved out of the bot. It ran from <cog_dir>/webui/app.py, which no
|
||||||
longer exists — so a matching process is guaranteed stale."""
|
longer exists - so a matching process is guaranteed stale."""
|
||||||
try:
|
try:
|
||||||
pattern = str(Path(__file__).resolve().parent / "webui" / "app.py")
|
pattern = str(Path(__file__).resolve().parent / "webui" / "app.py")
|
||||||
result = subprocess.run(
|
result = subprocess.run(
|
||||||
@@ -830,7 +830,7 @@ class TtsToy(Cog):
|
|||||||
voice_segments = _split_voice_segments(text)
|
voice_segments = _split_voice_segments(text)
|
||||||
has_voice_switches = len(voice_segments) > 1 or voice_segments[0][0] is not None
|
has_voice_switches = len(voice_segments) > 1 or voice_segments[0][0] is not None
|
||||||
|
|
||||||
# Fast path: no voice switches — use the legacy single-engine path
|
# Fast path: no voice switches - use the legacy single-engine path
|
||||||
if not has_voice_switches:
|
if not has_voice_switches:
|
||||||
return self._render_single_mode(
|
return self._render_single_mode(
|
||||||
api_key, model, voice_id, text, output_path, sfx_volume,
|
api_key, model, voice_id, text, output_path, sfx_volume,
|
||||||
@@ -1032,7 +1032,7 @@ class TtsToy(Cog):
|
|||||||
|
|
||||||
@commands.group(name="chatterbox", invoke_without_command=True)
|
@commands.group(name="chatterbox", invoke_without_command=True)
|
||||||
async def chatterbox_group(self, ctx: commands.Context):
|
async def chatterbox_group(self, ctx: commands.Context):
|
||||||
"""Chatterbox TTS voice management — upload, tune, and share cloned voices.
|
"""Chatterbox TTS voice management - upload, tune, and share cloned voices.
|
||||||
|
|
||||||
Use `[p]help chatterbox` to see all subcommands.
|
Use `[p]help chatterbox` to see all subcommands.
|
||||||
"""
|
"""
|
||||||
@@ -1049,13 +1049,13 @@ class TtsToy(Cog):
|
|||||||
"",
|
"",
|
||||||
"**── Voice Cloning ──**",
|
"**── Voice Cloning ──**",
|
||||||
"Upload a voice clip and Chatterbox will clone it for TTS.",
|
"Upload a voice clip and Chatterbox will clone it for TTS.",
|
||||||
f"• `{prefix}chatterbox addvoice <name>` — attach a .wav or .mp3 (5+ sec)",
|
f"• `{prefix}chatterbox addvoice <name>` - attach a .wav or .mp3 (5+ sec)",
|
||||||
f"• `{prefix}chatterbox addvoice <name> <url>` — or paste a direct link",
|
f"• `{prefix}chatterbox addvoice <name> <url>` - or paste a direct link",
|
||||||
f"• `{prefix}chatterbox removevoice <name>` — delete a voice",
|
f"• `{prefix}chatterbox removevoice <name>` - delete a voice",
|
||||||
f"• `{prefix}chatterbox myvoices` — see your uploaded voices",
|
f"• `{prefix}chatterbox myvoices` - see your uploaded voices",
|
||||||
f"• `{prefix}ttstoy myvoice <name>` — switch your active voice",
|
f"• `{prefix}ttstoy myvoice <name>` - switch your active voice",
|
||||||
"",
|
"",
|
||||||
"Tips: 5–15s of clear speech, one speaker, no background noise.",
|
"Tips: 5-15s of clear speech, one speaker, no background noise.",
|
||||||
".wav works best, .mp3 accepted. Short clips auto-loop to 5s.",
|
".wav works best, .mp3 accepted. Short clips auto-loop to 5s.",
|
||||||
"",
|
"",
|
||||||
"**── Special Tokens (Turbo model) ──**",
|
"**── Special Tokens (Turbo model) ──**",
|
||||||
@@ -1068,9 +1068,9 @@ class TtsToy(Cog):
|
|||||||
"**── Voice Settings ──**",
|
"**── Voice Settings ──**",
|
||||||
"All saved per voice per user. Each voice keeps its own tuning.",
|
"All saved per voice per user. Each voice keeps its own tuning.",
|
||||||
"",
|
"",
|
||||||
f"• `{prefix}chatterbox temp [0.0–1.5]` — randomness",
|
f"• `{prefix}chatterbox temp [0.0-1.5]` - randomness",
|
||||||
f"• `{prefix}chatterbox exag [0.25–2.0]` — expressiveness",
|
f"• `{prefix}chatterbox exag [0.25-2.0]` - expressiveness",
|
||||||
f"• `{prefix}chatterbox volume [-20–20]` — dB loudness offset",
|
f"• `{prefix}chatterbox volume [-20-20]` - dB loudness offset",
|
||||||
"Append `reset` to any of the above to clear it.",
|
"Append `reset` to any of the above to clear it.",
|
||||||
"",
|
"",
|
||||||
"**── Voice Sharing ──**",
|
"**── Voice Sharing ──**",
|
||||||
@@ -1116,8 +1116,8 @@ class TtsToy(Cog):
|
|||||||
f"**Loaded:** {'✅' if loaded else '❌'}",
|
f"**Loaded:** {'✅' if loaded else '❌'}",
|
||||||
"",
|
"",
|
||||||
"Available models:",
|
"Available models:",
|
||||||
"• `turbo` — fast, 350M params, supports [laugh]/[cough]/etc tags",
|
"• `turbo` - fast, 350M params, supports [laugh]/[cough]/etc tags",
|
||||||
"• `original` — better voice cloning, 0.5B params, stronger emotion control",
|
"• `original` - better voice cloning, 0.5B params, stronger emotion control",
|
||||||
"",
|
"",
|
||||||
f"Switch: `{ctx.clean_prefix}chatterbox model turbo` or `{ctx.clean_prefix}chatterbox model original`",
|
f"Switch: `{ctx.clean_prefix}chatterbox model turbo` or `{ctx.clean_prefix}chatterbox model original`",
|
||||||
]
|
]
|
||||||
@@ -1232,11 +1232,11 @@ class TtsToy(Cog):
|
|||||||
f"**Current TTS Mode: {mode_emoji} {current.upper()}**",
|
f"**Current TTS Mode: {mode_emoji} {current.upper()}**",
|
||||||
"",
|
"",
|
||||||
"Available modes:",
|
"Available modes:",
|
||||||
"- `minimax` – Use MiniMax cloud API (requires API key)",
|
"- `minimax` - Use MiniMax cloud API (requires API key)",
|
||||||
"- `chatterbox` – Use local Chatterbox TTS server (AI voice cloning)",
|
"- `chatterbox` - Use local Chatterbox TTS server (AI voice cloning)",
|
||||||
"- `dectalk` – Use DECTalk API (classic robotic voice)",
|
"- `dectalk` - Use DECTalk API (classic robotic voice)",
|
||||||
f"- `morshu` – Use MorshuTalk ({'available' if MORSHU_AVAILABLE else 'not installed'})",
|
f"- `morshu` - Use MorshuTalk ({'available' if MORSHU_AVAILABLE else 'not installed'})",
|
||||||
f"- `vox` – Black Mesa VOX announcer ({'available' if VOX_AVAILABLE else 'not installed'})",
|
f"- `vox` - Black Mesa VOX announcer ({'available' if VOX_AVAILABLE else 'not installed'})",
|
||||||
"",
|
"",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -1593,9 +1593,9 @@ class TtsToy(Cog):
|
|||||||
@chatterbox_group.command(name="temp")
|
@chatterbox_group.command(name="temp")
|
||||||
async def chatterbox_temperature(self, ctx: commands.Context, value: Optional[float] = None):
|
async def chatterbox_temperature(self, ctx: commands.Context, value: Optional[float] = None):
|
||||||
"""
|
"""
|
||||||
Show or set Chatterbox temperature for your current voice (0.0–1.5).
|
Show or set Chatterbox temperature for your current voice (0.0-1.5).
|
||||||
|
|
||||||
Saved per voice per user — each voice remembers its own setting.
|
Saved per voice per user - each voice remembers its own setting.
|
||||||
Lower = more consistent, higher = more varied.
|
Lower = more consistent, higher = more varied.
|
||||||
- `[p]chatterbox temp` → show current
|
- `[p]chatterbox temp` → show current
|
||||||
- `[p]chatterbox temp 0.8` → set value
|
- `[p]chatterbox temp 0.8` → set value
|
||||||
@@ -1640,14 +1640,14 @@ class TtsToy(Cog):
|
|||||||
await self.config.user(ctx.author).chatterbox_temperature_offsets.set(offsets)
|
await self.config.user(ctx.author).chatterbox_temperature_offsets.set(offsets)
|
||||||
await ctx.send(f"✅ Temperature for `{voice}` reset to server default.")
|
await ctx.send(f"✅ Temperature for `{voice}` reset to server default.")
|
||||||
else:
|
else:
|
||||||
await ctx.send("❌ Value must be a number (0.0–1.5) or `reset`.")
|
await ctx.send("❌ Value must be a number (0.0-1.5) or `reset`.")
|
||||||
|
|
||||||
@chatterbox_group.command(name="exag")
|
@chatterbox_group.command(name="exag")
|
||||||
async def chatterbox_exaggeration(self, ctx: commands.Context, value: Optional[float] = None):
|
async def chatterbox_exaggeration(self, ctx: commands.Context, value: Optional[float] = None):
|
||||||
"""
|
"""
|
||||||
Show or set Chatterbox exaggeration for your current voice (0.25–2.0).
|
Show or set Chatterbox exaggeration for your current voice (0.25-2.0).
|
||||||
|
|
||||||
Saved per voice per user — each voice remembers its own setting.
|
Saved per voice per user - each voice remembers its own setting.
|
||||||
Higher = more expressive/dramatic delivery.
|
Higher = more expressive/dramatic delivery.
|
||||||
- `[p]chatterbox exag` → show current
|
- `[p]chatterbox exag` → show current
|
||||||
- `[p]chatterbox exag 1.5` → set value
|
- `[p]chatterbox exag 1.5` → set value
|
||||||
@@ -1692,7 +1692,7 @@ class TtsToy(Cog):
|
|||||||
await self.config.user(ctx.author).chatterbox_exaggeration_offsets.set(offsets)
|
await self.config.user(ctx.author).chatterbox_exaggeration_offsets.set(offsets)
|
||||||
await ctx.send(f"✅ Exaggeration for `{voice}` reset to server default.")
|
await ctx.send(f"✅ Exaggeration for `{voice}` reset to server default.")
|
||||||
else:
|
else:
|
||||||
await ctx.send("❌ Value must be a number (0.25–2.0) or `reset`.")
|
await ctx.send("❌ Value must be a number (0.25-2.0) or `reset`.")
|
||||||
|
|
||||||
@chatterbox_group.command(name="volume")
|
@chatterbox_group.command(name="volume")
|
||||||
async def chatterbox_volume(self, ctx: commands.Context, value: Optional[float] = None):
|
async def chatterbox_volume(self, ctx: commands.Context, value: Optional[float] = None):
|
||||||
@@ -1744,9 +1744,9 @@ class TtsToy(Cog):
|
|||||||
@chatterbox_group.command(name="speed")
|
@chatterbox_group.command(name="speed")
|
||||||
async def chatterbox_speed(self, ctx: commands.Context, value: Optional[float] = None):
|
async def chatterbox_speed(self, ctx: commands.Context, value: Optional[float] = None):
|
||||||
"""
|
"""
|
||||||
Show or set speed for your current voice (0.25–4.0).
|
Show or set speed for your current voice (0.25-4.0).
|
||||||
|
|
||||||
Saved per-voice — each voice remembers its own speed.
|
Saved per-voice - each voice remembers its own speed.
|
||||||
- `[p]chatterbox speed` → show current voice's speed
|
- `[p]chatterbox speed` → show current voice's speed
|
||||||
- `[p]chatterbox speed 1.2` → set speed (1.0 = normal)
|
- `[p]chatterbox speed 1.2` → set speed (1.0 = normal)
|
||||||
- `[p]chatterbox speed reset` → remove speed override
|
- `[p]chatterbox speed reset` → remove speed override
|
||||||
@@ -1790,7 +1790,7 @@ class TtsToy(Cog):
|
|||||||
display = parts[1].rsplit(".", 1)[0] if len(parts) > 1 and parts[0].isdigit() else voice.rsplit(".", 1)[0]
|
display = parts[1].rsplit(".", 1)[0] if len(parts) > 1 and parts[0].isdigit() else voice.rsplit(".", 1)[0]
|
||||||
await ctx.send(f"✅ Speed for `{display}` reset to default.")
|
await ctx.send(f"✅ Speed for `{display}` reset to default.")
|
||||||
else:
|
else:
|
||||||
await ctx.send("❌ Value must be a number (0.25–4.0) or `reset`.")
|
await ctx.send("❌ Value must be a number (0.25-4.0) or `reset`.")
|
||||||
|
|
||||||
@chatterbox_group.command(name="sharevoice")
|
@chatterbox_group.command(name="sharevoice")
|
||||||
async def share_voice(self, ctx: commands.Context, name: str, target: discord.Member):
|
async def share_voice(self, ctx: commands.Context, name: str, target: discord.Member):
|
||||||
@@ -1979,7 +1979,7 @@ class TtsToy(Cog):
|
|||||||
return await ctx.send(f"No words found in VOX pack `{pack}`.")
|
return await ctx.send(f"No words found in VOX pack `{pack}`.")
|
||||||
|
|
||||||
# Split into chunks to avoid message length limits
|
# Split into chunks to avoid message length limits
|
||||||
header = f"**VOX Words ({pack}) — {len(words)} words:**\n"
|
header = f"**VOX Words ({pack}) - {len(words)} words:**\n"
|
||||||
chunk = header
|
chunk = header
|
||||||
for w in words:
|
for w in words:
|
||||||
entry = f"`{w}` "
|
entry = f"`{w}` "
|
||||||
@@ -2020,7 +2020,7 @@ class TtsToy(Cog):
|
|||||||
f"- Run `{prefix}ttstoy myvoice` to see all voice commands.",
|
f"- Run `{prefix}ttstoy myvoice` to see all voice commands.",
|
||||||
"",
|
"",
|
||||||
"**For Morshu mode:**",
|
"**For Morshu mode:**",
|
||||||
f"- Just switch to morshu mode — no setup needed.",
|
f"- Just switch to morshu mode - no setup needed.",
|
||||||
f"- All text is spoken in Morshu's voice from the CD-i Zelda games.",
|
f"- All text is spoken in Morshu's voice from the CD-i Zelda games.",
|
||||||
"",
|
"",
|
||||||
"**For VOX mode:**",
|
"**For VOX mode:**",
|
||||||
@@ -2084,10 +2084,10 @@ class TtsToy(Cog):
|
|||||||
"**MiniMax TTS Models**",
|
"**MiniMax TTS Models**",
|
||||||
"",
|
"",
|
||||||
"These are example model names you can use (subject to your MiniMax account):",
|
"These are example model names you can use (subject to your MiniMax account):",
|
||||||
"- `speech-01-turbo` – fast & cheap",
|
"- `speech-01-turbo` - fast & cheap",
|
||||||
"- `speech-01-hd` – higher quality, slower",
|
"- `speech-01-hd` - higher quality, slower",
|
||||||
"- `speech-02-turbo` – newer fast model",
|
"- `speech-02-turbo` - newer fast model",
|
||||||
"- `speech-02-hd` – newer high quality model",
|
"- `speech-02-hd` - newer high quality model",
|
||||||
"",
|
"",
|
||||||
f"Current configured model: `{current}`",
|
f"Current configured model: `{current}`",
|
||||||
"",
|
"",
|
||||||
@@ -2431,7 +2431,7 @@ class TtsToy(Cog):
|
|||||||
Generate TTS audio and upload the MP3. Also plays in voice chat if you're in one.
|
Generate TTS audio and upload the MP3. Also plays in voice chat if you're in one.
|
||||||
"""
|
"""
|
||||||
async with ctx.typing():
|
async with ctx.typing():
|
||||||
# Check if user is in a VC — we'll play there if so, but it's not required
|
# Check if user is in a VC - we'll play there if so, but it's not required
|
||||||
user_in_vc = ctx.author.voice and ctx.author.voice.channel
|
user_in_vc = ctx.author.voice and ctx.author.voice.channel
|
||||||
audio: Optional[Audio] = self.bot.get_cog("Audio")
|
audio: Optional[Audio] = self.bot.get_cog("Audio")
|
||||||
|
|
||||||
@@ -2586,7 +2586,7 @@ class TtsToy(Cog):
|
|||||||
await ctx.react_quietly("🗣")
|
await ctx.react_quietly("🗣")
|
||||||
|
|
||||||
# =========================================================================
|
# =========================================================================
|
||||||
# WEB UI — login, webui commands, and shared-state processor
|
# WEB UI - login, webui commands, and shared-state processor
|
||||||
# =========================================================================
|
# =========================================================================
|
||||||
|
|
||||||
WEBUI_SHARED_STATE = Path(os.environ.get("TTSTOY_SHARED_STATE", "/tmp/ttstoy_webui_state.json"))
|
WEBUI_SHARED_STATE = Path(os.environ.get("TTSTOY_SHARED_STATE", "/tmp/ttstoy_webui_state.json"))
|
||||||
@@ -2630,7 +2630,7 @@ class TtsToy(Cog):
|
|||||||
ephemeral=True,
|
ephemeral=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Register token with ttstoy webui — it writes to shared_auth.json.
|
# Register token with ttstoy webui - it writes to shared_auth.json.
|
||||||
# The user logs in once on the Homepage; the session cookie is shared
|
# The user logs in once on the Homepage; the session cookie is shared
|
||||||
# across all *.kingstons-scrapyard.net subdomains.
|
# across all *.kingstons-scrapyard.net subdomains.
|
||||||
avatar_url = str(ctx.author.display_avatar.url) if ctx.author.display_avatar else ""
|
avatar_url = str(ctx.author.display_avatar.url) if ctx.author.display_avatar else ""
|
||||||
@@ -2657,7 +2657,7 @@ class TtsToy(Cog):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
await ctx.author.send(
|
await ctx.author.send(
|
||||||
f"**Kingston's Scrapyard — Login**\n\n"
|
f"**Kingston's Scrapyard - Login**\n\n"
|
||||||
f"Go to: https://homepage.kingstons-scrapyard.net/login\n\n"
|
f"Go to: https://homepage.kingstons-scrapyard.net/login\n\n"
|
||||||
f"Your key (valid for 5 minutes):\n```\n{token}\n```\n"
|
f"Your key (valid for 5 minutes):\n```\n{token}\n```\n"
|
||||||
f"Log in once and you'll be signed in across all Scrapyard sites."
|
f"Log in once and you'll be signed in across all Scrapyard sites."
|
||||||
@@ -2707,7 +2707,7 @@ class TtsToy(Cog):
|
|||||||
await ctx.send("🔄 The web UI is now a scrapyard service.\nRestart it with: `sudo systemctl restart scrapyard`")
|
await ctx.send("🔄 The web UI is now a scrapyard service.\nRestart it with: `sudo systemctl restart scrapyard`")
|
||||||
|
|
||||||
# -------------------------------------------------------------------------
|
# -------------------------------------------------------------------------
|
||||||
# Background task — poll shared state for pending commands and posts
|
# Background task - poll shared state for pending commands and posts
|
||||||
# -------------------------------------------------------------------------
|
# -------------------------------------------------------------------------
|
||||||
|
|
||||||
@tasks.loop(seconds=3)
|
@tasks.loop(seconds=3)
|
||||||
@@ -2731,7 +2731,7 @@ class TtsToy(Cog):
|
|||||||
def _take_pending_state_updates(self):
|
def _take_pending_state_updates(self):
|
||||||
"""Under a cross-process lock: pop pending commands/posts and refresh user info.
|
"""Under a cross-process lock: pop pending commands/posts and refresh user info.
|
||||||
|
|
||||||
Returns (pending_cmds, pending_posts). Runs in a worker thread — the
|
Returns (pending_cmds, pending_posts). Runs in a worker thread - the
|
||||||
lock matches the one the web UI uses when appending, so no updates are
|
lock matches the one the web UI uses when appending, so no updates are
|
||||||
lost between the read and the write.
|
lost between the read and the write.
|
||||||
"""
|
"""
|
||||||
@@ -2899,13 +2899,13 @@ class TtsToy(Cog):
|
|||||||
break
|
break
|
||||||
|
|
||||||
if not member_vc or not member_guild:
|
if not member_vc or not member_guild:
|
||||||
log.info(f"[WebUI] {user_id} is not in a VC — skipping playback")
|
log.info(f"[WebUI] {user_id} is not in a VC - skipping playback")
|
||||||
return
|
return
|
||||||
|
|
||||||
member = member_guild.get_member(user_id)
|
member = member_guild.get_member(user_id)
|
||||||
audio_cog: Optional[Audio] = self.bot.get_cog("Audio")
|
audio_cog: Optional[Audio] = self.bot.get_cog("Audio")
|
||||||
if not audio_cog:
|
if not audio_cog:
|
||||||
log.warning("[WebUI] Audio cog not loaded — skipping VC playback")
|
log.warning("[WebUI] Audio cog not loaded - skipping VC playback")
|
||||||
return
|
return
|
||||||
text_channel = member_guild.system_channel or next(
|
text_channel = member_guild.system_channel or next(
|
||||||
(c for c in member_guild.text_channels
|
(c for c in member_guild.text_channels
|
||||||
@@ -2964,7 +2964,7 @@ class TtsToy(Cog):
|
|||||||
if ch.name.lower() in ("tts", "ttstoy", "tts-toy"):
|
if ch.name.lower() in ("tts", "ttstoy", "tts-toy"):
|
||||||
return ch
|
return ch
|
||||||
|
|
||||||
# No guild specified — search all guilds
|
# No guild specified - search all guilds
|
||||||
for guild in self.bot.guilds:
|
for guild in self.bot.guilds:
|
||||||
for ch in guild.text_channels:
|
for ch in guild.text_channels:
|
||||||
if ch.name.lower() in ("tts", "ttstoy", "tts-toy"):
|
if ch.name.lower() in ("tts", "ttstoy", "tts-toy"):
|
||||||
@@ -2978,8 +2978,8 @@ class TtsToy(Cog):
|
|||||||
"""Set or show the channel where web UI TTS audio is posted in this server.
|
"""Set or show the channel where web UI TTS audio is posted in this server.
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
[p]ttstoy webuichannel — show current channel
|
[p]ttstoy webuichannel - show current channel
|
||||||
[p]ttstoy webuichannel #channel — set channel for this server
|
[p]ttstoy webuichannel #channel - set channel for this server
|
||||||
"""
|
"""
|
||||||
if channel is None:
|
if channel is None:
|
||||||
guild_channel_id = await self.config.guild(ctx.guild).webui_tts_channel_id()
|
guild_channel_id = await self.config.guild(ctx.guild).webui_tts_channel_id()
|
||||||
|
|||||||
Reference in New Issue
Block a user