Юр. сторона и интеграции

Интеграция формы заявки с Telegram-ботом: пошагово

Как принимать заявки с сайта в Telegram-чат через бота: создание токена, привязка PHP, обработка ошибок и анти-спам. Готовые сниппеты.

обновлено 9 минут чтения 2 Дмитрий Акиндинов
Содержание
  1. Что мы сделаем
  2. Шаг 1: создание бота
  3. Шаг 2: получение chat_id
  4. Шаг 3: проверка отправки
  5. Шаг 4: PHP-обработчик формы
  6. Шаг 5: HTML-форма
  7. Шаг 6: JavaScript для отправки
  8. Шаг 7: продвинутая защита от спама
  9. Шаг 8: дублирование на email
  10. Шаг 9: интеграция с CRM
  11. Чего не делать
  12. Чек-лист рабочей интеграции
  13. Когда стоит делегировать
  14. Частые вопросы

Форма с уведомлением в Telegram собирается за 1-2 часа: создать бота, узнать chat_id, отправить тестовое сообщение, добавить PHP-обработчик. Заявка долетает за 2-3 секунды после нажатия кнопки. Скрытое поле-ловушка и проверка времени заполнения отбивают около 80 процентов ботов.

Telegram-бот для приёма заявок это самый простой и надёжный способ получать уведомления о новых лидах в режиме реального времени. Не нужно проверять почту, не нужно ждать письмо, никто не пропустит важную заявку. Заявка пришла в Telegram-канал или в личку через 2-3 секунды после нажатия Отправить.

Разберу, как сделать с нуля: от создания бота через BotFather до полностью работающей интеграции с PHP-формой на сайте, плюс защита от спама и обработка ошибок.

Иконка самолёта рядом с формой на сайте

Что мы сделаем

Полная схема:

  1. Пользователь заполняет форму на сайте.
  2. JS отправляет данные на ваш PHP-обработчик (например, mail.php).
  3. PHP-обработчик валидирует, проверяет анти-спам, сохраняет данные.
  4. PHP-обработчик через Telegram Bot API отправляет сообщение в указанный чат.
  5. Параллельно может отправлять email и/или сохранять в CSV.
  6. Возвращает успех или ошибку обратно в JS.
  7. JS показывает пользователю подтверждение или сообщение об ошибке.

Время реализации с нуля: 1-2 часа на простую форму, 4-6 часов на форму с валидацией и анти-спамом.

Шаг 1: создание бота

В Telegram пишете боту @BotFather. Команды:

  • /newbot создать нового бота.
  • BotFather просит имя (отображаемое, например Saitfix Bot) и username (@saitfix_bot, должен заканчиваться на _bot).
  • После регистрации он отдаёт API-токен в формате 0123456789:AAAA....

Сохраните этот токен в безопасном месте, не показывайте никому. С ним любой может писать от имени вашего бота.

Шаг 2: получение chat_id

Telegram API не знает, кому слать сообщение, без chat_id получателя. Получаем.

Вариант 1: личные сообщения

Открываете чат с вашим ботом, пишете /start. Бот пока ничего не отвечает (мы его ещё не запрограммировали), но Telegram уже знает, что вы с ним переписывались.

Открываете в браузере:

https://api.telegram.org/bot<ВАШ_ТОКЕН>/getUpdates

Видите JSON с историей. Ищете блок from > id. Это ваш chat_id (положительное число).

Вариант 2: групповой чат

Создаёте группу в Telegram, добавляете туда бота. Пишете в группе любое сообщение. Снова getUpdates. В JSON ищете chat > id для группы. Это отрицательное число с минусом, например -1001234567890.

Используете chat_id для отправки. В личке только лично, в группе все участники видят.

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

Создаёте Telegram-канал, добавляете бота как администратора с правом постить. Постите туда что-нибудь, через getUpdates находите chat > id. Используется тот же.

Я обычно делаю групповой чат для приёма заявок: туда добавлены все, кто реагирует на лиды.

Шаг 3: проверка отправки

Простая команда через curl или браузер:

https://api.telegram.org/bot<ТОКЕН>/sendMessage?chat_id=<CHAT_ID>&text=Привет%20мир

Если бот пришёл в указанный чат с текстом Привет мир, всё работает. Запоминаем формат API.

Шаг 4: PHP-обработчик формы

Базовый обработчик mail.php:

<?php
header('Content-Type: application/json; charset=utf-8');

// 1. Принимаем только POST
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    http_response_code(405);
    echo json_encode(['ok' => false, 'error' => 'Method not allowed']);
    exit;
}

// 2. Конфиг (в production выносим в отдельный файл вне webroot)
$BOT_TOKEN = '0123456789:AAAA...';
$CHAT_ID = '-1001234567890';
$RECAPTCHA_SECRET = ''; // если используете

// 3. Валидация полей
$name = trim($_POST['name'] ?? '');
$phone = trim($_POST['phone'] ?? '');
$email = trim($_POST['email'] ?? '');
$message = trim($_POST['message'] ?? '');
$consent = $_POST['consent'] ?? '';

if (!$name || !$phone || $consent !== 'on') {
    http_response_code(400);
    echo json_encode(['ok' => false, 'error' => 'Заполните обязательные поля']);
    exit;
}

if (!preg_match('/^[+d-()s]{7,20}$/', $phone)) {
    http_response_code(400);
    echo json_encode(['ok' => false, 'error' => 'Некорректный телефон']);
    exit;
}

// 4. Анти-спам: honeypot
if (!empty($_POST['website'])) {
    // honeypot заполнен ботом
    http_response_code(200);
    echo json_encode(['ok' => true]);
    exit;
}

// 5. Анти-спам: проверка времени
$formStart = (int)($_POST['ts'] ?? 0);
if ($formStart > 0 && (time() - $formStart) < 3) {
    http_response_code(400);
    echo json_encode(['ok' => false, 'error' => 'Слишком быстрая отправка']);
    exit;
}

// 6. Формируем сообщение для Telegram
$text = "🆕 Новая заявка с сайтаnn";
$text .= "👤 Имя: " . htmlspecialchars($name) . "n";
$text .= "📱 Телефон: " . htmlspecialchars($phone) . "n";
if ($email) {
    $text .= "✉️ Email: " . htmlspecialchars($email) . "n";
}
if ($message) {
    $text .= "💬 Сообщение: " . htmlspecialchars($message) . "n";
}
$text .= "n🌐 Источник: " . ($_SERVER['HTTP_REFERER'] ?? 'unknown');
$text .= "n📅 " . date('d.m.Y H:i');

// 7. Отправка в Telegram
$telegramUrl = "https://api.telegram.org/bot{$BOT_TOKEN}/sendMessage";
$payload = [
    'chat_id' => $CHAT_ID,
    'text' => $text,
    'parse_mode' => 'HTML',
];

$ch = curl_init($telegramUrl);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($payload));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

if ($httpCode !== 200) {
    // Логируем ошибку (но не показываем пользователю)
    error_log("Telegram API failed: $response");
    http_response_code(500);
    echo json_encode(['ok' => false, 'error' => 'Не удалось отправить заявку. Попробуйте позже.']);
    exit;
}

// 8. Параллельно сохраняем в CSV для бэкапа
$logEntry = [
    date('c'),
    $name,
    $phone,
    $email,
    str_replace(["n", "r"], ' ', $message),
    $_SERVER['REMOTE_ADDR'],
];
$logFile = __DIR__ . '/../private/leads.csv';
$fp = fopen($logFile, 'a');
fputcsv($fp, $logEntry);
fclose($fp);

// 9. Возвращаем успех
echo json_encode(['ok' => true, 'message' => 'Заявка отправлена']);

Этот код: — Принимает POST с формы. — Валидирует поля. — Проверяет honeypot (скрытое поле, которое заполняют только боты). — Проверяет время заполнения (если меньше 3 секунд, скорее всего бот). — Формирует красивое сообщение с эмодзи. — Отправляет в Telegram. — Сохраняет в CSV для бэкапа. — Возвращает JSON о результате.

Шаг 5: HTML-форма

Минимальная форма:

<form id="contact-form">
  <input type="hidden" name="ts" value="">
  <!-- Honeypot, скрыт от пользователей -->
  <input type="text" name="website" 
         style="position:absolute;left:-9999px" 
         tabindex="-1" autocomplete="off">
  
  <label>
    Имя <input type="text" name="name" required>
  </label>
  
  <label>
    Телефон <input type="tel" name="phone" required pattern="[+d-()s]{7,20}">
  </label>
  
  <label>
    Email <input type="email" name="email">
  </label>
  
  <label>
    Сообщение <textarea name="message" rows="3"></textarea>
  </label>
  
  <label>
    <input type="checkbox" name="consent" required>
    Я согласен на обработку персональных данных. 
    <a href="/privacy/" target="_blank">Политика конфиденциальности</a>
  </label>
  
  <button type="submit">Отправить заявку</button>
  <div id="form-status"></div>
