Что это учебное пособие охватывает, а что нет
Мы создаем небольшой, свободный от зависимостей PHP-хелпер, который вызывает API Telegram Bot SendMessage метод по cURL, тщательно проверяет ответ и избегает HTML, поэтому сообщение, которое вы хотите, является сообщением, которое отображает Telegram. Помощник предназначен для перетаскивания в обработчик веб-перехватчика, задание cron или скрипт обработчика форм.
Эта публикация not настройка веб-перехватчика, проверка подписи или стратегия ограничения скорости. Это отдельные проблемы. Мы фокусируемся на самом HTTP-вызове: коды состояния, декодирование JSON, ok = false; конверт, режимы синтаксического анализа и тайм-ауты.
Обертка
Мы сохраняем функцию маленькой и явной. Имеют значение два аргумента: идентификатор чата и текст. Все остальное - это конфигурация, которую вы можете изменить, не касаясь сайтов звонков.
<?php
declare(strict_types=1);
/**
* Send a message via the Telegram Bot API using cURL.
*
* @param int|string $chatId Telegram chat id.
* @param string $text Message text. Will be escaped for HTML parse_mode.
* @param array $extra Optional overrides: reply_markup, reply_to_message_id, etc.
*
* @return array{ok:bool, status:int, error?:string, result?:array}
*/
function tgSendMessage(int|string $chatId, string $text, array $extra = []): array
{
$token = getenv('TELEGRAM_BOT_TOKEN');
if (!$token) {
return ['ok' => false, 'status' => 0, 'error' => 'TELEGRAM_BOT_TOKEN is not set'];
}
$url = "https://api.telegram.org/bot{$token}/sendMessage&quo t;;
$payload = array_merge([
'chat_id' => $chatId,
'text' => $text,
'parse_mode' => 'HTML',
], $extra);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query($payload),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CONNECTTIMEOUT => 5,
CURLOPT_TIMEOUT => 10,
CURLOPT_HTTPHEADER => ['Content-Type: application/x-www-form-urlencoded'],
]);
$body = curl_exec($ch);
if ($body === false) {
$err = curl_error($ch) ?: 'unknown cURL error';
curl_close($ch);
return ['ok' => false, 'status' => 0, 'error' => "cURL: {$err}"];
}
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
// Body is required: empty response is a failure we want to surface.
if ($body === '' || $body === null) {
return ['ok' => false, 'status' => $status, 'error' => 'empty response body'];
}
$decoded = json_decode($body, true);
if (json_last_error() !== JSON_ERROR_NONE) {
return [
'ok' => false,
'status' => $status,
'error' => 'invalid JSON: ' . json_last_error_msg(),
];
}
if (!is_array($decoded)) {
return ['ok' => false, 'status' => $status, 'error' => 'non-array JSON payload'];
}
// Telegram returns 200 even when ok:false. Always trust the envelope.
if (empty($decoded['ok'])) {
return [
'ok' => false,
'status' => $status,
'error' => (string)($decoded['description'] ?? 'Telegram returned ok:false'),
];
}
return ['ok' => true, 'status' => $status, 'result' => $decoded['result'] ?? []];
}
Три вещи, на которые стоит обратить внимание:
1. Мы используем getenv('TELEGRAM_BOT_TOKEN'). Маркер считывается из окружающей среды, а не из источника. В производстве, источник его из Env. файл (анализируется вашим фреймворком) или из секретного хранилища платформы. 2. Мы проверяем ручку cURL с помощью cURL ошибка только после curl_exec вернулся ложныйa. ложный Возвращение из curl_exec является единственным сигналом о том, что сам запрос не удался на транспортном уровне. 3. Telegram возвращает http 200 с конвертом JSON {"ok":false,"error_code":...,"description":"..."} для сбоев на уровне приложения (чат не найден, сообщение слишком длинное, ошибка синтаксического анализа). Мы обрабатываем ok = false; как сбой независимо от HTTP-статуса.
Экранирование HTML перед отправкой
Если вы установили parse_mode: HTML, Telegram анализирует небольшое подмножество HTML: <b>, <i>, <u>, <s>, <code>, <pre>, <a href="...">. Все, что выглядит как HTML, но не является допустимым в этом подмножестве, вызывает 400 Bad Request: невозможно проанализировать сущности. Пользовательский текст, в частности, нуждается в экранировании.
function tgHtml(string $s): string
{
// Order matters: escape & first, then the rest.
return htmlspecialchars($s, ENT_QUOTES | ENT_HTML5, 'UTF-8');
}
// In a form-to-Telegram pipeline:
$name = tgHtml($_POST['name'] ?? '');
$message = tgHtml($_POST['message'] ?? '');
$text = "<b>New lead</b>\nName: {$name}\nMessage: {$message}";
$res = tgSendMessage($chatId, $text);
Если вы избежите пользовательского ввода перед составлением текста и разрешите только теги, которые вам действительно нужны, синтаксический анализатор не увидит блуждающих < и > и ваше сообщение не сломается. Если вам нужно сохранить форматирование, составьте его из безопасных частей, а не объединяйте исходные данные.
Обработка сбоев, которые вы можете повторить
SendMessage является идемпотентным, только если вы дадите ему то же самое Идентификатор редактировать — отправка того же SendMessage call twice создает два сообщения. Не повторяйте попытку вслепую. Обертка возвращает структурированные данные, чтобы вызывающий абонент мог принять решение.
$res = tgSendMessage($chatId, $text);
if (!$res['ok']) {
// Log only what is safe to log: do not echo $body verbatim.
error_log(sprintf(
'[tg] status=%d error=%s',
$res['status'],
$res['error'] ?? 'unknown'
));
// Application errors: 400 chat not found, 429 too many requests.
// 5xx and transport errors: safe to retry with backoff.
if ($res['status'] >= 500 || $res['status'] === 0) {
// schedule a retry in your queue
} else {
// give up, notify an admin channel
}
return;
}
// $res['result']['message_id'] is available for follow-up edits.
$messageId = $res['result']['message_id'];
- статус поле 0 для транспортных сбоев (DNS, TLS, тайм-аут, отказ в подключении). 0 это не настоящий HTTP-код, но это полезный бакет для повторных попыток «никогда не достигал Telegram».
Тайм-ауты и колпачки соединений
Важны два тайм-аута:
- CURLOPT_CONNECTTIMEOUT — как долго мы ждем установления TCP/TLS-соединения. - CURLOPT_TIMEOUT — общий бюджет запроса, включая DNS, TLS, send, wait, receive.
Для обработчика веб-перехватчика держите оба маленькими. Telegram быстрый; пятисекундное соединение и десятисекундный итог - это щедро. Длинные тайм-ауты внутри веб-запроса приводят к зависанию рабочих и медленным страницам. Если вам нужны гарантии доставки, поместите сообщение в очередь (Redis, база данных, SQS) и позвоните рабочему tgSendMessage из процесса CLI, где вы можете позволить себе более длинные бюджеты.
Если вы отправляете пакет, установите вменяемый CURLOPT_MAXCONNECTS и повторно используйте ручки с curl_multi_*. Приведенная выше обертка является одноразовой, потому что это форма, которая вписывается в большинство кодовых путей.
Производственные заметки (необязательно, рассматривать как таковые)
- выключить parse_mode когда вам это не нужно. Обычный текст полностью обходит синтаксический анализатор сущностей и удаляет целый класс ошибок. - Храните сообщения длиной не более 4096 символов. Это SendMessage лимит. Telegram вернется Сообщение слишком длинное если вы превысите его. Для длинного контента используйте sendDocument с сгенерированным .txt или .html . Использование disable_web_page_preview: true при отправке URL-адресов, которые вы не контролируете, чтобы избежать случайного предварительного просмотра вредоносных ссылок в чатах оператора. - Ошибки чтения из $decoded['description'] вместо HTTP-статуса. Telegram's error_code зеркалирует HTTP, но описание является человекоподобным ("Bad Request: message is too long", "Forbidden: bot was blocked by the user"). - Наблюдайте для… 429. Telegram rate-limits per chat and per bot. Назад и повторите попытку после Повторное соединение после: (секунды) API включает в конверт ошибки.
Быстрый контрольный список
- Токен считывается из окружающей среды. - curl_exec ложный рассматривается как сбой при транспортировке со строкой ошибки cURL. - json_last_error() проверяется после Декодирование JSON. - Telegram's ok = false; конверт рассматривается как сбой даже на HTTP 200. - HTML пользовательский ввод передается через htmlspecialchars перед составлением. - Тайм-ауты сокращены для звонков по запросу, дольше для фоновых работников.
Такого маленького помощника часто бывает достаточно. Как только у вас есть один хорошо функционирующий сайт звонков, второй и третий сайты звонков становятся тривиальными, и вы перестаете терять сообщения ok = false; откликов\реакций.
Если вы хотите получить справку для полного набора SendMessage и ответных форм разметки, Telegram Bot API reference является каноническим местом для поиска.
Этот шаблон — небольшая обертка cURL, строгие проверки конвертов, побег HTML, короткие тайм-ауты — тот же, который BotCreator использует при доставке ботов Telegram и мини-приложений для клиентов.