Сообщения
Фото, документы, media group
sendPhoto, sendDocument, sendMediaGroup: передача файла по file_id, URL и multipart, загрузка от пользователя через getFile, альбомы из 2–10 объектов.
Текстовые sendMessage — это треть всех ботов. Остальные две трети — картинки, PDF, голосовые и альбомы. Для них Bot API отдаёт семейство методов sendPhoto, sendDocument, sendVideo, sendAudio, sendVoice, sendAnimation, sendVideoNote. Сигнатура у них одинаковая: chat_id, что-то медиа-специфичное (photo, document, video и т.д.), опциональные caption и parse_mode.
Способы передачи файла
Поле медиа принимает три формы:
- file_id строкой — файл уже загружен в Telegram раньше (любым сообщением, в т.ч. от пользователя). Бесплатно и моментально.
- HTTP URL — Telegram сам скачает и зальёт к себе. Удобно для генерации картинок на лету (QR, графики), но не больше 5 МБ для фото и 20 МБ для остального.
- multipart/form-data — загрузка через бота. До 10 МБ на фото, 50 МБ на документ (через локальный Bot API — до 2 ГБ).
Правило: если планируете пересылать один и тот же файл много раз — сначала отправьте его один раз, заберите file_id из ответа и дальше используйте строку.
Отправка фото по URL
$api = 'https://api.telegram.org/bot' . BOT_TOKEN;
function sendPhotoByUrl(string $api, int $chatId, string $url, string $caption = ''): array {
$payload = [
'chat_id' => $chatId,
'photo' => $url,
'caption' => $caption,
'parse_mode' => 'HTML',
];
$ch = curl_init($api . '/sendPhoto');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
]);
$body = curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($body === false || $code !== 200) {
throw new RuntimeException("sendPhoto failed: HTTP $code");
}
$resp = json_decode($body, true);
if (!($resp['ok'] ?? false)) {
throw new RuntimeException('Telegram error: ' . ($resp['description'] ?? 'unknown'));
}
return $resp['result'];
}Возвращается объект Message, в нём message.photo — массив объектов PhotoSize разных размеров. Самый большой — последний, его file_id берите для повторной отправки.
Загрузка файла с диска
Когда нужна multipart-загрузка (документы, большие файлы, локальные картинки):
function sendDocument(string $api, int $chatId, string $path, string $filename = ''): array {
if (!is_file($path)) {
throw new InvalidArgumentException("File not found: $path");
}
if ($filename === '') {
$filename = basename($path);
}
$payload = [
'chat_id' => $chatId,
'document' => new CURLFile($path, mime_content_type($path) ?: 'application/octet-stream', $filename),
];
$ch = curl_init($api . '/sendDocument');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 60,
]);
$body = curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
$resp = json_decode($body, true);
if ($code !== 200 || !($resp['ok'] ?? false)) {
throw new RuntimeException('sendDocument error: ' . ($resp['description'] ?? "HTTP $code"));
}
return $resp['result'];
}Ключевое: CURLFile для PHP 5.5+. В caption можно дать подпись до 1024 символов с тем же parse_mode HTML/Markdown, что и в HTML и Markdown.
Media group: альбомы
Альбом из 2–10 объектов одного типа (фото или видео) отправляется через sendMediaGroup. Особенности:
- Все элементы — в массиве
media, каждый как JSON-строка с полямиtype,media, опциональноcaption,parse_mode. - Подпись (
caption) можно задать только первому элементу, остальные её игнорируют. - В
updateприходит одно сообщение с полемmedia_group_id; у каждогоMessageв массиве — свойfile_id.
function sendAlbum(string $api, int $chatId, array $urls): array {
if (count($urls) < 2 || count($urls) > 10) {
throw new InvalidArgumentException('Album must contain 2..10 items');
}
$media = array_map(fn(string $u) => json_encode([
'type' => 'photo',
'media' => $u,
]), $urls);
$payload = [
'chat_id' => $chatId,
'media' => $media, // array of JSON strings
];
$ch = curl_init($api . '/sendMediaGroup');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
]);
$body = curl_exec($ch);
$resp = json_decode($body, true);
curl_close($ch);
if (!($resp['ok'] ?? false)) {
throw new RuntimeException('sendMediaGroup: ' . ($resp['description'] ?? 'fail'));
}
return $resp['result']; // array of Message
}Смешивать фото и видео в одном альбоме нельзя — только один тип. Если нужен микс — отправляйте двумя группами подряд.
Получение файла от пользователя
Когда приходит update с message.photo или message.document:
- Берёте
file_idнужного размера (для фото — обычно последний, самый большой). - Вызываете
getFileс этимfile_id, получаетеfile_path— относительный путь на серверах Telegram. - Скачиваете по
https://api.telegram.org/file/bot<TOKEN>/<file_path>. Ссылка живёт минимум час.
Подводные камни
- URL-картинка должна отдавать корректный
Content-Typeи быть доступна боту без редиректов на логин. - Один и тот же URL Telegram перезальёт к себе и сгенерирует новый
file_id— не путайте с переиспользованием. captionподдерживаетparse_mode, но длинные подписи (>1024) обрезаются.- Видео в альбоме требует одинаковых пропорций — иначе Telegram склеит криво.
Медиа-сообщения нельзя отредактировать как текст — только подпись. Для замены картинки придётся удалить сообщение и отправить новое. Об этом — на следующей странице.
Дальше: Редактирование и удаление.