Add messaging platform enhancements: STT, stickers, Discord UX, Slack, pairing, hooks
Major feature additions inspired by OpenClaw/ClawdBot integration analysis: Voice Message Transcription (STT): - Auto-transcribe voice/audio messages via OpenAI Whisper API - Download voice to ~/.hermes/audio_cache/ on Telegram/Discord/WhatsApp - Inject transcript as text so all models can understand voice input - Configurable model (whisper-1, gpt-4o-mini-transcribe, gpt-4o-transcribe) Telegram Sticker Understanding: - Describe static stickers via vision tool with JSON-backed cache - Cache keyed by file_unique_id avoids redundant API calls - Animated/video stickers get emoji-based fallback description Discord Rich UX: - Native slash commands (/ask, /reset, /status, /stop) via app_commands - Button-based exec approvals (Allow Once / Always Allow / Deny) - ExecApprovalView with user authorization and timeout handling Slack Integration: - Full SlackAdapter using slack-bolt with Socket Mode - DMs, channel messages (mention-gated), /hermes slash command - File attachment handling with bot-token-authenticated downloads DM Pairing System: - Code-based user authorization as alternative to static allowlists - 8-char codes from unambiguous alphabet, 1-hour expiry - Rate limiting, lockout after failed attempts, chmod 0600 on data - CLI: hermes pairing list/approve/revoke/clear-pending Event Hook System: - File-based hook discovery from ~/.hermes/hooks/ - HOOK.yaml + handler.py per hook, sync/async handler support - Events: gateway:startup, session:start/reset, agent:start/step/end - Wildcard matching (command:* catches all command events) Cross-Channel Messaging: - send_message agent tool for delivering to any connected platform - Enables cron job delivery and cross-platform notifications Human-Like Response Pacing: - Configurable delays between message chunks (off/natural/custom) - HERMES_HUMAN_DELAY_MODE env var with min/max ms settings Warm Injection Message Style: - Retrofitted image vision messages with friendly kawaii-consistent tone - All new injection messages (STT, stickers, errors) use warm style Also: updated config migration to prompt for optional keys interactively, bumped config version, updated README, AGENTS.md, .env.example, cli-config.yaml.example, install scripts, pyproject.toml, and toolsets.
This commit is contained in:
parent
5404a8fcd8
commit
69aa35a51c
23 changed files with 2080 additions and 32 deletions
|
|
@ -117,11 +117,22 @@ DEFAULT_CONFIG = {
|
|||
},
|
||||
},
|
||||
|
||||
"stt": {
|
||||
"enabled": True,
|
||||
"model": "whisper-1",
|
||||
},
|
||||
|
||||
"human_delay": {
|
||||
"mode": "off",
|
||||
"min_ms": 800,
|
||||
"max_ms": 2500,
|
||||
},
|
||||
|
||||
# Permanently allowed dangerous command patterns (added via "always" approval)
|
||||
"command_allowlist": [],
|
||||
|
||||
# Config schema version - bump this when adding new required fields
|
||||
"_config_version": 1,
|
||||
"_config_version": 2,
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
|
|
@ -195,6 +206,20 @@ OPTIONAL_ENV_VARS = {
|
|||
"url": None,
|
||||
"password": True,
|
||||
},
|
||||
"SLACK_BOT_TOKEN": {
|
||||
"description": "Slack bot integration",
|
||||
"prompt": "Slack Bot Token (xoxb-...)",
|
||||
"url": "https://api.slack.com/apps",
|
||||
"tools": ["slack"],
|
||||
"password": True,
|
||||
},
|
||||
"SLACK_APP_TOKEN": {
|
||||
"description": "Slack Socket Mode connection",
|
||||
"prompt": "Slack App Token (xapp-...)",
|
||||
"url": "https://api.slack.com/apps",
|
||||
"tools": ["slack"],
|
||||
"password": True,
|
||||
},
|
||||
# Messaging platform tokens
|
||||
"TELEGRAM_BOT_TOKEN": {
|
||||
"description": "Telegram bot token from @BotFather",
|
||||
|
|
@ -375,6 +400,44 @@ def migrate_config(interactive: bool = True, quiet: bool = False) -> Dict[str, A
|
|||
results["warnings"].append(f"Skipped {var['name']} - some features may not work")
|
||||
print()
|
||||
|
||||
# Check for missing optional env vars and offer to configure
|
||||
missing_optional = get_missing_env_vars(required_only=False)
|
||||
# Filter to only truly optional ones (not already handled as required above)
|
||||
required_names = {v["name"] for v in missing_env} if missing_env else set()
|
||||
missing_optional = [v for v in missing_optional if v["name"] not in required_names]
|
||||
|
||||
if missing_optional and not quiet:
|
||||
print(f"\n ℹ️ {len(missing_optional)} optional API key(s) not configured:")
|
||||
for var in missing_optional:
|
||||
tools = var.get("tools", [])
|
||||
tools_str = f" → enables: {', '.join(tools)}" if tools else ""
|
||||
print(f" • {var['name']}: {var['description']}{tools_str}")
|
||||
|
||||
if interactive and missing_optional:
|
||||
print("\n Would you like to configure any optional keys now?")
|
||||
try:
|
||||
answer = input(" Configure optional keys? [y/N]: ").strip().lower()
|
||||
except (EOFError, KeyboardInterrupt):
|
||||
answer = "n"
|
||||
|
||||
if answer in ("y", "yes"):
|
||||
print()
|
||||
for var in missing_optional:
|
||||
if var.get("url"):
|
||||
print(f" Get your key at: {var['url']}")
|
||||
|
||||
if var.get("password"):
|
||||
import getpass
|
||||
value = getpass.getpass(f" {var['prompt']} (Enter to skip): ")
|
||||
else:
|
||||
value = input(f" {var['prompt']} (Enter to skip): ").strip()
|
||||
|
||||
if value:
|
||||
save_env_value(var["name"], value)
|
||||
results["env_added"].append(var["name"])
|
||||
print(f" ✓ Saved {var['name']}")
|
||||
print()
|
||||
|
||||
# Check for missing config fields
|
||||
missing_config = get_missing_config_fields()
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue