WeCom (корпоративный WeChat)

Команды установки Python-зависимостей на этой странице рассчитаны на чекаут исходников, подготовленный через PM. После изменения зависимостей заново активируйте чекаут и перезапустите Hermes.

Подключите Hermes к WeCom (企业微信) — корпоративному мессенджеру от Tencent. Адаптер работает через WebSocket-шлюз AI Bot, поэтому связь двусторонняя и идёт в реальном времени: ни публичный эндпоинт, ни вебхук не нужны.

См. также: WeCom Callback — настройка входящего вебхука.

Предварительные требования

  • Организационный аккаунт WeCom
  • AI Bot, созданный в админ-консоли WeCom
  • Bot ID и Secret со страницы учётных данных бота
  • Python-пакеты: aiohttp и httpx

Настройка

Шаг 1. Создайте AI Bot

Рекомендуемый способ: создание по QR-коду (одна команда)

hermes gateway setup

Выберите WeCom и отсканируйте QR-код мобильным приложением WeCom. Hermes сам создаст бот-приложение с нужными правами и сохранит учётные данные.

Мастер настройки выполнит следующее:

  1. Покажет QR-код прямо в терминале
  2. Подождёт, пока вы отсканируете его приложением WeCom
  3. Автоматически получит Bot ID и Secret
  4. Проведёт вас через настройку контроля доступа

Альтернатива: ручная настройка

Если создание по QR-коду недоступно, мастер переключается на ручной ввод:

  1. Войдите в админ-консоль WeCom
  2. Перейдите в Applications → Create Application → AI Bot
  3. Задайте имя и описание бота
  4. Скопируйте Bot ID и Secret со страницы учётных данных
  5. Запустите hermes gateway setup, выберите WeCom и введите учётные данные по запросу
внимание

Храните Bot Secret в секрете. Любой, у кого он есть, сможет выдать себя за вашего бота.

Шаг 2. Настройте Hermes

Вариант A: интерактивная настройка (рекомендуется)

hermes gateway setup

Выберите WeCom и следуйте подсказкам. Мастер проведёт вас через:

  • Учётные данные бота (по QR-коду или вручную)
  • Параметры контроля доступа (список разрешений, режим сопряжения или открытый доступ)
  • Домашний канал для уведомлений

Вариант B: ручная конфигурация

Добавьте в ~/.hermes/.env следующее:

WECOM_BOT_ID=your-bot-id
WECOM_SECRET=your-secret

# Опционально: ограничить доступ
WECOM_ALLOWED_USERS=user_id_1,user_id_2

# Опционально: домашний канал для cron/уведомлений
WECOM_HOME_CHANNEL=chat_id

Шаг 3. Запустите шлюз

hermes gateway

Возможности

  • Транспорт WebSocket — постоянное соединение, публичный эндпоинт не нужен
  • Личные и групповые сообщения — настраиваемые политики доступа
  • Списки разрешённых отправителей для каждой группы — точный контроль над тем, кто общается с ботом в конкретной группе
  • Поддержка медиа — загрузка и скачивание изображений, файлов, голоса и видео
  • Медиа с AES-шифрованием — входящие вложения расшифровываются автоматически
  • Контекст цитирования — сохраняется привязка ответа к ветке
  • Рендеринг Markdown — форматированные ответы
  • Корреляция ответов — ответ привязывается к контексту входящего сообщения
  • Автопереподключение — экспоненциальная выдержка при обрывах связи
заметка
Стриминг и индикаторы набора

Адаптер WeCom стримит ответы нативно через протокол WeCom msgtype: "stream": клиент показывает пузырь «печатает» сразу после начала хода, а ответ рендерится по токенам в одном и том же пузыре по мере генерации. Прогресс вызова инструментов сворачивается в тот же пузырь. Нативный стриминг подчиняется глобальному переключателю стриминга, который по умолчанию выключен: включите его через streaming.enabled: true в config.yaml. Платформенная настройка WeCom display.platforms.wecom.streaming (по умолчанию true) действует, только пока глобальный переключатель включён; поставьте false, чтобы в WeCom сохранить доставку одним сообщением.

Параметры конфигурации

Задаются в config.yaml в секции platforms.wecom.extra:

КлючПо умолчаниюОписание
bot_id—ID бота WeCom AI Bot (обязательно)
secret—Secret бота WeCom AI Bot (обязательно)
websocket_urlwss://openws.work.weixin.qq.comURL WebSocket-шлюза
dm_policyopenДоступ к личным сообщениям: open, allowlist, disabled, pairing
group_policyopenДоступ к группам: open, allowlist, disabled
allow_from[]ID пользователей, которым разрешены личные сообщения (при dm_policy=allowlist)
group_allow_from[]ID разрешённых групп (при group_policy=allowlist)
groups{}Настройки по каждой группе (см. ниже)
stream_keepalive_enabledfalseОтправлять периодические keepalive-кадры, чтобы продлевать ~6-минутное окно reply-стрима WeCom на длинных ходах
stream_keepalive_interval_seconds120Периодичность keepalive-кадров, когда включено
stream_safe_duration_seconds330Возраст стрима, после которого финализация предпочитает надёжную проактивную отправку