</form>

Несколько важных моментов:

  • name="website" это honeypot. Поле невидимо для пользователя, но боты заполняют все поля.
  • pattern для телефона добавляет браузерную валидацию.
  • consent обязателен по 152-ФЗ.

Шаг 6: JavaScript для отправки

const form = document.getElementById('contact-form');
const status = document.getElementById('form-status');

// При фокусе на форму запоминаем время
form.addEventListener('focusin', () => {
  if (!form.querySelector('[name="ts"]').value) {
    form.querySelector('[name="ts"]').value = Math.floor(Date.now() / 1000);
  }
}, { once: true });

form.addEventListener('submit', async (e) => {
  e.preventDefault();
  status.textContent = 'Отправка...';
  
  const formData = new FormData(form);
  
  try {
    const response = await fetch('/mail.php', {
      method: 'POST',
      body: formData,
    });
    const data = await response.json();
    
    if (data.ok) {
      status.textContent = 'Заявка отправлена. Свяжемся в течение часа.';
      form.reset();
    } else {
      status.textContent = data.error || 'Ошибка отправки.';
    }
  } catch (err) {
    status.textContent = 'Ошибка сети. Попробуйте позже.';
  }
});

Простой fetch, обработка ответа. Никаких внешних библиотек.

Чат-баббл с иконкой бота и стрелкой формы

Шаг 7: продвинутая защита от спама

Honeypot и проверка времени отбивают 80 процентов ботов. Для оставшихся 20 несколько вариантов.

Вариант 1: yandex-CAPTCHA

Российский аналог reCAPTCHA. Бесплатно, работает в РФ. Подключается похоже на Google.

Регистрируетесь в cloud.yandex.ru/services/smartcaptcha, получаете client key и server key. На странице вставляете виджет, в обработчике сверяете токен через API.

Вариант 2: rate limiting

Один IP не может отправить больше 5 заявок в час. Реализуется через файл с временем последней отправки или через Redis.

$ipFile = __DIR__ . '/../private/rate-limit/' . md5($_SERVER['REMOTE_ADDR']);
if (file_exists($ipFile)) {
    $count = (int)file_get_contents($ipFile);
    if ($count > 5) {
        http_response_code(429);
        echo json_encode(['ok' => false, 'error' => 'Слишком много заявок']);
        exit;
    }
}
file_put_contents($ipFile, ((int)@file_get_contents($ipFile) + 1));
// Отдельным cron'ом сбрасываем папку каждый час

Вариант 3: проверка ключевых слов

Простой блек-лист: casino, viagra, crypto, forex. Если в имени или сообщении встречается, отбрасываем.

$blacklist = ['casino', 'viagra', 'crypto', 'forex', 'bitcoin'];
foreach ($blacklist as $word) {
    if (stripos($name . ' ' . $message, $word) !== false) {
        // молча отбрасываем как успешное
        echo json_encode(['ok' => true]);
        exit;
    }
}

Вариант 4: проверка JS

Если форма отправляется через fetch, и заголовок X-Requested-With: XMLHttpRequest отсутствует, скорее всего бот отправляет вне браузера.

В JS добавляете заголовок:

fetch('/mail.php', {
  method: 'POST',
  body: formData,
  headers: { 'X-Requested-With': 'XMLHttpRequest' }
});

В PHP проверяете:

if (!isset($_SERVER['HTTP_X_REQUESTED_WITH']) || $_SERVER['HTTP_X_REQUESTED_WITH'] !== 'XMLHttpRequest') {
    http_response_code(403);
    exit;
}

Эти четыре уровня в комбинации отбивают почти всех ботов.

Шаг 8: дублирование на email

Telegram удобный, но нужен и email-бэкап. Если Telegram упал, email сохранит заявку.

Добавляем в обработчик после Telegram:

$to = 'info@saitfix.ru';
$subject = 'Заявка с сайта от ' . $name;
$body = "Имя: $namenТелефон: $phonenEmail: $emailnСообщение: $message";
$headers = "From: noreply@saitfix.rurnReply-To: $emailrnContent-Type: text/plain; charset=utf-8";

mail($to, $subject, $body, $headers);

mail() это базовая функция PHP. На большинстве хостингов работает без настройки. Если у вас настроен корпоративный SMTP (Yandex 360 или другой), лучше отправлять через PHPMailer с авторизацией для лучшей доставляемости.

Шаг 9: интеграция с CRM

