feat: voice profiles, rubberband FX, per-bot plugin filtering
- Add rubberband package to container for pitch-shifting FX - Split FX chain: rubberband CLI for pitch, ffmpeg for filters - Configurable voice profile (voice, fx, piper params) in [voice] - Extra bots inherit voice config (minus trigger) for own TTS - Greeting is voice-only, spoken directly by the greeting bot - Per-bot only_plugins/except_plugins filtering on Mumble - Alias plugin, core plugin tests Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
85
plugins/alias.py
Normal file
85
plugins/alias.py
Normal file
@@ -0,0 +1,85 @@
|
||||
"""Plugin: user-defined command aliases (persistent)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from derp.plugin import command
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
_NS = "alias"
|
||||
|
||||
|
||||
@command("alias", help="Aliases: !alias add|del|list|clear")
|
||||
async def cmd_alias(bot, message):
|
||||
"""Create short aliases for existing bot commands.
|
||||
|
||||
Usage:
|
||||
!alias add <name> <target> Create alias (e.g. !alias add s skip)
|
||||
!alias del <name> Remove alias
|
||||
!alias list Show all aliases
|
||||
!alias clear Remove all aliases (admin only)
|
||||
"""
|
||||
parts = message.text.split(None, 3)
|
||||
if len(parts) < 2:
|
||||
await bot.reply(message, "Usage: !alias <add|del|list|clear> [args]")
|
||||
return
|
||||
|
||||
sub = parts[1].lower()
|
||||
|
||||
if sub == "add":
|
||||
if len(parts) < 4:
|
||||
await bot.reply(message, "Usage: !alias add <name> <target>")
|
||||
return
|
||||
name = parts[2].lower()
|
||||
target = parts[3].lower()
|
||||
|
||||
# Cannot shadow an existing registered command
|
||||
if name in bot.registry.commands:
|
||||
await bot.reply(message, f"'{name}' is already a registered command")
|
||||
return
|
||||
|
||||
# Cannot alias to another alias (single-level only)
|
||||
if bot.state.get(_NS, target) is not None:
|
||||
await bot.reply(message, f"'{target}' is itself an alias; no chaining")
|
||||
return
|
||||
|
||||
# Target must resolve to a real command
|
||||
if target not in bot.registry.commands:
|
||||
await bot.reply(message, f"unknown command: {target}")
|
||||
return
|
||||
|
||||
bot.state.set(_NS, name, target)
|
||||
await bot.reply(message, f"alias: {name} -> {target}")
|
||||
|
||||
elif sub == "del":
|
||||
if len(parts) < 3:
|
||||
await bot.reply(message, "Usage: !alias del <name>")
|
||||
return
|
||||
name = parts[2].lower()
|
||||
if bot.state.delete(_NS, name):
|
||||
await bot.reply(message, f"alias removed: {name}")
|
||||
else:
|
||||
await bot.reply(message, f"no alias: {name}")
|
||||
|
||||
elif sub == "list":
|
||||
keys = bot.state.keys(_NS)
|
||||
if not keys:
|
||||
await bot.reply(message, "No aliases defined")
|
||||
return
|
||||
entries = []
|
||||
for key in sorted(keys):
|
||||
target = bot.state.get(_NS, key)
|
||||
entries.append(f"{key} -> {target}")
|
||||
await bot.reply(message, "Aliases: " + ", ".join(entries))
|
||||
|
||||
elif sub == "clear":
|
||||
if not bot._is_admin(message):
|
||||
await bot.reply(message, "Permission denied: clear requires admin")
|
||||
return
|
||||
count = bot.state.clear(_NS)
|
||||
await bot.reply(message, f"Cleared {count} alias(es)")
|
||||
|
||||
else:
|
||||
await bot.reply(message, "Usage: !alias <add|del|list|clear> [args]")
|
||||
Reference in New Issue
Block a user