Политики доступа

Политика личных сообщений

Определяет, кто может писать боту в личку:

ЗначениеПоведение
openПисать боту может кто угодно (по умолчанию)
allowlistПисать могут только ID пользователей из allow_from
disabledВсе личные сообщения игнорируются
pairingРежим сопряжения (для первичной настройки)
WECOM_DM_POLICY=allowlist

Политика групп

Определяет, в каких группах бот отвечает:

ЗначениеПоведение
openБот отвечает во всех группах (по умолчанию)
allowlistБот отвечает только в группах, чьи ID перечислены в group_allow_from
disabledВсе групповые сообщения игнорируются
WECOM_GROUP_POLICY=allowlist

Списки разрешённых отправителей для каждой группы

Чтобы управлять доступом более тонко, можно ограничить круг пользователей, которым разрешено общаться с ботом внутри конкретных групп. Настраивается в config.yaml:

platforms:
  wecom:
    enabled: true
    extra:
      bot_id: "your-bot-id"
      secret: "your-secret"
      group_policy: "allowlist"
      group_allow_from:
        - "group_id_1"
        - "group_id_2"
      groups:
        group_id_1:
          allow_from:
            - "user_alice"
            - "user_bob"
        group_id_2:
          allow_from:
            - "user_charlie"
        "*":
          allow_from:
            - "user_admin"

Как это работает:

  1. Параметры group_policy и group_allow_from решают, разрешена ли группа в принципе.
  2. Если группа прошла проверку верхнего уровня, список groups.<group_id>.allow_from (если он задан) дополнительно ограничивает, кто из участников этой группы может общаться с ботом.
  3. Запись с шаблоном "*" задаёт значение по умолчанию для групп, не перечисленных явно.
  4. В списке разрешений работает шаблон * — он пропускает всех пользователей; регистр в записях не учитывается.
  5. Записи можно при желании указывать с префиксом wecom:user: или wecom:group: — префикс отбрасывается автоматически.

Если для группы не задан allow_from, разрешены все её участники (при условии, что сама группа прошла проверку политики верхнего уровня).

Поддержка медиа

Входящие (приём)

Адаптер принимает медиавложения от пользователей и кеширует их локально, чтобы агент мог их обработать:

ТипКак обрабатывается
ИзображенияСкачиваются и кешируются локально. Поддерживаются как ссылки на изображения, так и base64.
ФайлыСкачиваются и кешируются. Имя файла берётся из исходного сообщения.
ГолосЕсли доступна расшифровка голосового сообщения, её текст извлекается.
Смешанные сообщенияСообщения WeCom смешанного типа (текст + изображения) разбираются, все составляющие извлекаются.

Цитируемые сообщения: медиа из цитируемых сообщений (на которые отвечают) тоже извлекается, чтобы у агента был контекст того, на что отвечает пользователь.

Расшифровка медиа с AES-шифрованием

Часть входящих медиавложений WeCom шифрует алгоритмом AES-256-CBC. Адаптер делает это сам:

  • Когда у входящего медиа есть поле aeskey, адаптер скачивает зашифрованные байты и расшифровывает их по AES-256-CBC с дополнением PKCS#7.
  • Ключ AES — это значение поля aeskey, раскодированное из base64 (должно быть ровно 32 байта).
  • IV берётся из первых 16 байт ключа.
  • Понадобится Python-пакет cryptography (hermes pm repair).

Настраивать ничего не нужно: расшифровка происходит прозрачно при получении зашифрованного медиа.

Исходящие (отправка)

МетодЧто отправляетЛимит размера
sendТекстовые сообщения в Markdown4000 символов на сообщение; более длинный текст разбивается на несколько сообщений
send_image / send_image_fileНативные сообщения с изображением10 МБ
send_documentФайловые вложения20 МБ
send_voiceГолосовые сообщения (для нативного голоса только формат AMR)2 МБ
send_videoВидеосообщения10 МБ

Загрузка по частям: файлы загружаются кусками по 512 КБ через трёхэтапный протокол (init → chunks → finish). Адаптер делает это сам.

