# Создание Telegram бота на PHP #5: работа с хуками

В новом уроке мы с вами поговорим о настройке хуков и напишем свой первый обработчик команд.

В первом уроке я вам рассказывал что такое хуки, давайте повторим:

**Hooks (Хуки)** — это способ общения с программой, по средствам отправки данных от **сервера** — **клиенту**. То есть при определённых изменениях в программе, сервер (приложение) будет отправлять данные на указанный URL скрипта клиента.

Например. Каждый раз когда пользователи будут писать сообщения боту, данные о сообщениях будут отправляться на указанный скрипт, где вы сможете записать сообщения в БД или отправить ответ.

Полный список всех записей курса находится на сайте <https://prog-time.ru/course_cat/telegram-bot-basic/> или в публикациях на Хабр <https://habr.com/ru/users/Prog-Time/posts/>

Для регистрации хука нужно выполнить 2 правила:

* разместить скрипт на виртуальный сервер (хостинг)
* домен на который будут отправляться запросы, должен иметь SSL сертификат и работать через HTTPS соединение

Если ваш хостинг соответствует данным требованиям, то давайте займёмся регистрацией хука для Telegram бота.

#### Регистрация хука для Telegram бота

Для регистрации хука нам нужно отправить запрос с методом **setWebhook()**, которому в качестве параметра **url** мы должны передать ссылку на скрипт обработчик. В моём случае это просто php скрипт.

Вот пример запроса:

```
$token = "5340791844:AAEXXDduvInvQrlykV91USOQSevrPVU";

$getQuery = array(
     "url" => "https://prog-time.ru/tg_script/index.php",
);
$ch = curl_init("https://api.telegram.org/bot". $token ."/setWebhook?" . http_build_query($getQuery));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HEADER, false);

$resultQuery = curl_exec($ch);
curl_close($ch);

echo $resultQuery;
```

Если запрос прошёл успешно, то вы получите следующий ответ:

```
{
  "ok": true,
  "result": true,
  "description": "Webhook was set"
}
```

Теперь давайте проверим работу нашего обработчика. Сообщения приходят POST-запросом, с типом `application/json`. Получить его в PHP можно следующим образом:

```
$data = file_get_contents('php://input');
$data = json_decode($data, true);
```

#### Разбор параметров передаваемых через Hooks

Давайте теперь посмотрим что приходит на наш скрипт при отправке простого текстового сообщения боту.

Здесь есть небольшая проблема! Скрипты будут выполняться в рандомный момент и если мы не запишем данные, то они пропадут в пустоту. Для записи ответа вы можете использовать БД или как я, просто записать массив в файл **txt**.

Для записи строки я буду использовать дополнительную, самописную функцию **writeLogFile()**

Моя функция принимает 2 параметра:

* первый параметр, строка для записи. В нашем случае это JSON строка.
* второй параметр используется для очистки файла и перезаписи. Если данный параметр имеет значение **false**, то в файл дописывается информация.

```
function writeLogFile($string, $clear = false){
    $log_file_name = __DIR__."/message.txt";
    if($clear == false) {
	$now = date("Y-m-d H:i:s");
	file_put_contents($log_file_name, $now." ".print_r($string, true)."\r\n", FILE_APPEND);
    }
    else {
	file_put_contents($log_file_name, '');
        file_put_contents($log_file_name, $now." ".print_r($string, true)."\r\n", FILE_APPEND);
    }
}
```

Полный код для записи информации в файл будет выглядеть следующим образом.

```
function writeLogFile($string, $clear = false){
    $log_file_name = __DIR__."/message.txt";
    if($clear == false) {
		$now = date("Y-m-d H:i:s");
		file_put_contents($log_file_name, $now." ".print_r($string, true)."\r\n", FILE_APPEND);
    }
    else {
		file_put_contents($log_file_name, '');
        file_put_contents($log_file_name, $now." ".print_r($string, true)."\r\n", FILE_APPEND);
    }
}

$data = file_get_contents('php://input');
writeLogFile($data, true);
```

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

<figure><img src="https://habrastorage.org/r/w1560/getpro/habr/upload_files/c7c/8e5/413/c7c8e541331f4ba1948600abd92eef49.png" alt="" height="46" width="503"><figcaption></figcaption></figure>

Теперь выведем полученную информацию на страницу

```
echo file_get_contents(__DIR__."/message.txt");
```

Вот что мы получаем. Это объект в котором записана информация о созданном сообщение, мы видим данные о пользователе, данные о чате, дата отправления и текст сообщения.