Часто хочется не только Telegram и email, но и CRM. Добавляем третий вызов после двух предыдущих.

amoCRM

$amoUrl = 'https://your-domain.amocrm.ru/api/v4/leads';
$amoToken = 'BEARER_TOKEN_HERE';

$leadData = [
    'name' => "Заявка от $name",
    'pipeline_id' => 12345,
    '_embedded' => [
        'contacts' => [[
            'name' => $name,
            'custom_fields_values' => [
                ['field_id' => 678, 'values' => [['value' => $phone]]]
            ]
        ]]
    ]
];

$ch = curl_init($amoUrl);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([$leadData]));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer ' . $amoToken,
    'Content-Type: application/json'
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_exec($ch);
curl_close($ch);

Bitrix24

Через webhook:

$bitrixWebhook = 'https://your.bitrix24.ru/rest/1/SECRET_TOKEN/crm.lead.add';
$leadData = [
    'fields' => [
        'TITLE' => "Заявка от $name",
        'NAME' => $name,
        'PHONE' => [['VALUE' => $phone, 'VALUE_TYPE' => 'WORK']]
    ]
];

$ch = curl_init($bitrixWebhook);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($leadData));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_exec($ch);
curl_close($ch);

Webhook создаётся в Bitrix24 в Разработчикам > Другое > Входящий вебхук.

Чего не делать

1. Не хранить токен в коде на GitHub

Токен бота это полный контроль. Кто его получит, шлёт сообщения от вашего имени. Храните в .env или в private/config.php вне webroot и git.

2. Не доверять данным из формы

Любая входящая строка от пользователя должна валидироваться. Не вставляйте напрямую в URL, в SQL, в HTML без экранирования.

3. Не делать асинхронную отправку без проверки

Если PHP-обработчик не дождался ответа Telegram (например, упал в timeout), пользователь видит успех, а заявка не пришла. Лучше: отправить, дождаться ответа, и только потом ответить пользователю.

4. Не ставить очень короткий timeout

Если Telegram API задерживается, ваш CURLOPT_TIMEOUT = 3 может оборвать связь. Ставьте 10-15 секунд минимум.

5. Не игнорировать ошибки

Если Telegram вернул ошибку, нужно записать в лог, чтобы потом разобраться. Не просто echo error, а error_log() для администратора.

Сервер шлёт сообщение на телефон

Чек-лист рабочей интеграции

  • Бот создан через BotFather, токен сохранён.
  • Получен chat_id целевого чата или канала.
  • Тестовое сообщение через API доходит.
  • PHP-обработчик валидирует поля.
  • Honeypot и проверка времени установлены.
  • Заявка отправляется в Telegram.
  • Бэкап в CSV или email включён.
  • При ошибке Telegram пользователь видит понятное сообщение.
  • Токен и chat_id хранятся вне webroot и git.
  • Ошибки логируются.
  • Анти-спам тестировался реальной нагрузкой.

Когда стоит делегировать

Простая интеграция формы с Telegram это работа на 2-4 часа. С CRM и расширенным анти-спамом до 6-10 часов. Если форма с многими полями и сложной логикой, до 16 часов.

Эта работа входит в часовую ставку или в любой из тарифов на /#pricing. Если вам нужна форма с интеграцией Telegram плюс CRM, опишите ситуацию через /#contact. Связанные материалы про интеграции и формы в /blog/category/legal-and-integrations/.

Частые вопросы

Как настроить отправку заявок в Telegram

Создать бота через служебного бота платформы, получить токен, узнать chat_id нужного чата или канала, добавить в обработчик формы запрос к API с текстом заявки. Простая форма собирается за час-два.

Как узнать chat_id

Написать боту любое сообщение и запросить у API список обновлений: chat_id будет в ответе. Для канала бота сначала добавляют администратором, иначе он не сможет туда писать.

Как защитить форму от спама

Скрытым полем, которое человек не видит, и проверкой времени заполнения: отправка быстрее нескольких секунд почти наверняка бот. Связка убирает около 80 процентов мусора без капчи.

Стоит ли дублировать заявки на почту

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

Передайте техническую часть в надёжные руки.

Слежу за хостингом, бэкапами и обновлениями. Когда что-то пойдёт не так – звоните мне, не своему программисту, не в спортзал и не маме.

Короткие вопросы по теме

Все вопросы по теме "Юр. сторона и интеграции"

Заявка

Обсудим задачу

Напишите имя и телефон, выберите удобный канал. Перезвоню или напишу в течение часа в рабочее время.

Куда удобнее ответить?