Автоматическое понижение: если медиа превышает лимит размера для своего нативного типа, но укладывается в абсолютный лимит файла в 20 МБ, оно автоматически отправляется как обычное файловое вложение:

  • Изображения > 10 МБ → отправляются файлом
  • Видео > 10 МБ → отправляются файлом
  • Голос > 2 МБ → отправляется файлом
  • Аудио не в AMR → отправляется файлом (для нативного голоса WeCom поддерживает только AMR)

Файлы сверх абсолютного лимита в 20 МБ отклоняются, а в чат отправляется информационное сообщение.

Ответы в режиме reply

Получив сообщение через WeCom callback, адаптер запоминает ID входящего запроса. Если ответ отправляется, пока контекст запроса ещё активен, адаптер использует режим reply WeCom (aibot_respond_msg) и привязывает ответ напрямую к входящему сообщению. В клиенте WeCom разговор от этого выглядит естественнее.

Когда активен нативный reply-стрим, ответ передаётся постепенно через кадры reply-режима msgtype: "stream". Если контекст входящего запроса истёк, недоступен или кадр стрима не прошёл, адаптер переключается на проактивную отправку через aibot_send_msg.

Режим reply работает и с медиа: загруженный файл можно отправить ответом на исходное сообщение.

Соединение и переподключение

Адаптер держит постоянное WebSocket-соединение со шлюзом WeCom по адресу wss://openws.work.weixin.qq.com.

Жизненный цикл соединения

  1. Подключение: открывается WebSocket-соединение и отправляется кадр аутентификации aibot_subscribe с bot_id и secret.
  2. Heartbeat: каждые 30 секунд уходят ping-кадры на уровне приложения — они держат соединение живым.
  3. Прослушивание: адаптер непрерывно читает входящие кадры и вызывает колбэки сообщений.

Поведение при переподключении

При потере связи адаптер переподключается с экспоненциальной выдержкой:

ПопыткаЗадержка
1-я попытка2 секунды
2-я попытка5 секунд
3-я попытка10 секунд
4-я попытка30 секунд
5-я и далее60 секунд

После каждого успешного переподключения счётчик выдержки сбрасывается в ноль. При обрыве все ожидающие futures запросов завершаются с ошибкой, чтобы вызывающий код не завис навсегда.

Дедупликация

Входящие сообщения дедуплицируются по ID сообщений с окном в 5 минут и кешем не более чем на 1000 записей. Так одно и то же сообщение не обрабатывается дважды во время переподключения или сетевых сбоев.

Все переменные окружения

ПеременнаяОбязательнаПо умолчаниюОписание
WECOM_BOT_ID✅—ID бота WeCom AI Bot
WECOM_SECRET✅—Secret бота WeCom AI Bot
WECOM_ALLOWED_USERS—(пусто)ID пользователей через запятую для списка разрешений на уровне шлюза
WECOM_HOME_CHANNEL——ID чата для вывода cron/уведомлений
WECOM_WEBSOCKET_URL—wss://openws.work.weixin.qq.comURL WebSocket-шлюза
WECOM_DM_POLICY—openПолитика доступа к личным сообщениям
WECOM_GROUP_POLICY—openПолитика доступа к группам

Решение проблем

ПроблемаЧто делать
WECOM_BOT_ID and WECOM_SECRET are requiredЗадайте обе переменные окружения или настройте их в мастере
WeCom startup failed: aiohttp not installedУстановите aiohttp: python -c "import pm; pm.sync_venv(['messaging'], explicit=True)"
WeCom startup failed: httpx not installedУстановите httpx: hermes pm repair
invalid secret (errcode=40013)Проверьте, что secret совпадает с учётными данными бота
Timed out waiting for subscribe acknowledgementПроверьте сетевой доступ к openws.work.weixin.qq.com
Бот не отвечает в группахПроверьте параметр group_policy и убедитесь, что ID группы есть в group_allow_from
Бот игнорирует некоторых пользователей в группеПроверьте списки allow_from по группам в секции groups
Не удаётся расшифровать медиаУстановите cryptography: hermes pm repair
cryptography is required for WeCom media decryptionВходящее медиа зашифровано AES. Установите: hermes pm repair
Голосовые сообщения уходят файламиДля нативного голоса WeCom поддерживает только формат AMR. Остальные форматы автоматически понижаются до файла.
Ошибка File too largeУ WeCom абсолютный лимит на любую загрузку файла — 20 МБ. Сожмите или разбейте файл.
Изображения уходят файламиИзображения > 10 МБ выходят за нативный лимит и автоматически понижаются до файловых вложений.
Timeout sending message to WeComВозможно, WebSocket отвалился. Поищите в логах сообщения о переподключении.
WeCom websocket closed during authenticationПроблема с сетью или неверные учётные данные. Проверьте bot_id и secret.
перевод и редактура:

обновлено

⌕ ESC