Надсилання повідомлень API бота Telegram за допомогою PHP cURL: перевірка статусу, перевірка HTML та тайм-аути

Що охоплює цей посібник, а що ні

Ми створюємо невеликий, вільний від залежностей PHP-помічник, який викликає API бота Telegram SendMessage метод cURL, ретельно перевіряє відповідь і уникає HTML, тому повідомлення, яке ви хочете, - це повідомлення, яке відображає Telegram. Помічник призначений для перетягування в обробник веб-перехоплювача, завдання cron або скрипт обробника форм.

Цей допис не налаштування вебхука, перевірка підпису або стратегія обмеження швидкості. Це окремі проблеми. Ми зосереджуємося на самому виклику 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 Ты вернулся. falsea false Повернення з 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 Неправильний запит: неможливо проаналізувати об 'єкти. Користувацький текст, зокрема, повинен бути захищений.

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 подвійний дзвінок створює два повідомлення. Не намагайтеся повторити спробу наосліп. Обгортка повертає структуровані дані, щоб абонент міг прийняти рішення.

$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, відправку, очікування та отримання.

Для обробника веб-перехоплювача залишайте обидва маленькими. Telegram швидкий; п 'ятисекундне з' єднання та десятисекундний загальний обсяг є щедрим. Довгі тайм-аути в веб-запиті призводять до зависання працівника та повільних сторінок. Якщо вам потрібні гарантії доставки, розмістіть повідомлення в черзі (Redis, база даних, SQS) і зателефонуйте працівнику tgSendMessage з процесу CLI, де ви можете дозволити собі більш тривалі бюджети.

Якщо ви надсилаєте пакет, встановіть SANE CURLOPT_MAXCONNECTS і повторно використовуйте ручки з curl_multi_*. Вищезазначена обгортка є одноразовою, оскільки вона вписується в більшість шляхів коду.

Виробничі примітки (необов 'язково розглядати як такі)

- shutdown parse_mode коли вам це не потрібно. Звичайний текст повністю обходить синтаксичний аналізатор об 'єктів і видаляє цілий клас помилок. - Зберігайте повідомлення довжиною не більше 4096 символів. Це SendMessage ліміт. Telegram повернеться Повідомлення задовге якщо ви перевищуєте його. Для довгого контенту використовуйте sendDocument зі згенерованим .txt від кулі HTML. . Використання disable_web_page_preview: true під час надсилання URL-адрес, які ви не можете контролювати, щоб уникнути випадкового попереднього перегляду шкідливих посилань у чатах оператора. - Читати помилки з $decoded['description'] замість HTTP-статусу. Телеграма error_code відображає http, але опис є гуманоїдом ("Поганий запит: повідомлення задовге", "Заборонено: бот був заблокований користувачем"). - Спостерігайте за… 429. Telegram rate-limits per chat and per bot. Поверніться назад і повторіть спробу через Перез' єднуватись після: (секунди) API містить помилки в конверті.

Швидкий контрольний список

- Маркер зчитується з навколишнього середовища. - curl_exec false розглядається як несправність транспортування з рядком помилки cURL. - json_last_error() перевіряється після Декодування JSON. - Telegram's ok = false; конверт розглядається як збій навіть на HTTP 200. - Введення HTML-коду користувачем передається через htmlspecialchars перед складанням. - Тайм-аути скорочуються для дзвінків на вимогу, довше для фонових працівників.

Такого маленького помічника часто буває достатньо. Після того, як у вас є один добре функціонуючий сайт дзвінків, другий і третій сайти дзвінків стають тривіальними, і ви перестаєте втрачати повідомлення ok = false; реакції\реакції.

Якщо ви хочете отримати допомогу щодо повного набору SendMessage та взаємних форм розмітки, Telegram Bot API reference - це канонічне місце для пошуку.

Цей шаблон являє собою невелику обгортку cURL, суворі перевірки конвертів, втечу HTML, короткі тайм-аути — той самий, який BotCreator використовує при доставці ботів і гаджетів Telegram клієнтам.

Нові статті — у Telegram

Розбираємо, що автоматизувати в бізнесі та як це працює на практиці. Без спаму.