```
{
  "update_id": 803290892,
  "message": {
    "message_id": 41,
    "from": {
      "id": 1424646511,
      "is_bot": false,
      "first_name": "Илья",
      "last_name": "Лящук",
      "username": "iliyalyachuk",
      "language_code": "ru"
    },
    "chat": {
      "id": 1424646511,
      "first_name": "Илья",
      "last_name": "Лящук",
      "username": "iliyalyachuk",
      "type": "private"
    },
    "date": 1659098034,
    "text": "Новое тестовое сообщение"
  }
}
```

#### Данные при нажатие на кнопку в чате

Если пользователь нажал на кнопку, то на скрипт также будет отправлен запрос с данными о пользователе и о кнопке.

Отличительной особенностью таких запросов является то что главный ключ **message** заменяется на **callback\_query**, а сам массив message будет находиться внутри.

Получить код кнопки на которую было произведено нажатие, можно из **callback\_query** -> **data**.

```
{
  "update_id": 803290921,
  "callback_query": {
    "id": "6118810175780540321",
    "from": {
      "id": 1424646511,
      "is_bot": false,
      "first_name": "Илья",
      "last_name": "Лящук",
      "username": "iliyalyachuk",
      "language_code": "ru"
    },
    "message": {
      "message_id": 113,
      "from": {
        "id": 5340791844,
        "is_bot": true,
        "first_name": "test_prog_time",
        "username": "test_prog_time_bot"
      },
      "chat": {
        "id": 1424646511,
        "first_name": "Илья",
        "last_name": "Лящук",
        "username": "iliyalyachuk",
        "type": "private"
      },
      "date": 1659335238,
      "text": "Тестовое сообщение",
      "reply_markup": {
        "inline_keyboard": [
          [
            {
              "text": "YOUR BUTTON LABEL TEXT",
              "callback_data": "test_123"
            }
          ]
        ]
      }
    },
    "chat_instance": "4661722712167232747",
    "data": "test_123"
  }
}
```

#### Данные при отправке изображения

Теперь давайте посмотри данные которые приходят при отправке изображения в чат, от пользователя.

```
{
  "update_id": 803290893,
  "message": {
    "message_id": 42,
    "from": {
      "id": 1424646511,
      "is_bot": false,
      "first_name": "Илья",
      "last_name": "Лящук",
      "username": "iliyalyachuk",
      "language_code": "ru"
    },
    "chat": {
      "id": 1424646511,
      "first_name": "Илья",
      "last_name": "Лящук",
      "username": "iliyalyachuk",
      "type": "private"
    },
    "date": 1659099213,
    "photo": [
      {
        "file_id": "AgACAgIAAxkBAAMqYuPYTHnTFqNQZ3DB5B-f_MovPOMAArm9MRud5CFLxgi3BP6dpsoBAAMCAANzAAMpBA",
        "file_unique_id": "AQADub0xG53kIUt4",
        "file_size": 1863,
        "width": 90,
        "height": 90
      },
      {
        "file_id": "AgACAgIAAxkBAAMqYuPYTHnTFqNQZ3DB5B-f_MovPOMAArm9MRud5CFLxgi3BP6dpsoBAAMCAANtAAMpBA",
        "file_unique_id": "AQADub0xG53kIUty",
        "file_size": 30064,
        "width": 320,
        "height": 320
      },
      {
        "file_id": "AgACAgIAAxkBAAMqYuPYTHnTFqNQZ3DB5B-f_MovPOMAArm9MRud5CFLxgi3BP6dpsoBAAMCAAN5AAMpBA",
        "file_unique_id": "AQADub0xG53kIUt-",
        "file_size": 133230,
        "width": 880,
        "height": 880
      },
      {
        "file_id": "AgACAgIAAxkBAAMqYuPYTHnTFqNQZ3DB5B-f_MovPOMAArm9MRud5CFLxgi3BP6dpsoBAAMCAAN4AAMpBA",
        "file_unique_id": "AQADub0xG53kIUt9",
        "file_size": 138716,
        "width": 800,
        "height": 800
      }
    ]
  }
}
```

После получения данного массива мы можем сохранить отправленное изображение на своём сервере. Для этого нам нужно с помощью метода **getFile** получить полный путь к изображению, передав ему в качестве параметра **file\_id**.

Полный код для сохранения будет выглядеть так:

```
/* токен */
$token = "5340791844:AAEXXDduvInvQrlWHRXykV91USOQSevrPVU";

/* массив с параметрами запроса */
$getQuery = array(
    "file_id" => "AgACAgIAAxkBAAMqYuPYTHnTFqNQZ3DB5B-f_MovPOMAArm9MRud5CFLxgi3BP6dpsoBAAMCAAN5AAMpBA",
);
$ch = curl_init("https://api.telegram.org/bot". $token ."/getFile?" . http_build_query($getQuery));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_HEADER, false);

$resultQuery = curl_exec($ch);
curl_close($ch);

/* записываем ответ в формате PHP массива */
$arrDataResult = json_decode($resultQuery, true);

/* записываем URL необходимого изображения */
$fileUrl = $arrDataResult["result"]["file_path"];

/* формируем полный URL до файла */
$photoPathTG = "https://api.telegram.org/file/bot". $token ."/" . $fileUrl;

/* забираем название файла */
$arrFilePath = explode("/", $fileUrl);
$newFilerPath = __DIR__ . "/img/" . $arrFilePath[1];

/* сохраняем файл на сервер */
file_put_contents($newFilerPath , file_get_contents($photoPathTG));
```

#### Скрипт для ответа на запросы через Хук

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

Поехали…

Токен бота запишем в константу **TG\_TOKEN**

```
define("TG_TOKEN", "5340791844:AAEXXDdu324vInvQrlWHyk8V91USOQSevrPVU");
```

Для удобства я создал специальные функции для отправки типовых запросов на сервер Telegram. Созданные функции принимают в качестве первого аргумента массив с параметрами запроса.

```
/* для отправки текстовых сообщений */
function TG_sendMessage($getQuery) {
    $ch = curl_init("https://api.telegram.org/bot". TG_TOKEN ."/sendMessage?" . http_build_query($getQuery));
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($ch, CURLOPT_HEADER, false);
    $res = curl_exec($ch);
    curl_close($ch);

    return $res;
}

/* для отправки изображений */
function TG_sendPhoto($arrayQuery) {
    $ch = curl_init('https://api.telegram.org/bot'. TG_TOKEN .'/sendPhoto');
    curl_setopt($ch, CURLOPT_POST, 1);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $arrayQuery);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($ch, CURLOPT_HEADER, false);
    $res = curl_exec($ch);
    curl_close($ch);

    return $res;
}

/* для получения данных о файле */
function TG_getFile($arrayQuery) {
    $ch = curl_init("https://api.telegram.org/bot". TG_TOKEN ."/getFile?" . http_build_query($arrayQuery));
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($ch, CURLOPT_HEADER, false);
    $res = curl_exec($ch);
    curl_close($ch);

    return $res;
}
```

Так же я добавил дополнительную функцию, которая выводит список всех файлов из директории. Данная функция будет отдавать массив с файлами, для последующего отправления рандомного файла в чат.

```
function list_files($path) {
    if ($path[mb_strlen($path) - 1] != '/') {
	$path .= '/';
    }
 
    $files = array();
    $dh = opendir($path);
    while (false !== ($file = readdir($dh))) {
	if ($file != '.' && $file != '..' && !is_dir($path.$file) && $file[0] != '.') {
	    $files[] = $file;
	}
    }
 
    closedir($dh);
    return $files;
}
```

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

В переменные **$textMessage** записывает текст сообщения, а в переменную **$chatId** записываем id чата.

```
$data = file_get_contents('php://input');

$arrDataAnswer = json_decode($data, true);
$textMessage = mb_strtolower($arrDataAnswer["message"]["text"]);
$chatId = $arrDataAnswer["message"]["chat"]["id"];
```

Ниже мы проверяем наличие файла в сообщение. Если пользователь отправил файл, то мы его сохраняем в папку с картинками.

Здесь желательно прописать более сложный обработчик для проверки типа файла, но сейчас, чтобы не затягивать видео, я просто буду проверять наличие файла в сообщение.

```
if(!empty($arrDataAnswer["message"]["photo"])) {
    $documentData = array_pop($arrDataAnswer["message"]["photo"]);
}
else if(!empty($arrDataAnswer["message"]["document"])) {
    $documentData = array_pop($arrDataAnswer["message"]["document"]);
}
```

Далее мы прописываем проверку на текст сообщения и в случае нужного текста отправляем ответное сообщение.

Если пользователь отправил «Привет», то мы в ответ отправляем сообщение «Привет! Есть фото для меня?». Данное сообщение отправляется с помощью ранее созданной функции **TG\_sendMessage()**.

```
if($textMessage == 'привет') {
    $textMessage_bot = "Привет! Есть фото для меня";

    $arrayQuery = array(
	'chat_id' 		=> 1424646511,
	'text'			=> $textMessage_bot,
	'parse_mode'	=> "html",
    );
    TG_sendMessage($arrayQuery);
}
```

Ниже пропишем подобный код для запроса изображения. Если пользователь отправил «хочу фото», то мы выбираем рандомное изображение и отправляем его пользователю с помощью функции **TG\_sendPhoto()**.

