Получение ботом медиа-файлов и сохранение их на своем сервере

В своей работе над ботом, который проводит розыгрыши, мне потребовалось сохранять у себя на сервере файлы, присылаемые пользователями.  Очень интересный и простой процесс получился.

У Telegram Bot Api для этого есть специальный метод getFile. На входе он получает file_id, на выходе объект File.

Параметров file_id у картинки может быть больше одного, потому как телеграм присылает несколько вариантов размеров картинки, последний всегда оригинал. При загрузке картинки в бот приходит информация в виде объекта Photo, в котором лежит массив объектов PhotoSize

Наш бот будет принимать только команду /start и картинку, на остальные запросы он будет "ругаться".  Чтобы определить, что нам пришло в нашем уже знакомом по предыдущим статьям методе init() будем проверять ключи массива пришедших данных. Для реализации задуманного нам нужен только объект Message

<?php
    // проверяем если пришло сообщение
    if (array_key_exists('message', $data)) {
        //если пришла команда /start
        if ($data['message']['text'] == "/start") {
            $this->sendMessage($chat_id, "Приветствую! Загрузите картинку.");
        } elseif (array_key_exists('photo', $data['message'])) {
            // если пришла картинка то сохраняем ее у себя
            $text = $this->getPhoto($data['message']['photo'])
                ? "Спасибо! Можете еще загрузить мне понравилось их сохранять."
                : "Что-то пошло не так, попробуйте еще раз";
            // отправляем сообщение о результате   
            $this->sendMessage($chat_id, $text);
        } else {
            // если пришло что-то другое
            $this->sendMessage($chat_id, "Не понимаю команду! Просто загрузите картинку.");
        }
    }
?>


Картинки мы будем сохранять в директории img, располагается она рядом с файлом index.php в котором наш код.

.
..
[ img ]
index.php


В случае если прислали картинку, то мы передаем массив объектов PhotoSize в метод getPhoto(), он вернет результат в boolean. Внутри метода происходит 2 действия:

  1. через метод getPhotoPath() получаем расположение файла на сервере Telegram
  2. через метод copyPhoto() копируем картинку к себе на сервер

Картинка располагается на сервере Telegram по стандартному пути:

https://api.telegram.org/file/bot<token>/<file_path>


Выводит картинку по этому пути где-либо в сети не рекомендую, так как будет доступен ваш токен от бота. 

Сохранять себе на сервер мы будем оригинал, поэтому нам надо узнать количество элементов в массиве, использовать будем функцию count()

Чтобы получить расширение файла, будем разбивать file_path в массив по знаку . функцией explode() и брать последний элемент используя функцию end(). Для копирования файлов пользуемся функцией copy().

<?php
    // общая функция загрузки картинки
    private function getPhoto($data)
    {
    	// берем последнюю картинку в массиве
        $file_id = $data[count($data) - 1]['file_id'];
        // получаем file_path
        $file_path = $this->getPhotoPath($file_id);
        // возвращаем результат загрузки фото
        return $this->copyPhoto($file_path);
    }

    // функция получения метонахождения файла
    private function getPhotoPath($file_id) {
    	// получаем объект File
        $array = json_decode($this->requestToTelegram(['file_id' => $file_id], "getFile"), TRUE);
        // возвращаем file_path
        return  $array['result']['file_path'];
    }

    // копируем фото к себе
    private function copyPhoto($file_path) {
    	// ссылка на файл в телеграме
        $file_from_tgrm = "https://api.telegram.org/file/bot".$this->botToken."/".$file_path;
        // достаем расширение файла
        $ext =  end(explode(".", $file_path));
        // назначаем свое имя здесь время_в_секундах.расширение_файла
        $name_our_new_file = time().".".$ext;
        return copy($file_from_tgrm, "img/".$name_our_new_file);
    }
?>

Подводим итог

Сохранить картинку через Телеграм бот очень просто. Методы Bot API позволяют это сделать быстро. Для сохранения других типов файлов с Телеграм алгоритм аналогичный. Скачать исходный код можно по этой ссылке


Комментарии

