Треть рассылки в новом мессенджере курьерам не дошла. Мы нашли подставленный ID — и дотянули её до 903 из 1442

Первая массовая рассылка в новом мессенджере обычно проверяет не мессенджер, а вашу базу данных.
Первая массовая рассылка в новом мессенджере обычно проверяет не мессенджер, а вашу базу данных.

У нашего курьерского парка два канала связи с водителями: старый — Telegram, и новый — отечественный мессенджер MAX, куда недавно добавили фирменного бота. Чтобы курьеры узнали, что в MAX заработала партнёрская программа «пригласи друга — получи бонус», мы подготовили рассылку на 1442 адресата — всех, у кого в карточке уже стоял мессенджер MAX.

Как увидели

Рассылка ушла вечером. Отчёт пришёл почти сразу: 857 доставлено, 10 человек заблокировали бота — нормальная цифра для любой массовой отправки. А вот третья строка была неожиданной: 564 сообщения, больше трети базы, вернулись с ошибкой «диалог не найден».

Для бота, которому человек никогда не писал, такая ошибка — штатная вещь: значит, диалога действительно ещё нет. Но здесь все 564 адреса принадлежали людям, которые уже числились подключёнными к MAX — то есть диалог должен был существовать.

Как решили

Стали разбирать конкретные идентификаторы. У любого мессенджера, где есть боты, обычно два разных числа на одного человека: постоянный ID самого пользователя и ID конкретного диалога с ним. Оба выглядят как просто числа и даже случайно попадают в похожий диапазон — поэтому подмену легко не заметить, если проверять вручную десяток карточек.

Разбор показал: когда мы переносили контакты курьеров из прежней CRM в карточку мессенджера MAX, в поле «идентификатор пользователя» записали ID диалога оттуда, а не ID самого человека. Платформа честно слала сообщение по этому числу — просто адресовала его диалогу, которого к моменту отправки уже не существовало: диалоги в старой системе переживали переносы и объединения, а id человека — нет.

Чинить пришлось не формулу, а сами данные. Для каждой из 1345 карточек запросили у самого мессенджера настоящий, устойчивый идентификатор человека — через тот диалог, который записан в карточке сейчас, — и заменили подставленное число на него. Попутно вскрылось, что 41 курьер существовал в базе в виде двух разных карточек одного и того же человека: бот не узнал его при повторном входе и создал вторую запись. Карточки склеили, сохранив баланс бонусов и историю обеих. Ещё 47 карточек с идентификатором, который вообще никуда не вёл, пометили нерабочими — слать туда больше не пытаемся.

Результат

По исправленным 1345 адресам сделали повторную отправку — туда, где диалог теперь действительно существовал. Из 564 неудачных адресов 554 оказались исправимыми: 46 сообщений дошли, 458 — это курьеры, которые заблокировали бота задолго до рассылки (не ошибка данных, а выбор человека, этих адресов не вернуть), и 50 так и не нашлись — видимо, устаревшие учётки, которыми давно никто не пользуется.

Итог: рассылка дошла до 903 из 1442 адресатов (63%) вместо изначальных 857 (59%). И что важнее самих 46 новых доставок — база для каждой следующей рассылки в MAX стала чище на 41 дубль и 47 мёртвых записей.

Что я для себя унёс

Поле с похожим названием в двух разных системах — не одно и то же поле. «Идентификатор пользователя» в старой CRM и в новом мессенджере технически совпадали по типу — оба просто числа, — но означали разные вещи: диалог против человека. Такую подмену не ловит ни юнит-тест, ни ручная проверка десятка карточек — числа выглядят правдоподобно в обоих случаях. Единственный надёжный способ её найти — отправить реальное сообщение на реальную, не отобранную вручную выборку и честно прочитать отчёт о неудачах, а не только о доставленных. Если переносите пользователей между системами — особенно между разными мессенджерами, — проверяйте идентификатор прямым вызовом API хотя бы один раз перед первой массовой отправкой, а не доверяйте полю только потому, что оно называется знакомо.