Compare commits

...

1 Commits

Author SHA1 Message Date
Frederico Ribeiro
532b6f0dd6 feat: make WhatsApp reply prefix configurable via env var
Add WHATSAPP_REPLY_PREFIX to OPTIONAL_ENV_VARS, _ENV_VARS_NEVER_INHERIT
blocklist, and ENV_VARS_BY_VERSION (config version 10 -> 11).

Set to empty to disable the default header, or provide a custom prefix
with \n escape support.
2026-03-18 03:57:18 -07:00
6 changed files with 49 additions and 3 deletions

View File

@@ -35,7 +35,7 @@ _EXTRA_ENV_KEYS = frozenset({
"SIGNAL_ALLOWED_USERS", "SIGNAL_GROUP_ALLOWED_USERS", "SIGNAL_ALLOWED_USERS", "SIGNAL_GROUP_ALLOWED_USERS",
"DINGTALK_CLIENT_ID", "DINGTALK_CLIENT_SECRET", "DINGTALK_CLIENT_ID", "DINGTALK_CLIENT_SECRET",
"TERMINAL_ENV", "TERMINAL_SSH_KEY", "TERMINAL_SSH_PORT", "TERMINAL_ENV", "TERMINAL_SSH_KEY", "TERMINAL_SSH_PORT",
"WHATSAPP_MODE", "WHATSAPP_ENABLED", "WHATSAPP_MODE", "WHATSAPP_ENABLED", "WHATSAPP_REPLY_PREFIX",
"MATTERMOST_HOME_CHANNEL", "MATTERMOST_REPLY_MODE", "MATTERMOST_HOME_CHANNEL", "MATTERMOST_REPLY_MODE",
"MATRIX_PASSWORD", "MATRIX_ENCRYPTION", "MATRIX_HOME_ROOM", "MATRIX_PASSWORD", "MATRIX_ENCRYPTION", "MATRIX_HOME_ROOM",
}) })
@@ -372,7 +372,7 @@ DEFAULT_CONFIG = {
}, },
# Config schema version - bump this when adding new required fields # Config schema version - bump this when adding new required fields
"_config_version": 10, "_config_version": 11,
} }
# ============================================================================= # =============================================================================
@@ -387,6 +387,7 @@ ENV_VARS_BY_VERSION: Dict[int, List[str]] = {
5: ["WHATSAPP_ENABLED", "WHATSAPP_MODE", "WHATSAPP_ALLOWED_USERS", 5: ["WHATSAPP_ENABLED", "WHATSAPP_MODE", "WHATSAPP_ALLOWED_USERS",
"SLACK_BOT_TOKEN", "SLACK_APP_TOKEN", "SLACK_ALLOWED_USERS"], "SLACK_BOT_TOKEN", "SLACK_APP_TOKEN", "SLACK_ALLOWED_USERS"],
10: ["TAVILY_API_KEY"], 10: ["TAVILY_API_KEY"],
11: ["WHATSAPP_REPLY_PREFIX"],
} }
# Required environment variables with metadata for migration prompts. # Required environment variables with metadata for migration prompts.
@@ -775,6 +776,14 @@ OPTIONAL_ENV_VARS = {
"category": "messaging", "category": "messaging",
"advanced": True, "advanced": True,
}, },
"WHATSAPP_REPLY_PREFIX": {
"description": "Optional WhatsApp reply prefix. Use an empty value to disable it. Supports \\n escapes.",
"prompt": "WhatsApp reply prefix",
"url": None,
"password": False,
"category": "messaging",
"advanced": True,
},
"API_SERVER_ENABLED": { "API_SERVER_ENABLED": {
"description": "Enable the OpenAI-compatible API server (true/false). Allows frontends like Open WebUI, LobeChat, etc. to connect.", "description": "Enable the OpenAI-compatible API server (true/false). Allows frontends like Open WebUI, LobeChat, etc. to connect.",
"prompt": "Enable API server (true/false)", "prompt": "Enable API server (true/false)",

View File

@@ -347,6 +347,34 @@ class TestOptionalEnvVarsRegistry:
all_vars.extend(vars_list) all_vars.extend(vars_list)
assert "TAVILY_API_KEY" in all_vars assert "TAVILY_API_KEY" in all_vars
def test_whatsapp_reply_prefix_registered(self):
"""WHATSAPP_REPLY_PREFIX is listed in OPTIONAL_ENV_VARS."""
from hermes_cli.config import OPTIONAL_ENV_VARS
assert "WHATSAPP_REPLY_PREFIX" in OPTIONAL_ENV_VARS
def test_whatsapp_reply_prefix_is_messaging_category(self):
"""WHATSAPP_REPLY_PREFIX is a messaging setting."""
from hermes_cli.config import OPTIONAL_ENV_VARS
assert OPTIONAL_ENV_VARS["WHATSAPP_REPLY_PREFIX"]["category"] == "messaging"
def test_whatsapp_reply_prefix_is_advanced(self):
"""WHATSAPP_REPLY_PREFIX is marked as advanced."""
from hermes_cli.config import OPTIONAL_ENV_VARS
assert OPTIONAL_ENV_VARS["WHATSAPP_REPLY_PREFIX"]["advanced"] is True
def test_whatsapp_reply_prefix_in_env_vars_by_version(self):
"""WHATSAPP_REPLY_PREFIX is listed in ENV_VARS_BY_VERSION."""
from hermes_cli.config import ENV_VARS_BY_VERSION
all_vars = []
for vars_list in ENV_VARS_BY_VERSION.values():
all_vars.extend(vars_list)
assert "WHATSAPP_REPLY_PREFIX" in all_vars
def test_default_config_version_covers_env_var_versions(self):
"""Latest config version is at least the newest env-var migration version."""
from hermes_cli.config import DEFAULT_CONFIG, ENV_VARS_BY_VERSION
assert DEFAULT_CONFIG["_config_version"] >= max(ENV_VARS_BY_VERSION)
class TestAnthropicTokenMigration: class TestAnthropicTokenMigration:
"""Test that config version 8→9 clears ANTHROPIC_TOKEN.""" """Test that config version 8→9 clears ANTHROPIC_TOKEN."""

View File

@@ -263,6 +263,7 @@ class TestBlocklistCoverage:
"WHATSAPP_ENABLED", "WHATSAPP_ENABLED",
"WHATSAPP_MODE", "WHATSAPP_MODE",
"WHATSAPP_ALLOWED_USERS", "WHATSAPP_ALLOWED_USERS",
"WHATSAPP_REPLY_PREFIX",
"SIGNAL_HTTP_URL", "SIGNAL_HTTP_URL",
"SIGNAL_ACCOUNT", "SIGNAL_ACCOUNT",
"SIGNAL_ALLOWED_USERS", "SIGNAL_ALLOWED_USERS",

View File

@@ -99,6 +99,7 @@ def _build_provider_env_blocklist() -> frozenset:
"WHATSAPP_ENABLED", "WHATSAPP_ENABLED",
"WHATSAPP_MODE", "WHATSAPP_MODE",
"WHATSAPP_ALLOWED_USERS", "WHATSAPP_ALLOWED_USERS",
"WHATSAPP_REPLY_PREFIX",
"SIGNAL_HTTP_URL", "SIGNAL_HTTP_URL",
"SIGNAL_ACCOUNT", "SIGNAL_ACCOUNT",
"SIGNAL_ALLOWED_USERS", "SIGNAL_ALLOWED_USERS",

View File

@@ -149,6 +149,7 @@ For native Anthropic auth, Hermes prefers Claude Code's own credential files whe
| `WHATSAPP_ENABLED` | Enable the WhatsApp bridge (`true`/`false`) | | `WHATSAPP_ENABLED` | Enable the WhatsApp bridge (`true`/`false`) |
| `WHATSAPP_MODE` | `bot` (separate number) or `self-chat` (message yourself) | | `WHATSAPP_MODE` | `bot` (separate number) or `self-chat` (message yourself) |
| `WHATSAPP_ALLOWED_USERS` | Comma-separated phone numbers (with country code, no `+`) | | `WHATSAPP_ALLOWED_USERS` | Comma-separated phone numbers (with country code, no `+`) |
| `WHATSAPP_REPLY_PREFIX` | Optional WhatsApp reply prefix. Supports `\n` escapes. Set to an empty value to disable the header entirely |
| `SIGNAL_HTTP_URL` | signal-cli daemon HTTP endpoint (for example `http://127.0.0.1:8080`) | | `SIGNAL_HTTP_URL` | signal-cli daemon HTTP endpoint (for example `http://127.0.0.1:8080`) |
| `SIGNAL_ACCOUNT` | Bot phone number in E.164 format | | `SIGNAL_ACCOUNT` | Bot phone number in E.164 format |
| `SIGNAL_ALLOWED_USERS` | Comma-separated E.164 phone numbers or UUIDs | | `SIGNAL_ALLOWED_USERS` | Comma-separated E.164 phone numbers or UUIDs |

View File

@@ -95,6 +95,10 @@ Add the following to your `~/.hermes/.env` file:
WHATSAPP_ENABLED=true WHATSAPP_ENABLED=true
WHATSAPP_MODE=bot # "bot" or "self-chat" WHATSAPP_MODE=bot # "bot" or "self-chat"
WHATSAPP_ALLOWED_USERS=15551234567 # Comma-separated phone numbers (with country code, no +) WHATSAPP_ALLOWED_USERS=15551234567 # Comma-separated phone numbers (with country code, no +)
# Optional
WHATSAPP_REPLY_PREFIX= # Empty disables the default "Hermes Agent" header
# WHATSAPP_REPLY_PREFIX=Susie\\n\\n # Example custom prefix with newlines
``` ```
Then start the gateway: Then start the gateway:
@@ -140,7 +144,7 @@ Hermes supports voice on WhatsApp:
- **Incoming:** Voice messages (`.ogg` opus) are automatically transcribed using the configured STT provider: local `faster-whisper`, Groq Whisper (`GROQ_API_KEY`), or OpenAI Whisper (`VOICE_TOOLS_OPENAI_KEY`) - **Incoming:** Voice messages (`.ogg` opus) are automatically transcribed using the configured STT provider: local `faster-whisper`, Groq Whisper (`GROQ_API_KEY`), or OpenAI Whisper (`VOICE_TOOLS_OPENAI_KEY`)
- **Outgoing:** TTS responses are sent as MP3 audio file attachments - **Outgoing:** TTS responses are sent as MP3 audio file attachments
- Agent responses are prefixed with "⚕ **Hermes Agent**" by default. You can customize or disable this in `config.yaml`: - Agent responses are prefixed with "⚕ **Hermes Agent**" by default. You can customize or disable this in `config.yaml` or via the `WHATSAPP_REPLY_PREFIX` environment variable:
```yaml ```yaml
# ~/.hermes/config.yaml # ~/.hermes/config.yaml
@@ -149,6 +153,8 @@ whatsapp:
# reply_prefix: "🤖 *My Bot*\n──────\n" # Custom prefix (supports \n for newlines) # reply_prefix: "🤖 *My Bot*\n──────\n" # Custom prefix (supports \n for newlines)
``` ```
`WHATSAPP_REPLY_PREFIX` works as an environment-variable override for backward compatibility, but `config.yaml` is the preferred path.
--- ---
## Troubleshooting ## Troubleshooting