Авторизуйтесь через Telegram, чтобы оставить комментарий.
Откройте бот @SiteAuthBot, нажмите кнопку Старт/Start. Следуйте инструкциям бота.


  • Хай
    - 20.02.2018 в 16:18
  • А можете подсказать чтобы бот вместе с успешной загрузкой выводил ещё и линк на загруженный файл?
    wo.rez 20.12.2018 в 17:24
    • Скинул решение в бот обратной связи
      iMakeBots 20.12.2018 в 17:39
  • Привет. Я писал код с нуля, не используя ничего. У вас тут есть функция requestToTelegram, не могу понять где ее можно взять, немного не понятно(
    Ильяс 07.04.2020 в 08:19
    • В архиве приложенном к статье есть файл index.php - в нем есть класс Bot в этом классе последний метод
          /** Отправляем запрос в Телеграмм
           * @param $data
           * @param string $type
           * @return mixed
           */
          private function requestToTelegram($data, $type)
          {
              $result = null;
              if (is_array($data)) {
                  $ch = curl_init();
                  curl_setopt($ch, CURLOPT_URL, $this->apiUrl . $this->botToken . '/' . $type);
                  curl_setopt($ch, CURLOPT_POST, count($data));
                  curl_setopt ($ch, CURLOPT_RETURNTRANSFER, 1);
                  curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
                  $result = curl_exec($ch);
                  curl_close($ch);
              }
              return $result;
          }
      iMakeBots 07.04.2020 в 08:27
  • можно готовый билд ?
    Winer 29.05.2020 в 09:22
    • В статье файл, это уже готовый билд
      iMakeBots 29.05.2020 в 11:03
  • Здраствуйте , а на ftp сервер загрузить можно и какой командой, чтоб успел загрузиться файл. Спасибо.
    Александр Alex 03.11.2020 в 18:01
  • А как переслать сразу файл в группу , не сохраняя на сервере, подскажите пожалуйста?
    Шардик 16.11.2020 в 18:03
    • и как отправить видеофайл / GIF? подстановка video вместо photo не решает задачу
      Шардик 16.11.2020 в 20:32
    • А как переслать сразу файл в группу , не сохраняя на сервере, подскажите пожалуйста?
      Вадим 01.08.2021 в 05:43
  • Эххх... Этот код бы перевести в Python... Кому под силу это сделать?
    Sergio64rus 01.02.2021 в 20:57
  • С чем может быть связано, что https://api.telegram.org/file/bot<token>/<file_path> стал отдавать код ошибки 404 спустя неделю работы? Новые файлы телеграм отправляет, выдает id файлов, но их по ссылкам на api.telegram нет
    Aleksandr Bigun 13.02.2021 в 21:50
  • А как переслать сразу файл в группу , не сохраняя на сервере, подскажите пожалуйста?
    Вадим 01.08.2021 в 05:43
  • Привет, Хороший Человек!
    Отличный сайт...

    А как можно сохранять себе не оригинал, а сжатую телеграмом картинку, или задать размеры сохраняемой картинки?
    А еще как сделать так, чтобы имя картинки было равно ID пользователя, который ее грузит...
    Большой Спасиб!
    Boris Spirin 20.01.2022 в 20:53
  • Здравствуйте!
    Я запустил этот код, изменил только токен бота, ид-р админа, внес брекпоинты для отладки.
    Почему в данном коде есть ошибка при отправке боту простого изображения с форматом png?
    А именно ошибка при получении пути файла по его ид-ру файла.

    {"ok":false,"error_code":400,"description":"Bad Request: invalid file_id"}

    Вот код метода инит.

    [code]

    /** Обрабатываем сообщение
    * @param $data
    */
    public function init($data)
    {

    // создаем массив из пришедших данных от API Telegram
    $arrData = $this->getData($data);

    // Определяем id пользователя
    $chat_id = $arrData['message']['chat']['id'];

    // проверяем кто написал: пользователь или админ
    $is_admin = $this->isAdmin($chat_id);

    // /help

    $this->requestToTelegram(array("text" => "Hello, people!"), $chat_id, "sendMessage");
    $this->requestToTelegram(array("text" => json_encode($arrData)), $chat_id, "sendMessage");

    if($this->isHelpBot($arrData)) {

    $help_text = "Это текст справки";
    // Отправляем сообщение
    $this->requestToTelegram(array("text" => $help_text), $chat_id, "sendMessage");
    }

    // если это Старт
    if($this->isStartBot($arrData)) {

    $this->requestToTelegram(array("text" => "Старт бота!"), $chat_id, "sendMessage");

    // Определяем кто написал
    $chat_id = $is_admin ? $this->adminId : $chat_id;

    // Выводим приветственное слово
    $hello = $is_admin ? $this->helloAdmin : $this->setTextHello($this->helloUser, $arrData);

    // Отправляем сообщение
    $this->requestToTelegram(array("text" => $hello), $chat_id, "sendMessage");

    } else {

    $this->requestToTelegram(array("text" => "Пришло сообщение"), $chat_id, "sendMessage");

    $typeMsg = $this->getTypeMsg($arrData);

    // отвечаем что пришло фото
    $this->requestToTelegram(array("text" => $typeMsg), $chat_id, "sendMessage");

    if ($typeMsg=="photo"){

    $this->requestToTelegram(array("text" => "Запуск загрузки фото"), $chat_id, "sendMessage");

    $text = $this->getPhoto($data['message']['photo'], $chat_id)
    ? "Спасибо! Можете еще загрузить мне понравилось их сохранять."
    : "Что-то пошло не так, попробуйте еще раз";

    // отправляем сообщение о результате
    // $this->sendMessage($chat_id, $text);
    $this->requestToTelegram(array("text" => $text), $chat_id, "sendMessage");
    }





    // Если это не старт
    if($is_admin) {
    if($this->isReply($arrData)) {

    // если ответ самому себе
    if($this->isAdmin($arrData['message']['reply_to_message']['from']['id'])) {
    $this->requestToTelegram(array("text" => "Вы ответили сами себе."), $this->adminId, "sendMessage");
    } elseif($this->isBot($arrData)) {

    // если ответ боту
    $this->requestToTelegram(array("text" => "Вы ответили Боту."), $this->adminId, "sendMessage");
    } else {

    // все нормально отправляем на обработку
    $this->getTypeCommand($arrData);
    }
    } else {

    // нажать кнопку ответить
    $this->requestToTelegram(array("text" => $this->answerAdmin), $this->adminId, "sendMessage");
    }
    } else {

    // Если это написал пользователь то перенаправляем админу
    $dataSend = array(
    'from_chat_id' => $chat_id,
    'message_id' => $arrData['message']['message_id'],
    );
    $this->requestToTelegram($dataSend, $this->adminId, "forwardMessage");
    }
    }
    }


    // общая функция загрузки картинки
    private function getPhoto($data, $chat_id)
    {
    $this->requestToTelegram(array("text" => "function getPhoto старт"), $chat_id, "sendMessage");
    // берем последнюю картинку в массиве
    $file_id = $data[count($data) - 1]['file_id'];

    // получаем file_path
    $file_path = $this->getPhotoPath($file_id, $chat_id);
    $this->requestToTelegram(array("text" => "получили file_path"), $chat_id, "sendMessage");

    // возвращаем результат загрузки фото
    return $this->copyPhoto($file_path);
    }

    // функция получения метонахождения файла
    private function getPhotoPath($file_id, $chat_id)
    {

    // получаем объект File
    $array = json_decode($this->requestToTelegram2(['file_id' => $file_id], "getFile"), TRUE);

    $this->requestToTelegram(array("text" => "function getPhotoPath старт"), $chat_id, "sendMessage");

    $this->requestToTelegram(array("text" => json_encode($array)), $chat_id, "sendMessage");

    // возвращаем file_path
    return $array['result']['file_path'];
    }

    // копируем фото к себе
    private function copyPhoto($file_path, $chat_id)
    {

    $this->requestToTelegram(array("text" => "function copyPhoto старт"), $chat_id, "sendMessage");

    // ссылка на файл в телеграме
    $file_from_tgrm = "https://api.telegram.org/file/bot".$this->botToken."/".$file_path;
    // достаем расширение файла
    $ext = end(explode(".", $file_path));
    // назначаем свое имя здесь время_в_секундах.расширение_файла
    $name_our_new_file = time().".".$ext;
    return copy($file_from_tgrm, "img/".$name_our_new_file);
    }

    /** Определяем тип сообщения и передаем для отправки
    * @param $data
    */
    private function getTypeMsg($data)
    {
    // определяем id пользователя для уведомления
    $chat_id = $data['message']['reply_to_message']['forward_from']['id'];
    // если текст
    if (array_key_exists('text', $data['message'])) {

    return("text");

    // готовим данные
    $dataSend = array(
    'text' => $data['message']['text'],
    );

    // отправляем - передаем нужный метод
    $this->requestToTelegram($dataSend, $chat_id, "sendMessage");

    } elseif (array_key_exists('sticker', $data['message'])) {

    return("sticker");
    $dataSend = array(
    'sticker' => $data['message']['sticker']['file_id'],
    );
    $this->requestToTelegram($dataSend, $chat_id, "sendSticker");

    } elseif (array_key_exists('document', $data['message'])) {

    return("document");
    $dataSend = array(
    'document' => $data['message']['document']['file_id'],
    'caption' => $data['message']['caption'],
    );
    $this->requestToTelegram($dataSend, $chat_id, "sendDocument");

    } elseif (array_key_exists('photo', $data['message'])) {

    return("photo");
    // картинки Телеграм ресайзит и предлагает разные размеры, мы берем самый последний вариант
    // так как он самый большой - то есть оригинал
    $img_num = count($data['message']['photo']) - 1;
    $dataSend = array(
    'photo' => $data['message']['photo'][$img_num]['file_id'],
    'caption' => $data['message']['caption'],
    );
    $this->requestToTelegram($dataSend, $chat_id, "sendPhoto");

    } elseif (array_key_exists('video', $data['message'])) {

    return("video");
    $dataSend = array(
    'video' => $data['message']['video']['file_id'],
    'caption' => $data['message']['caption'],
    );
    $this->requestToTelegram($dataSend, $chat_id, "sendVideo");

    } else {
    return("Тип передаваемого сообщения не поддерживается");
    $this->requestToTelegram(array("text" => "Тип передаваемого сообщения не поддерживается"), $chat_id, "sendMessage");
    }
    }



    /** Проверяем не отвечаем ли мы боту
    * @param $data
    * @return bool
    */
    private function isBot($data) {
    return ($data['message']['reply_to_message']['from']['is_bot'] == 1
    Sergey Michaylovich 16.06.2022 в 15:49
    • может быть из-за того, что файл на латинском языке имеет название?
      хотя я загружал и с именем на кириллице и на латинице.
      Sergey Michaylovich 16.06.2022 в 15:50