Weixin (WeChat)
Подключите VibeOS к WeChat (微信) — платформе личных сообщений от Tencent. Адаптер использует API iLink Bot от Tencent для личных аккаунтов WeChat — это отличается от WeCom (корпоративного WeChat). Сообщения доставляются через long-polling, поэтому публичный endpoint или webhook не требуются.
Этот адаптер предназначен для личных аккаунтов WeChat (微信). Если вам нужен корпоративный WeChat, используйте адаптер WeCom.
QR-логин подключает VibeOS к идентичности iLink-бота (например, a5ace6fd482e@im.bot), а не к полноценному скриптуемому личному аккаунту WeChat. Последствия:
- Идентичность iLink-бота обычно нельзя пригласить в обычные группы WeChat так, как обычный контакт.
- iLink обычно не доставляет события обычных групп WeChat (включая упоминания через
@личного аккаунта, использованного для QR-логина) на шлюз для большинства типов бот-аккаунтов. - Упоминание через
@личного аккаунта WeChat, который сканировал QR-код, не равнозначно упоминанию iLink-бота — бот является отдельной идентичностью. - Настройки
WEIXIN_GROUP_POLICY/WEIXIN_GROUP_ALLOWED_USERSниже действуют только тогда, когда iLink действительно возвращает события групп для вашего типа аккаунта. Если этого не происходит, сообщения из групп никогда не достигнут VibeOS независимо от политики.
На практике в большинстве развёртываний надёжно работают только личные сообщения (DM) iLink-боту. Если доставка групповых сообщений не работает после настройки, ограничение находится на стороне iLink, а не VibeOS. Шлюз записывает предупреждение WARNING при запуске, если WEIXIN_GROUP_POLICY установлено в любое значение, кроме disabled.
Предварительные требования
- Личный аккаунт WeChat
- Пакеты Python:
aiohttpиcryptography - Отображение QR-кода в терминале включено при установке VibeOS с дополнением
messaging
Установите необходимые зависимости:
pip install aiohttp cryptography
# Опционально: для отображения QR-кода в терминале
cd ~/.vibeos/vibeos-agent && uv pip install -e ".[messaging]"
Настройка
1. Запустите мастер настройки
Самый простой способ подключить аккаунт WeChat — через интерактивную настройку:
vibeos gateway setup
Выберите Weixin при появлении запроса. Мастер выполнит следующие действия:
- Запросит QR-код у API iLink Bot
- Отобразит QR-код в вашем терминале (или предоставит URL)
- Дождётся, пока вы отсканируете QR-код мобильным приложением WeChat
- Предложит подтвердить вход на телефоне
- Автоматически сохранит учётные данные аккаунта в
~/.vibeos/weixin/accounts/
После подтверждения вы увидите сообщение вида:
微信连接成功,account_id=your-account-id
Мастер сохраняет account_id, token и base_url, чтобы вам не пришлось настраивать их вручную.
2. Настройте переменные окружения
После первоначального QR-логина установите как минимум идентификатор аккаунта в ~/.vibeos/.env:
WEIXIN_ACCOUNT_ID=your-account-id
# Опционально: переопределить токен (обычно сохраняется автоматически после QR-логина)
# WEIXIN_TOKEN=your-bot-token
# Опционально: ограничить доступ
WEIXIN_DM_POLICY=open
WEIXIN_ALLOWED_USERS=user_id_1,user_id_2
# Опционально: восстановить устаревшее поведение разбиения многострочных сообщений
# WEIXIN_SPLIT_MULTILINE_MESSAGES=true
# Опционально: домашний канал для cron/уведомлений
WEIXIN_HOME_CHANNEL=chat_id
WEIXIN_HOME_CHANNEL_NAME=Home
3. Запустите шлюз
vibeos gateway
Адаптер восстановит сохранённые учётные данные, подключится к API iLink и начнёт long-polling для получения сообщений.
Возможности
- Long-poll транспорт — не требуется публичный endpoint, webhook или WebSocket
- QR-логин — настройка через сканирование кода с помощью
vibeos gateway setup - Личные сообщения (DM) — настраиваемые политики доступа; групповая переписка зависит от того, доставляет ли iLink события групп для подключённой идентичности (часто не работает для аккаунтов iLink-ботов — см. предупреждение выше)
- Поддержка медиа — изображения, видео, файлы и голосовые сообщения
- AES-128-ECB зашифрованный CDN — автоматическое шифрование/дешифрование всех медиа-передач
- Сохранение контекстных токенов — непрерывность ответов после перезапусков благодаря хранению на диске
- Форматирование Markdown — сохраняет Markdown, включая заголовки, таблицы и блоки кода, чтобы клиенты WeChat, поддерживающие Markdown, могли отображать его нативно
- Умная разбивка сообщений — сообщения остаются единым «пузырём», если не превышают лимит; только слишком большие данные разбиваются по логическим границам
- Индикаторы набора текста — отображает статус «печатает…» в клиенте WeChat, пока агент обрабатывает запрос
- Защита от SSRF — исходящие URL медиа проверяются перед загрузкой
- Дедупликация сообщений — скользящее окно в 5 минут предотвращает двойную обработку
- Автоматические повторные попытки с задержкой — восстановление после временных ошибок API
Параметры конфигурации
Устанавливаются в config.yaml в разделе platforms.weixin.extra:
| Ключ | По умолчанию | Описание |
|---|---|---|
account_id | — | Идентификатор аккаунта iLink Bot (обязательно) |
token | — | Токен iLink Bot (обязательно, сохраняется автоматически после QR-логина) |
base_url | https://ilinkai.weixin.qq.com | Базовый URL API iLink |
cdn_base_url | https://novac2c.cdn.weixin.qq.com/c2c | Базовый URL CDN для передачи медиа |
dm_policy | open | Доступ к личным сообщениям: open, allowlist, disabled, pairing |
group_policy | disabled | Доступ к группам: open, allowlist, disabled |
allow_from | [] | Идентификаторы пользователей, разрешённых для DM (при dm_policy=allowlist) |
group_allow_from | [] | Идентификаторы групп, разрешённых для ответов (при group_policy=allowlist) |
split_multiline_messages | false | При true разбивать многострочные ответы на несколько сообщений в чате (устаревшее поведение). При false оставлять многострочные ответы одним сообщением, если они не превышают лимит длины. |
text_batch_delay_seconds | 3.0 | Период тишины (в секундах) перед отправкой буферизованной пачки быстрых текстовых сообщений как одного объединённого запроса. iLink доставляет сообщения по одному, поэтому эта задержка предотвращает отдельный вызов агента на каждый фрагмент. Установите 0 для немедленной отправки каждого сообщения. |
text_batch_split_delay_seconds | 5.0 | Увеличенная задержка сброса, используемая, когда последний фрагмент близок к порогу разбиения (длинные сообщения, которые iLink мог разбить на части). |
Политики доступа
Политика DM
Определяет, кто может отправлять боту личные сообщения:
| Значение | Поведение |
|---|---|
open | Любой может писать боту в DM (по умолчанию) |
allowlist | Только идентификаторы пользователей из allow_from могут писать в DM |
disabled | Все DM игнорируются |
pairing | Режим сопряжения (для начальной настройки) |
WEIXIN_DM_POLICY=allowlist
WEIXIN_ALLOWED_USERS=user_id_1,user_id_2
WEIXIN_ALLOWED_USERS — это входящий фильтр, а не система приглашений. QR-логин подключает одну идентичность iLink-бота к VibeOS. Другие люди не сканируют QR-код VibeOS своими аккаунтами; они должны написать подключённому iLink-боту/контакту через WeChat, и VibeOS обработает DM только в том случае, если идентификатор отправителя в Weixin присутствует в WEIXIN_ALLOWED_USERS.
Практический порядок настройки:
- Один раз выполните сопряжение VibeOS с помощью
vibeos gateway setupи запишите подключённый аккаунт iLink-бота. - Попросите каждого разрешённого пользователя отправить личное сообщение этому боту/контакту.
- Прочитайте идентификатор отправителя/пользователя из логов шлюза или из полезной нагрузки входящего события.
- Добавьте эти идентификаторы в
WEIXIN_ALLOWED_USERS, затем перезапустите шлюз.
Если только аккаунт, сканировавший QR-код, может общаться с VibeOS, убедитесь, что другие пользователи пишут именно идентичности iLink-бота, а не личному аккаунту WeChat, выполнившему QR-логин. iLink-бот — это отдельная идентичность, и маршрутизация обычных контактов/групп WeChat может быть ограничена поведением iLink со стороны Tencent.
Политика групп
Определяет, в каких группах бот отвечает, когда iLink доставляет события групп для подключённой идентичности. Для идентичностей iLink-ботов, вошедших через QR (например, ...@im.bot), события групп обычно не доставляются вовсе, поэтому эта политика может не иметь эффекта — см. предупреждение об ограничениях iLink-бота в начале страницы.
| Значение | Поведение |
|---|---|
open | Бот отвечает во всех группах (если события доставляются) |
allowlist | Бот отвечает только в группах, перечисленных в group_allow_from (если события доставляются) |
disabled | Все групповые сообщения игнорируются (по умолчанию) |
WEIXIN_GROUP_POLICY=allowlist
# ВНИМАНИЕ: это список идентификаторов групповых чатов через запятую, а НЕ идентификаторов пользователей,
# несмотря на то, что имя переменной содержит "USERS". Учитывайте это при настройке.
WEIXIN_GROUP_ALLOWED_USERS=group_id_1,group_id_2
Политика групп по умолчанию — disabled для Weixin (в отличие от WeCom, где по умолчанию open). Это сделано намеренно — личные аккаунты WeChat могут состоять во многих группах, а идентичности iLink-ботов обычно вообще не могут получать обычные групповые сообщения WeChat. Шлюз записывает предупреждение WARNING при запуске, если вы устанавливаете WEIXIN_GROUP_POLICY в любое значение, кроме disabled.
Поддержка медиа
Входящие (получение)
Адаптер получает медиавложения от пользователей, загружает их с CDN WeChat, расшифровывает и кэширует локально для обработки агентом:
| Тип | Как обрабатывается |
|---|---|
| Изображения | Загружаются, расшифровываются AES и кэшируются как JPEG. |
| Видео | Загружается, расшифровывается AES и кэшируется как MP4. |
| Файлы | Загружаются, расшифровываются AES и кэшируются. Исходное имя файла сохраняется. |
| Голос | Если доступна текстовая расшифровка, она извлекается как текст. В противном случае аудио (формат SILK) загружается и кэшируется. |
Цитируемые сообщения: Медиа из цитируемых (на которые дан ответ) сообщений также извлекается, чтобы агент имел контекст того, на что отвечает пользователь.
Зашифрованный CDN AES-128-ECB
Медиафайлы WeChat передаются через зашифрованный CDN. Адаптер обрабатывает это прозрачно:
- Входящие: Зашифрованное медиа загружается с CDN по URL с
encrypted_query_param, затем расшифровывается с помощью AES-128-ECB, используя предоставленный в полезной нагрузке сообщения ключ для каждого файла. - Исходящие: Файлы шифруются локально с помощью случайного ключа AES-128-ECB, загружаются на CDN, и зашифрованная ссылка включается в исходящее сообщение.
- Ключ AES имеет длину 16 байт (128 бит). Ключи могут поступать в виде raw base64 или hex-кодировки — адаптер обрабатывает оба формата.
- Требуется пакет Python
cryptography.
Никакой настройки не требуется — шифрование и дешифрование происходят автоматически.
Исходящие (отправка)
| Метод | Что отправляет |
|---|---|
send | Текстовые сообщения с форматированием Markdown |
send_image / send_image_file | Нативные сообщения с изображениями (через загрузку на CDN) |
send_document | Вложения файлов (через загрузку на CDN) |
send_video | Видеосообщения (через загрузку на CDN) |
Все исходящие медиа проходят через зашифрованный поток загрузки на CDN:
- Генерация случайного ключа AES-128
- Шифрование файла с помощью AES-128-ECB + дополнение PKCS#7
- Запрос URL для загрузки у API iLink (
getuploadurl) - Загрузка шифротекста на CDN
- Отправка сообщения с зашифрованной медиа-ссылкой
Сохранение контекстных токенов
API iLink Bot требует, чтобы context_token возвращался с каждым исходящим сообщением для данного собеседника. Адаптер поддерживает хранилище контекстных токенов на диске:
- Токены сохраняются для каждого аккаунта+собеседника в
~/.vibeos/weixin/accounts/<account_id>.context-tokens.json - При запуске ранее сохранённые токены восстанавливаются
- Каждое входящее сообщение обновляет сохранённый токен для этого отправителя
- Исходящие сообщения автоматически включают последний контекстный токен
Это обеспечивает непрерывность ответов даже после перезапусков шлюза.
Форматирование Markdown
Клиенты WeChat, подключённые через API iLink Bot, могут отображать Markdown напрямую, поэтому адаптер сохраняет Markdown вместо его перезаписи:
- Заголовки остаются как заголовки Markdown (
#,##, ...) - Таблицы остаются как таблицы Markdown
- Блоки кода остаются как ограждённые блоки кода
- Чрезмерные пустые строки сворачиваются до двойных новых строк вне ограждённых блоков кода
Разбивка сообщений
Сообщения доставляются как одно сообщение в чате, если они помещаются в лимит платформы. Только слишком большие полезные нагрузки разбиваются для доставки:
- Максимальная длина сообщения: 4000 символов
- Сообщения, не превышающие лимит, остаются целыми, даже если содержат несколько абзацев или переносов строк
- Сообщения, превышающие лимит, разбиваются по логическим границам (абзацы, пустые строки, блоки кода)
- Блоки кода по возможности сохраняются целыми (никогда не разбиваются внутри блока, если только сам блок не превышает лимит)
- Отдельные блоки, превышающие лимит, возвращаются к логике усечения базового адаптера
- Задержка между фрагментами в 0,3 с предотвращает сброс из-за лимитов скорости WeChat при отправке нескольких фрагментов
Индикаторы набора текста
Адаптер отображает статус набора текста в клиенте WeChat:
- Когда приходит сообщение, адаптер получает
typing_ticketчерез APIgetconfig - Тикет набора текста кэшируется на 10 минут для каждого пользователя
send_typingотправляет сигнал начала набора;stop_typingотправляет сигнал остановки набора- Шлюз автоматически запускает индикаторы набора текста, пока агент обрабатывает сообщение
Long-poll соединение
Адаптер использует HTTP long-polling (не WebSocket) для получения сообщений:
Как это работает
- Подключение: Проверяет учётные данные и запускает цикл опроса
- Опрос: Вызывает
getupdatesс таймаутом 35 секунд; сервер удерживает запрос, пока не придут сообщения или не истечёт таймаут - Обработка: Входящие сообщения обрабатываются конкурентно через
asyncio.create_task - Буфер синхронизации: Постоянный курсор синхронизации (
get_updates_buf) сохраняется на диск, чтобы адаптер возобновлял работу с правильной позиции после перезапусков
Поведение при повторных попытках
При ошибках API адаптер использует простую стратегию повторных попыток:
| Условие | Поведение |
|---|---|
| Временная ошибка (1–2 раза) | Повтор через 2 секунды |
| Повторяющиеся ошибки (3+) | Ожидание 30 секунд, затем сброс счётчика |
Истечение сессии (errcode=-14) | Пауза на 10 минут (может потребоваться повторный вход) |
| Таймаут | Немедленный повторный опрос (нормальное поведение long-poll) |
Дедупликация
Входящие сообщения дедуплицируются по идентификаторам сообщений с окном в 5 минут. Это предотвращает двойную обработку при сетевых сбоях или перекрывающихся ответах опроса.
Блокировка токена
Только один экземпляр шлюза Weixin может использовать данный токен одновременно. Адаптер получает ограниченную блокировку при запуске и освобождает её при остановке. Если другой шлюз уже использует тот же токен, запуск завершается с информативным сообщением об ошибке.
Все переменные окружения
| Переменная | Обязательная | По умолчанию | Описание |
|---|---|---|---|
WEIXIN_ACCOUNT_ID | ✅ | — | Идентификатор аккаунта iLink Bot (из QR-логина) |
WEIXIN_TOKEN | ✅ | — | Токен iLink Bot (сохраняется автоматически после QR-логина) |
WEIXIN_BASE_URL | — | https://ilinkai.weixin.qq.com | Базовый URL API iLink |
WEIXIN_CDN_BASE_URL | — | https://novac2c.cdn.weixin.qq.com/c2c | Базовый URL CDN для передачи медиа |
WEIXIN_DM_POLICY | — | open | Политика доступа к DM: open, allowlist, disabled, pairing |
WEIXIN_GROUP_POLICY | — | disabled | Политика доступа к группам: open, allowlist, disabled |
WEIXIN_ALLOWED_USERS | — | (пусто) | Идентификаторы пользователей через запятую для белого списка DM |
WEIXIN_GROUP_ALLOWED_USERS | — | (пусто) | Идентификаторы групповых чатов (не идентификаторы пользователей) через запятую для белого списка групп. Имя переменной устаревшее — она ожидает идентификаторы групп, а не пользователей. |
WEIXIN_HOME_CHANNEL | — | — | Идентификатор чата для вывода cron/уведомлений |
WEIXIN_HOME_CHANNEL_NAME | — | Home | Отображаемое имя для домашнего канала |
WEIXIN_ALLOW_ALL_USERS | — | — | Флаг уровня шлюза для разрешения всем пользователям (используется мастером настройки) |
Устранение неполадок
| Проблема | Решение |
|---|---|
Weixin startup failed: aiohttp and cryptography are required | Установите оба пакета: pip install aiohttp cryptography |
Weixin startup failed: WEIXIN_TOKEN is required | Запустите vibeos gateway setup для завершения QR-логина или установите WEIXIN_TOKEN вручную |
Weixin startup failed: WEIXIN_ACCOUNT_ID is required | Установите WEIXIN_ACCOUNT_ID в вашем .env или запустите vibeos gateway setup |
Another local VibeOS gateway is already using this Weixin token | Сначала остановите другой экземпляр шлюза — разрешён только один опросчик на токен |
Истечение сессии (errcode=-14) | Ваша сессия истекла. Повторно запустите vibeos gateway setup для сканирования нового QR-кода |
| QR-код истёк во время настройки | QR автоматически обновляется до 3 раз. Если он продолжает истекать, проверьте сетевое подключение |
| Бот не отвечает на DM | Проверьте WEIXIN_DM_POLICY — если установлено allowlist, отправитель должен быть в WEIXIN_ALLOWED_USERS |
| Бот игнорирует групповые сообщения | Политика групп по умолчанию — disabled. Установите WEIXIN_GROUP_POLICY=open или allowlist — но учтите, что идентичности iLink-ботов, вошедших через QR (...@im.bot), обычно вообще не могут получать обычные групповые сообщения WeChat. Если в логах шлюза нет сырых входящих событий для групповых сообщений, ограничение на стороне iLink, а не VibeOS. |
| Загрузка/выгрузка медиа не удаётся | Убедитесь, что установлен cryptography. Проверьте сетевой доступ к novac2c.cdn.weixin.qq.com |
Blocked unsafe URL (SSRF protection) | URL исходящего медиа указывает на частный/внутренний адрес. Разрешены только публичные URL |
| Голосовые сообщения отображаются как текст | Если WeChat предоставляет расшифровку, адаптер использует текст. Это ожидаемое поведение |
| Сообщения дублируются | Адаптер дедуплицирует по идентификатору сообщения. Если вы видите дубликаты, проверьте, не запущено ли несколько экземпляров шлюза |
iLink POST ... HTTP 4xx/5xx | Ошибка API от сервиса iLink. Проверьте действительность токена и сетевое подключение |
| QR-код в терминале не отображается | Переустановите с дополнением messaging: cd ~/.vibeos/vibeos-agent && uv pip install -e ".[messaging]". Альтернативно, откройте URL, напечатанный над QR-кодом |