```
else if($textMessage == 'хочу фото') {
    $textMessage_bot = "Вот, держи!";

    $listFile = list_files(__DIR__ . "/img/");

    $max = count($listFile) - 1;
    $randIdFile = rand(0, $max);

    $filePath = __DIR__ . "/img/" . $listFile[$randIdFile];

    $arrayQuery = array(
         'chat_id' => $chatId,
	  "photo" => new CURLFile($filePath),
	  "caption" => "Вот твоё фото!"
    );
    TG_sendPhoto($arrayQuery);

} 
```

Далее пропишем код для сохранения любых, отправленных в чат, изображений.

```
if(!empty($documentData)) {

    $arrayQuery = array(
	"file_id" => $documentData["file_id"],
    );
    $resultQuery = TG_getFile($arrayQuery);

    /* записываем ответ в формате PHP массива */
    $arrDataResult = json_decode($resultQuery, true);

    /* записываем URL необходимого изображения */
    $fileUrl = $arrDataResult["result"]["file_path"];

    /* формируем полный URL до файла */
    $photoPathTG = "https://api.telegram.org/file/bot". TG_TOKEN ."/" . $fileUrl;

    /* забираем название файла */
    $arrFilePath = explode("/", $fileUrl);
    $newFilerPath = __DIR__ . "/img/" . $arrFilePath[1];

    /* сохраняем файл на сервер */
    file_put_contents($newFilerPath , file_get_contents($photoPathTG));

    $arrayQuery = array(
	'chat_id' => 1424646511,
	'text' => "Отличное фото! Я его, пожалуй, сохраню",
	'parse_mode' => "html",
    );
    TG_sendMessage($arrayQuery);

}
```

Ну и на последок, давайте пропишем ещё 2 условия. Первое условие будет отправлять кнопку в чат, а второе условие будет проверять нажатие на кнопку и отправлять дополнительное сообщение.

Запрос для отправки кнопок создаём аналогично запросу со словом «Привет». По запросу мы будем отправлять 2 кнопки с callback\_data — **but\_1** и **but\_2**.

```
if($textMessage == 'отправь кнопки') {
    $textMessage_bot = "Вот твои кнопки! Нажимай";

    $arrayQuery = array(
	'chat_id' => 1424646511,
	'text'	=> $textMessage_bot,
	'parse_mode'	=> "html",
	'reply_markup' => json_encode(array(
	    'inline_keyboard' => array(
		array(
		    array(
			'text' => 'Кнопка 1',
			'callback_data' => 'but_1',
		    ),

		    array(
			'text' => 'Кнопка 2',
			'callback_data' => 'but_2',
		    )
		),
	    ),
	)),
    );  
    TG_sendMessage($arrayQuery);
}
```

Теперь давайте пропишем проверку нажатия на кнопки. Здесь нам нужно записать в переменную **$dataBut** код нашей кнопки, чтобы по нему в дальнейшем делать проверку. В переменную **$textMessage** и **$chatId** мы так же записываем текст сообщения и id пользователя, только в этот раз достаём эти данные из массива с ключом **callback\_query**.

Ниже проверяем код нажатой кнопки и отправляем простое текстовое сообщение в ответ.

```
if($arrDataAnswer["callback_query"]) {
    $dataBut = $arrDataAnswer["callback_query"]["data"];
    $textMessage = mb_strtolower($arrDataAnswer["callback_query"]["message"]["text"]);
    $chatId = $arrDataAnswer["callback_query"]["message"]["chat"]["id"];

    if($dataBut == "but_1") {
	$arrayQuery = array(
	    'chat_id' => 1424646511,
	    'text' => "Ты нажал на 'КНОПКА 1'",
	    'parse_mode' => "html",
	);
	TG_sendMessage($arrayQuery);
    }
    else if($dataBut == "but_2") {
	$arrayQuery = array(
	    'chat_id' => 1424646511,
	    'text' => "Ты нажал на 'КНОПКА 2'",
	    'parse_mode' => "html",
	);
	TG_sendMessage($arrayQuery);
    }
}
```

Подведём итоги! В новом уроке, мы с вами научились обрабатывать запросы от Телеграма к серверу и прописали свой простой обработчик. Аналогичным образом вы можете прописать ответы на любые команды.

Так же, я хочу сказать, что в ближайшее время выйдет курс по разработке интернет-магазина в Telegram, разработанный на основе материалов из этого курса. Новый курс будет состоять только из практических материалов. По окончанию данного курса вы сможете создавать полноценные интернет-магазины в Telegram с каталогом, карточками товаров, корзиной и формой для оформления заказа. Все данные о заказах будут записываться в базу данных. По итогу вы сможете разработать отличное решение, которое легко продать как готовый кейс на фриланс биржах.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://book.konstantinsecurity.com/readme/architect/bots/telegram/sozdanie-telegram-bota-na-php-5-rabota-s-khukami.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
