Массовая отправка в Telegram без блокировок
Архитектура очередей для массовой рассылки в Telegram: rate limiter, DLQ, мониторинг и отказоустойчивость.
Почему массовая отправка в Telegram — это сложно
Telegram разрабатывался как мессенджер для личного общения, а не как канал массовых рассылок. Из-за этого Bot API имеет ограничения на скорость отправки: обычно порядка 30 сообщений в секунду в разные чаты и около 20 сообщений в минуту в один чат (значения могут меняться). Подробнее — в нашей статье лимиты Telegram API.
При наивной реализации рассылки на 100 000 подписчиков вы столкнётесь со следующими проблемами:
- Время отправки: при лимите порядка 30 msg/s 100 000 сообщений займут около часа
- Ошибки 429: неизбежны без rate limiter (см. разбор ошибки 429)
- Потеря сообщений: если процесс упадёт на 50%, вы не знаете кому отправлено
- Заблокированные пользователи: часть пользователей блокирует бота
- Вариативные ошибки: CHAT_NOT_FOUND, USER_DEACTIVATED, BOT_BLOCKED
Наблюдение: по данным проектов в Релая, при массовых рассылках заметная доля сообщений получает ошибки, не связанные с rate limiting (бот заблокирован, чат удалён и т.д.). Это нормально и должно быть предусмотрено в архитектуре.
Архитектура с учётом rate limiting
Правильная архитектура массовой отправки выглядит так:
Рендер схемы...
Ключевые компоненты:
- Scheduler — разбивает рассылку на батчи и помещает в очередь
- Message Queue — надёжное хранилище задач (Redis, RabbitMQ)
- Rate Limiter — контролирует скорость выдачи задач воркерам
- Worker Pool — отправляет сообщения через Telegram API
- DLQ — хранит неудачные отправки для анализа
- Monitoring — отслеживает прогресс и ошибки
Проектирование очереди (Redis / RabbitMQ)
Выбор системы очередей зависит от масштаба. Для большинства случаев Redis (с использованием BullMQ) — практичный выбор:
| Критерий | Redis (BullMQ) | RabbitMQ |
|---|---|---|
| Простота настройки | Высокая | Средняя |
| Rate limiting | Встроен в BullMQ | Через плагин |
| Масштабируемость | До ~1M задач | Миллионы задач |
| Retry-логика | Встроена | Через DLX |
| Мониторинг | Bull Board | Management UI |
| Гарантия доставки | At-least-once | At-least-once / At-most-once |
Паттерн Worker Pool
Worker Pool — основа масштабируемой отправки. Вот реализация на Node.js с использованием BullMQ:
// worker.js — воркер массовой отправки в Telegram
import { Worker } from "bullmq";
import IORedis from "ioredis";
const connection = new IORedis({ host: "localhost", port: 6379 });
const TELEGRAM_TOKEN = process.env.TELEGRAM_BOT_TOKEN;
const worker = new Worker(
"telegram-broadcast",
async (job) => {
const { chatId, text, parseMode } = job.data;
const res = await fetch(
`https://api.telegram.org/bot${TELEGRAM_TOKEN}/sendMessage`,
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
chat_id: chatId,
text,
parse_mode: parseMode || "HTML",
}),
}
);
if (res.status === 429) {
const body = await res.json();
const retryAfter = body.parameters?.retry_after || 5;
// Бросаем ошибку — BullMQ сделает retry через retryAfter сек
throw new Error(`RATE_LIMITED:${retryAfter}`);
}
if (res.status === 403) {
// Пользователь заблокировал бота — не ретраим
return { status: "blocked", chatId };
}
if (!res.ok) {
throw new Error(`Telegram API error: ${res.status}`);
}
return { status: "sent", chatId };
},
{
connection,
concurrency: 5, // 5 параллельных воркеров
limiter: {
max: 25, // не более 25 задач
duration: 1000, // за 1 секунду
},
}
);
worker.on("completed", (job, result) => {
console.log(`✓ ${job.id}: ${result.status} (${result.chatId})`);
});
worker.on("failed", (job, err) => {
console.error(`✗ ${job?.id}: ${err.message}`);
});И код для добавления задач в очередь:
// enqueue.js — постановка задач рассылки в очередь
import { Queue } from "bullmq";
import IORedis from "ioredis";
const connection = new IORedis({ host: "localhost", port: 6379 });
const queue = new Queue("telegram-broadcast", { connection });
async function startBroadcast(userIds, message) {
console.log(`Начинаем рассылку на ${userIds.length} пользователей...`);
const jobs = userIds.map((chatId, idx) => ({
name: `msg-${chatId}`,
data: { chatId, text: message },
opts: {
attempts: 3,
backoff: { type: "exponential", delay: 5000 },
removeOnComplete: { age: 3600 }, // убираем через 1 час
removeOnFail: { age: 24 * 3600 }, // ошибки храним сутки
priority: idx < 1000 ? 1 : 10, // первые 1000 — приоритет
},
}));
// Добавляем батчами по 1000
for (let i = 0; i < jobs.length; i += 1000) {
const batch = jobs.slice(i, i + 1000);
await queue.addBulk(batch);
console.log(`Добавлено ${Math.min(i + 1000, jobs.length)} / ${jobs.length}`);
}
console.log("Все задачи добавлены в очередь");
}
// Запуск рассылки
const userIds = await getUserIdsFromDB();
startBroadcast(userIds, "🚀 Новая функция уже доступна!");Dead Letter Queue для неудавшихся сообщений
Dead Letter Queue (DLQ) — обязательный компонент production-системы. DLQ хранит сообщения, которые не удалось доставить после всех попыток. Причины попадания в DLQ:
- Пользователь заблокировал бота (403 Forbidden)
- Чат не найден или удалён (400 Bad Request)
- Превышено максимальное количество retry
- Невалидный формат сообщения
DLQ позволяет анализировать причины недоставок и автоматически обновлять базу подписчиков (удалять заблокировавших, деактивированных и т.д.).
Best practice: настройте задачу для периодического анализа DLQ. Если сообщение попало в DLQ по причине 403 (бот заблокирован) — помечайте пользователя как неактивного в базе. Это сэкономит ресурсы при следующей рассылке.
Мониторинг и алертинг
Без мониторинга массовая рассылка — «чёрный ящик». Вот ключевые показатели:
| Показатель | Описание | Алерт |
|---|---|---|
| Отправлено / всего | Прогресс рассылки | Если прогресс остановился >5 мин |
| Скорость (msg/sec) | Текущая скорость отправки | < 10 msg/sec при ожидаемых 25 |
| Ошибки 429 | Rate limiting от Telegram | > 5% от общего числа запросов |
| Размер очереди | Задачи, ожидающие обработки | Если растёт, а не уменьшается |
| DLQ размер | Недоставленные сообщения | > 10% от объёма рассылки |
| ETA завершения | Ожидаемое время окончания | Превышение запланированного на 50% |
Сегментация и батчинг
Эффективная массовая отправка начинается с правильной сегментации аудитории:
- Сегментируйте по активности: отправляйте сначала активным пользователям (взаимодействовали за последние 7 дней), затем менее активным
- Разбивайте на батчи по 5 000–10 000: это позволяет контролировать прогресс и останавливать при проблемах
- Добавляйте паузы между батчами: 30–60 секунд между сегментами снижают риск 429
- Персонализируйте: используйте имя пользователя, последнее действие — это повышает Open Rate
- A/B тестирование: отправьте два варианта сообщения первым 1000 пользователям, затем лучший — остальным
Тестирование массовой отправки
Перед запуском рассылки на всех пользователей обязательно протестируйте:
- Тест на 10 пользователей: проверка контента и форматирования
- Тест на 100 пользователей: проверка основного потока
- Тест на 1 000 пользователей: проверка rate limiting и retry
- Нагрузочный тест: эмулируйте полный объём с mock-сервером
// Простой mock-сервер для тестирования (Express.js)
import express from "express";
const app = express();
app.use(express.json());
let requestCount = 0;
app.post("/bot:token/sendMessage", (req, res) => {
requestCount++;
// Симуляция 429 при превышении лимита
if (requestCount % 35 === 0) {
return res.status(429).json({
ok: false,
error_code: 429,
description: "Too Many Requests: retry after 3",
parameters: { retry_after: 3 },
});
}
// Симуляция заблокированного бота (5% случаев)
if (Math.random() < 0.05) {
return res.status(403).json({
ok: false,
error_code: 403,
description: "Forbidden: bot was blocked by the user",
});
}
res.json({ ok: true, result: { message_id: requestCount } });
});
app.listen(3001, () => console.log("Mock Telegram API on :3001"));Массовая отправка через Релая
Всё вышеописанное — это серьёзная инженерная работа. Релая предлагает готовое решение, которое включает все компоненты:
| Компонент | DIY-реализация | Через Релая |
|---|---|---|
| Очередь сообщений | Redis + BullMQ (настройка ~1-2 дня) | Встроено |
| Rate limiter | Token Bucket (реализация ~4 часа) | Адаптивный, встроен |
| Retry + DLQ | Backoff + обработка ошибок (~1 день) | Автоматически |
| Мониторинг | Настройка аналитики (~2 дня) | Дашборд из коробки |
| Сегментация | Ручная (SQL + код) | UI для сегментов |
| Мультиканальность | Отдельная интеграция каждого канала | Telegram + MAX + VK |
| Время запуска | 1–2 недели | 30 минут |
Экономика: разработка DIY-системы массовой отправки стоит ~150–300 часов инженера. При стоимости часа 3 000 ₽ — это 450 000–900 000 ₽.Релая обойдётся значительно дешевле и начнёт работать сразу.
MAX как дополнительный канал
При массовой отправке через Telegram вы ограничены 30 msg/sec. Добавление MAX как второго канала даёт несколько преимуществ:
- Параллельная доставка: сообщения уходят через оба канала одновременно, увеличивая охват
- Резервный канал: если Telegram rate limit достигнут, критические уведомления идут через MAX
- Растущая аудитория: MAX активно растёт в России, и ваша аудитория может быть там
- Более предсказуемые лимиты: лимиты MAX API — прозрачны и задокументированы
Через Релая подключение MAX занимает минуты: единый API для всех каналов, единая очередь, единый дашборд. Подробнее о мультиканальном подходе — в статье устойчивая система доставки.
Итог: массовая отправка в Telegram — инженерно сложная задача. Правильная архитектура с очередями, rate limiting и DLQ критически важна. Если вы не хотите тратить недели на инфраструктуру — используйте Релая и сфокусируйтесь на содержании рассылок, а не на борьбе с лимитами.
Создайте бесплатный MAX-профиль
Если хочется не просто читать, а сразу проверить сценарий руками: подключите MAX, отправьте себе тестовое сообщение и уже потом решайте, нужны ли другие каналы.