Настройка WhatsApp
К WhatsApp Hermes подключается через встроенный мост на базе Baileys. Он эмулирует сессию WhatsApp Web и работает в обход официального WhatsApp Business API. Аккаунт разработчика Meta или верификация Business не нужны.
Запустите
hermes gateway setupи выберите WhatsApp — мастер проведёт вас по шагам.
Эта страница посвящена мосту Baileys — быстрая настройка, личные аккаунты, публичный URL не нужен, но есть риск бана.
Если вы строите настоящего бизнес-бота и вам важна стабильность, смотрите руководство по WhatsApp Business Cloud API. Это официальный путь, поддерживаемый Meta: никакого риска блокировки аккаунта, но потребуется бизнес-аккаунт Meta и публичный URL для вебхука.
При желании оба адаптера можно запускать параллельно на разных номерах телефонов.
За пределами Business API сторонние боты официально не поддерживаются. Любой сторонний мост несёт небольшой риск ограничений на аккаунт. Чтобы его снизить:
- Заведите отдельный номер телефона под бота (не свой личный)
- Не рассылайте массовые сообщения и спам — держите общение в живом, разговорном формате
- Не автоматизируйте исходящие тем, кто вам ещё не писал
Протокол WhatsApp Web время от времени меняется, и это может временно ломать совместимость со сторонними мостами. В таких случаях Hermes обновляет зависимость моста. Если после очередного обновления WhatsApp бот перестал отвечать — подтяните свежую версию Hermes и выполните повторную привязку.
Несколько профилей
Мультиплексор хоста может обслуживать отдельную привязанную сессию WhatsApp для каждого профиля.
Чтобы привязать вторичный профиль, выполните hermes -p work whatsapp, затем включите WhatsApp
для этого профиля. Включённый профиль без creds.json пропускается со статусом
whatsapp_unpaired и командой привязки.
Явный platforms.whatsapp.extra.bridge_port имеет приоритет. Иначе вторичный профиль выбирает
первый свободный порт в диапазоне 3001–3999, который не занят записью другого профиля, и
сохраняет его в собственном файле platforms/whatsapp/bridge_port для следующих запусков.
Операторы могут заранее создать этот файл с номером порта; чтобы выделить новый порт, удалите
файл. Профиль запуска использует порт 3000, если у него ещё нет такого файла (он появляется,
когда профиль побывал вторичным): тогда его шлюз и hermes send --to whatsapp:<chat_id>
продолжают использовать записанный порт.
Вторичный профиль подхватывает уже работающий на его порту мост, только если собственный
pid-файл его сессии указывает на этот процесс (pid, время старта в ядре и порт, на котором он
запущен): именно это остаётся после аварии шлюза. Нездоровый мост по той же идентичности
завершается и запускается заново. Любой другой процесс, занявший порт, — фатальная ошибка
только для этого профиля. Задайте в platforms.whatsapp.extra.bridge_port другой свободный
порт или остановите процесс, который его держит. Остальные профили продолжают работать.
hermes gateway status --profile work показывает собственный адаптер WhatsApp этого профиля,
а не общий вход.
Профили, у каждого из которых свой шлюз (а не один мультиплексированный), по умолчанию все
используют порт 3000. Назначьте каждому свой порт в config.yaml этого профиля:
platforms:
whatsapp:
extra:
bridge_port: 3001 # one distinct port per profile
Шлюз определяет работающий мост по каталогу сессии, который мост сообщает в /health. Мост,
обслуживающий сессию другого профиля, не подхватывается и не останавливается никогда: WhatsApp
второго профиля не запускается с ошибкой whatsapp_bridge_foreign_session, где указаны порт и
чужая сессия. Процесс, который держит порт, но не отвечает на /health вовремя, тоже
остаётся работать, а WhatsApp завершается с повторяемой ошибкой whatsapp_bridge_unresponsive.
hermes send --to whatsapp:<chat_id> и доставка из cron проверяют то же поле и ничего не
отправляют через мост другого профиля или через мост, у которого /health не отвечает. Если вы
переопределяете session_path, делайте его разным для каждого профиля, иначе профили будут
делить один вход в WhatsApp. Мосты, запущенные более старым Hermes, не сообщают каталог сессии
и один раз перезапускаются, как после обновления моста.
Два режима
| Режим | Как работает | Кому подойдёт |
|---|---|---|
| Отдельный номер для бота (рекомендуется) | Под бота выделяется отдельный телефонный номер. Люди пишут прямо на него. | Чистый UX, несколько пользователей, меньший риск бана |
| Самочат на личном номере | Используется ваш собственный WhatsApp. Чтобы пообщаться с агентом, вы пишете самому себе. | Быстрый старт, один пользователь, тесты |
Что понадобится
- Node.js v18+ и npm — мост WhatsApp запускается как процесс Node.js
- Телефон с установленным WhatsApp (для сканирования QR-кода)
В отличие от старых мостов на основе браузера, нынешний вариант на Baileys обходится без локального Chromium и стека Puppeteer.
Шаг 1. Запустите мастер настройки
hermes whatsapp
Мастер сделает следующее:
- Спросит, какой режим вам нужен (bot или self-chat)
- При необходимости установит зависимости моста
- Выведет QR-код прямо в терминал
- Подождёт, пока вы его отсканируете
Как отсканировать QR-код:
- Откройте WhatsApp на телефоне
- Зайдите в Настройки → Связанные устройства
- Нажмите Привязка устройства
- Наведите камеру на QR-код в терминале
После привязки мастер подтвердит соединение и завершится. Сессия сохраняется автоматически.
Если QR-код выглядит искажённым, проверьте, что ширина терминала не меньше 60 столбцов и он поддерживает Unicode. Иногда помогает другой эмулятор терминала.
Шаг 2. Где взять второй номер (режим бота)
Для режима бота нужен номер, ещё не зарегистрированный в WhatsApp. Есть три варианта:
| Вариант | Стоимость | Примечания |
|---|---|---|
| Google Voice | Бесплатно | Только для США. Получите номер на voice.google.com. Подтвердите WhatsApp по SMS через приложение Google Voice. |
| Предоплаченная SIM | $5–15 разово | Любой оператор. Активируйте, подтвердите WhatsApp — дальше SIM может лежать в ящике. Номер нужно держать активным (звонок раз в 90 дней). |
| VoIP-сервисы | от бесплатного до $5/мес | TextNow, TextFree и аналоги. Часть VoIP-номеров WhatsApp блокирует — если первый не сработал, попробуйте пару других. |
Когда номер на руках:
- Установите WhatsApp на телефон (либо используйте приложение WhatsApp Business с двумя SIM)
- Зарегистрируйте новый номер в WhatsApp
- Запустите
hermes whatsappи отсканируйте QR-код из этого аккаунта WhatsApp
Шаг 3. Настройте Hermes
Добавьте в файл ~/.hermes/.env следующее:
# Обязательно
WHATSAPP_ENABLED=true
WHATSAPP_MODE=bot # "bot" или "self-chat"
# Контроль доступа — выберите ОДИН из вариантов:
WHATSAPP_ALLOWED_USERS=15551234567 # Номера телефонов через запятую (с кодом страны, без +)
# WHATSAPP_ALLOWED_USERS=* # ИЛИ * — разрешить всех
# WHATSAPP_ALLOW_ALL_USERS=true # ИЛИ этот флаг (то же, что *)
Значение WHATSAPP_ALLOWED_USERS=* пропускает любых отправителей (то же, что WHATSAPP_ALLOW_ALL_USERS=true).
Поведение согласовано со списками разрешений для групп Signal.
Хотите вместо этого пользоваться привязкой по паролю — уберите обе переменные и положитесь на
систему DM-привязки.
Дополнительные настройки поведения в ~/.hermes/config.yaml:
unauthorized_dm_behavior: pair
whatsapp:
unauthorized_dm_behavior: ignore
unauthorized_dm_behavior: pair— глобальное значение по умолчанию. Неизвестным отправителям личных сообщений выдаётся код привязки.whatsapp.unauthorized_dm_behavior: ignoreзаставляет WhatsApp молчать в ответ на неавторизованные личные сообщения — для приватного номера обычно вариант получше.
Групповые чаты (режим бота)
Группы фильтруются групповой политикой, а не списком разрешённых для личных сообщений. WHATSAPP_GROUP_POLICY / whatsapp.group_policy
по умолчанию равно pairing — из групп тогда ничего не пересылается. allowlist вместе с WHATSAPP_GROUP_ALLOWED_USERS /
whatsapp.group_allow_from (JID групп через запятую, например 120363001234567890@g.us) пропускает перечисленные
группы; open пропускает все группы, где состоит бот. Затем отправитель проверяется, как любой другой участник
шлюза: если задан WHATSAPP_ALLOWED_USERS, участник должен быть в нём (или пройти сопряжение). Отправителя, которого WhatsApp
адресует по LID, сопоставляют по номеру телефона, который Baileys передаёт вместе с ним, так что первый контакт без
файла lid-mapping не отбрасывается. Если списка разрешённых отправителей нет,
allowlist доверяет только списку JID групп и пропускает любого участника перечисленной группы, а open всё равно
требует, чтобы участник был сопряжён, или WHATSAPP_ALLOW_ALL_USERS=true. По умолчанию бот отвечает на каждое пропущенное
сообщение в группе; задайте require_mention: true / WHATSAPP_REQUIRE_MENTION=true, чтобы он отвечал только на @-упоминания, ответы
боту или /commands (группы из free_response_chats — исключение).
Теперь запустите шлюз:
hermes gateway # Foreground
hermes gateway install # Install as a user service
sudo hermes gateway install --system # Linux only: boot-time system service
Шлюз сам поднимет мост WhatsApp, используя сохранённую сессию.
Сохранение сессии
Свою сессию мост Baileys держит в ~/.hermes/platforms/whatsapp/session. Отсюда несколько следствий:
- Сессии переживают перезапуски — заново сканировать QR-код каждый раз не придётся
- В данных сессии лежат ключи шифрования и учётные данные устройства
- Не публикуйте и не коммитьте этот каталог сессии — он даёт полный доступ к аккаунту WhatsApp
Повторная привязка
Если сессия сломалась (сброс телефона, обновление WhatsApp, ручная отвязка), в логах шлюза появятся ошибки соединения. Как это починить:
hermes whatsapp
Команда сгенерирует новый QR-код. Отсканируйте его заново — и сессия восстановится. С временными разрывами (моргнула сеть, телефон ненадолго ушёл в офлайн) шлюз справляется сам благодаря логике переподключения.
Голосовые сообщения
В WhatsApp у Hermes есть поддержка голоса:
- Входящие: голосовые (
.oggopus) автоматически расшифровываются настроенным STT-провайдером: локальныйfaster-whisper, Groq Whisper (GROQ_API_KEY) или OpenAI Whisper (VOICE_TOOLS_OPENAI_KEY) - Исходящие: ответы TTS отправляются вложением — аудиофайлом MP3
- По умолчанию ответы агента предваряются заголовком «☤ Hermes Agent». Его можно изменить или вовсе убрать в
config.yaml:
# ~/.hermes/config.yaml
whatsapp:
reply_prefix: "" # Empty string disables the header
# reply_prefix: "🤖 *My Bot*\n──────\n" # Custom prefix (supports \n for newlines)
send_read_receipts: false # Mark accepted inbound messages as read (blue ticks)
Когда send_read_receipts установлен в true, адаптер помечает прошедшие политику входящие сообщения как прочитанные после фильтрации по DM/группе/упоминанию. Отклонённые сообщения (например, от отправителей не из allowlist) прочитанными не помечаются. По умолчанию отключено — ради приватности. При изменении этой настройки подпроцесс моста автоматически перезапускается при следующем подключении.
Форматирование и доставка сообщений
WhatsApp умеет показывать потоковые (постепенные) ответы — бот правит своё сообщение в реальном времени по мере того, как ИИ генерирует текст, ровно как в Discord и Telegram. Внутри по возможностям доставки WhatsApp отнесён к платформам уровня TIER_MEDIUM.
Разбивка на части
Длинные ответы автоматически режутся на несколько сообщений по 4096 символов на фрагмент (практический предел отображения в WhatsApp). Настраивать ничего не нужно — шлюз сам делит текст и отправляет фрагменты по очереди.
Markdown под WhatsApp
Обычный Markdown в ответах ИИ автоматически переводится в родное форматирование WhatsApp:
| Markdown | Как отображается | |
|---|---|---|
**bold** | *bold* | жирный |
~~strikethrough~~ | ~strikethrough~ | |
# Heading | *Heading* | Жирный текст (родных заголовков нет) |
[link text](url) | link text (url) | URL в тексте |
Блоки кода и инлайн-код остаются как есть: тройные бэктики WhatsApp поддерживает изначально.
Прогресс по инструментам
Когда агент вызывает инструменты (веб-поиск, операции с файлами и прочее), WhatsApp показывает живые индикаторы прогресса: видно, какой инструмент сейчас работает. Это включено по умолчанию — настраивать ничего не надо.
Нативные опросы, clarify-как-опрос и геолокация
Адаптер на Baileys-мосту (режим бота) поддерживает несколько нативных типов сообщений WhatsApp:
- Опросы — агент может отправить нативный опрос WhatsApp (вопрос + варианты) через эндпоинт
/send-pollмоста. Голоса за варианты возвращаются обратно в разговор. - Уточняющие вопросы как опросы — когда агент задаёт clarify-вопрос с несколькими вариантами ответа, он отображается как нативный опрос с одиночным выбором; тап по варианту отвечает на вопрос. Если отправить опрос не удалось, адаптер откатывается к обычному текстовому вопросу. Запросы подтверждения (approval) никогда не превращаются в опросы — опросы используются только для настоящих clarify-вопросов с выбором из нескольких вариантов.
- Геометки — агент может отправить нативную геометку (широта/долгота, опционально название/адрес) через
/send-location, а входящие отправленные локации (в том числе live-локации) доставляются агенту как сообщения с геолокацией.
Всё это работает «из коробки» в режиме бота (Baileys); настраивать ничего не нужно.
Пакетирование сообщений (debounce)
Каждое сообщение WhatsApp доставляет по отдельности, поэтому быстрая серия (пересланные подборки, разбитая на части вставка, многострочный текст) иначе порождала бы по отдельному вызову агента на каждый фрагмент — а это лишний расход токенов и несколько разрозненных ответов. Адаптер копит идущие подряд текстовые сообщения из одного чата и отправляет их единым запросом после короткой паузы (по умолчанию 0,3 с, до 1 с для очень длинных фрагментов; потолок — 2 с / 4 с). Подстраивается через config.yaml:
# ~/.hermes/config.yaml
gateway:
platforms:
whatsapp:
extra:
text_batch_delay_seconds: 0.3 # quiet period before flushing a batch (max 2.0)
text_batch_split_delay_seconds: 1.0 # extended delay near the split threshold (max 4.0)
Поставьте text_batch_delay_seconds: 0, чтобы каждое сообщение уходило сразу (пакетирование отключится).
Ответы с цитатой
Ответ на более раннее сообщение (цитата) даёт агенту процитированный текст как контекст. Цитата изображения, голосового, видео или документа вдобавок прикрепляет этот файл к ходу, так что «что это?» под процитированной картинкой работает — независимо от того, пришло вложение от другого человека или от самого бота (график из cron, сгенерированная картинка). С цитатой WhatsApp присылает лишь заглушку-миниатюру, поэтому файл берётся из кэша скачиваний моста (входящие медиа, в памяти на время жизни моста) или из локального индекса собственных отправок бота (последние 1000 сообщений); цитаты чего-то более старого приходят без вложения.
Устранение неполадок
| Проблема | Решение |
|---|---|
| QR-код не сканируется | Проверьте ширину терминала (от 60 столбцов). Попробуйте другой терминал. Убедитесь, что сканируете из нужного аккаунта WhatsApp (номер бота, а не личный). |
| QR-код истекает | Коды обновляются примерно раз в 20 секунд. Если время вышло, перезапустите hermes whatsapp. |
| Сессия не сохраняется | Проверьте, что каталог ~/.hermes/platforms/whatsapp/session существует и доступен на запись. В контейнере подключите его как постоянный том. |
| Неожиданный выход из аккаунта | После долгого простоя WhatsApp отвязывает устройства. Держите телефон включённым и в сети, при необходимости заново привяжитесь через hermes whatsapp. |
| Мост падает или зацикливается на переподключении | Перезапустите шлюз, обновите Hermes и выполните повторную привязку, если сессию обнулило изменение протокола WhatsApp. |
| Бот перестал работать после обновления WhatsApp | Обновите Hermes до свежей версии моста, затем привяжитесь заново. |
| macOS: «Node.js not installed», хотя node работает в терминале | Службы launchd не наследуют PATH вашей оболочки. Выполните hermes gateway install, чтобы перезаписать текущий PATH в plist, а затем hermes gateway start. Подробности — в документации по службе шлюза. |
| Сообщения не доходят | Убедитесь, что WHATSAPP_ALLOWED_USERS содержит номер отправителя (с кодом страны, без + и пробелов), либо задайте *, чтобы пропускать всех. Поставьте WHATSAPP_DEBUG=true в .env и перезапустите шлюз — тогда сырые события сообщений будут видны в bridge.log. |
| Бот отвечает незнакомцам кодом привязки | Задайте whatsapp.unauthorized_dm_behavior: ignore в ~/.hermes/config.yaml, если хотите, чтобы неавторизованные личные сообщения молча игнорировались. |
Безопасность
Настройте контроль доступа до запуска в бой. Пропишите в WHATSAPP_ALLOWED_USERS конкретные
номера телефонов (с кодом страны, без +), поставьте *, чтобы пропускать всех, либо задайте
WHATSAPP_ALLOW_ALL_USERS=true. Без любой из этих настроек шлюз отклоняет все входящие
сообщения — в целях безопасности.
По умолчанию неавторизованные личные сообщения всё же получают в ответ код привязки. Если хотите, чтобы приватный номер WhatsApp полностью молчал для незнакомцев, задайте:
whatsapp:
unauthorized_dm_behavior: ignore
- В каталоге
~/.hermes/platforms/whatsapp/sessionлежат полные учётные данные сессии — берегите его как пароль - Выставьте права на файлы:
chmod 700 ~/.hermes/platforms/whatsapp/session - Используйте отдельный номер телефона под бота, чтобы изолировать риск от личного аккаунта
- Заподозрили компрометацию — отвяжите устройство через WhatsApp → Настройки → Связанные устройства
- Номера телефонов в логах частично затираются, но всё же пересмотрите свою политику хранения логов