Готовый код бота Telegram на PHP для принятия заявок в техподдержку
Поступила задача написать бота, который бы принимал заявки в техподдержку обслуживания автоматов с кофе, ну знаете те, которые наливают кофе в автоматическом режиме.
Содержание поста
- Требования к боту
- Окружение
- Выбор библиотеки для создания бота
- Развертывание окружения под бота на хостинге
- Установка библиотеки Telegram Bot SDK for PHP
- Регистрация бота в Telegram
- Полный код бота
- Подключение библиотеки /../vendor/autoload.php.
- Указываем API ключ в двух местах
- Создание функции, которая будет записывать все ошибки и данные при взаимодействии пользователя ботом в файл bot.log в папке logs.
- Создание функции, которая будет сохранять файлы в папке image, загруженные при создании заявки в вашу техподдержку.
- Указываем название Телеграм канала.
- Создание кнопок на клавиатуре.
- Приветственное сообщение при нажатии на кнопку /start
- Проверки
- Отправляем сообщение в канал техподдержки
- Демонстрации работы бота
Требования к боту
- Показ приветственного сообщения.
- Выбор адреса автомата кнопками.
- Загрузка фото с описанием проблемы.
- Проверки, чтобы пользователь не смог отправить пустое сообщение или без фото или без текста.
- Показ сообщения о принятии заявки.
- Отправка сообщения в Телеграм канал техподдержки после отправки фото с текстом.
- Сохранять на сервере загруженные картинки и обращения пользователей.
Окружение
Нам нужен будет домен, можно использовать любой поддомен, защищенный сертификат SSL на домен и немного места на вашем хостинге, который бы поддерживал PHP 8.
Выбор библиотеки для создания бота


Развертывание окружения под бота на хостинге
Я у себя создал поддомен, подключил к нему бесплатный сертификат Lets Encrypt, и теперь можно загружать наш пакет библиотеки Telegram bot SDK по FTP.

Вы можете просто распаковать архив библиотеки Telegram Bot SDK for PHP на вашем компьютере и загрузить содержимое по FTP, например через FileZilla.

Как вы видите я создал еще папки image для загрузки фото, которые будут сюда попадать после отправки сообщения и папку logs для записи всех действий с ботом. Также я создал файл bot.php, который будет содержать код нашего бота.
Установка библиотеки Telegram Bot SDK for PHP
Устанавливается библиотека с помощь composer, если вы не знаете, что это такое, то лучше обратиться в техподдержку вашего хостинга с просьбой помочь его установить.
Устанавливается composer через терминал.
Перейдите в каталог с нашим сайтом:
cd bot.az8.ru
После этого загрузим файл установки:
curl https://getcomposer.org/installer -o composer-setup.php
Далее необходимо запустить его и передать параметр –install-dir. В этом параметре надо будет определить каталог, в который будет загружен phar-архив. Мы установим его в каталог bin
php composer-setup.php --install-dir=bin
Вывод этих двух команд будет примерно следующим:
All settings correct for using Composer
Downloading...
Composer (version 1.9.0) successfully installed to: /home/c0000/bot.az8.ru/bin/composer.phar
Use it: php ./bin/composer.phar
Далее запускаем Composer с помощью следующей команды:
bin/php bin/composer.phar
Проверяем правильно ли мы все сделали. Должна выводиться версия composer.
composer --version
Устанавливаем Telegram Bot SDK for PHP вводя в терминале
composer require irazasyed/telegram-bot-sdk
На выходе мы должны получить папку vendor

Обратите внимание, что у меня библиотека установлена выше каталога www где будет располагаться сам бот. Вы можете установить библиотеку куда угодно и потом в файле бота прописать путь до местонахождения папки vendor.
Теперь нам осталось зарегистрировать бота в Telegram получив API ключ, и через вебхук, также посредством терминала подключить его к нашему домену.
Регистрация бота в Telegram
1. Нужно перейти по ссылке - https://t.me/BotFather и выбрать команду /newbot

2. Выберите имя и username для бота и получите API ключ

3. Создать канал в Telegram.
Думаю не стоит заострять внимание на создание канала. Это довольно просто.
Теперь все готово для создания бота. По итогу у нас есть установленная библиотека Telegram SDK for PHP, полученный API ключ, создан канал в Telegram.
Полный код бота
<?php
require __DIR__ . '/../vendor/autoload.php'; // Подключаем autoload файл из Composer
use Telegram\Bot\Api;
use Telegram\Bot\Keyboard\Keyboard;
function logMessage($message) {
$logFile = __DIR__ . '/logs/bot.log';
$currentTime = date('Y-m-d H:i:s');
if (!file_exists(dirname($logFile))) {
mkdir(dirname($logFile), 0777, true);
}
file_put_contents($logFile, "[$currentTime] $message\n", FILE_APPEND);
}
function downloadFile($url, $saveTo) {
$ch = curl_init($url);
$fp = fopen($saveTo, 'wb');
if ($fp === false) {
logMessage("Failed to open file: $saveTo");
return false;
}
curl_setopt($ch, CURLOPT_FILE, $fp);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_exec($ch);
if (curl_errno($ch)) {
logMessage('cURL error: ' . curl_error($ch));
}
curl_close($ch);
fclose($fp);
return true;
}
try {
// Создаем экземпляр API
$telegram = new Api('Сюда вставляем ваш API ключ');
$channelId = '@Ваштелеграмканал';
// Получаем обновления
$updates = $telegram->getWebhookUpdates();
logMessage("Received update: " . json_encode($updates));
if (isset($updates['message'])) {
$message = $updates['message'];
$chatId = $message['chat']['id'];
logMessage("Message from chat ID $chatId");
if (isset($message['text'])) {
$text = $message['text'];
logMessage("Received text message: $text");
if ($text == '/start') {
// Приветственное сообщение и кнопки выбора адреса
$keyboard = Keyboard::make()
->inline()
->row([
Keyboard::inlineButton(['text' => 'Кофемат №1', 'callback_data' => 'Вайнера 55']),
Keyboard::inlineButton(['text' => 'Кофемат №2', 'callback_data' => 'Соболева 5'])
])
->row([
Keyboard::inlineButton(['text' => 'Кофемат №3', 'callback_data' => 'Успенский 151']),
Keyboard::inlineButton(['text' => 'Кофемат №4', 'callback_data' => 'Ленина 83'])
])
->row([
Keyboard::inlineButton(['text' => 'Кофемат №5', 'callback_data' => 'Монтажников 26']),
]);
$telegram->sendMessage([
'chat_id' => $chatId,
'text' => 'Это техническая поддержка. Мы поможем с проблемой кофемата. Выберите адрес вашего автомата, далее загрузите фото чека с описанием проблемы и укажите ваш номер телефона для связи.',
'reply_markup' => $keyboard
]);
logMessage("Sent start message with keyboard");
} else {
// Получаем сохраненный адрес кофемата
$sessionFile = __DIR__ . "/sessions/$chatId.txt";
if (!file_exists($sessionFile)) {
$telegram->sendMessage([
'chat_id' => $chatId,
'text' => 'Пожалуйста, выберите ваш кофемат перед отправкой сообщения. Без этого мы не сможем решить вашу проблему'
]);
logMessage("Reminded user to select a coffee machine");
} else {
$address = file_get_contents($sessionFile);
// Сообщение пользователю о необходимости отправки фото
$telegram->sendMessage([
'chat_id' => $chatId,
'text' => 'Пожалуйста, отправьте фото чека с описанием проблемы и укажите ваш номер телефона. Без этого мы не сможем решить вашу проблему'
]);
logMessage("Sent reminder to user to send photo");
}
}
} elseif (isset($message['photo'])) {
// Логика обработки загруженного фото
$caption = $message['caption'] ?? '';
$fileId = end($message['photo'])['file_id'];
logMessage("Received photo with caption: $caption");
// Проверка наличия текста
if (empty($caption)) {
$telegram->sendMessage([
'chat_id' => $chatId,
'text' => 'Пожалуйста, отправьте еще раз фото чека с текстом описания проблемы и укажите ваш номер телефона для связи.'
]);
logMessage("Reminded user to send photo with caption");
} else {
// Получение файла
$file = $telegram->getFile(['file_id' => $fileId]);
$filePath = $file->getFilePath();
$fileUrl = "https://api.telegram.org/file/botВставляем ваш ключ API/{$filePath}";
// Создание директории, если не существует
$imageDir = __DIR__ . '/image/';
if (!file_exists($imageDir)) {
mkdir($imageDir, 0777, true);
}
// Скачивание файла через cURL
$localFilePath = $imageDir . basename($filePath);
if (downloadFile($fileUrl, $localFilePath)) {
logMessage("Saved photo to $localFilePath");
// Получаем сохраненный адрес кофемата
$sessionFile = __DIR__ . "/sessions/$chatId.txt";
if (!file_exists($sessionFile)) {
$telegram->sendMessage([
'chat_id' => $chatId,
'text' => 'Пожалуйста, выберите ваш кофемат перед отправкой сообщения. Без этого мы не сможем решить вашу проблему'
]);
logMessage("Reminded user to select a coffee machine");
} else {
$address = file_get_contents($sessionFile);
// Отправка информации в канал
$telegram->sendPhoto([
'chat_id' => $channelId,
'photo' => $fileId,
'caption' => "Адрес кофемата: $address\nОписание проблемы:\n$caption"
]);
logMessage("Forwarded photo to channel");
// Сообщение пользователю об успешной загрузке
$telegram->sendMessage([
'chat_id' => $chatId,
'text' => 'Спасибо! Ваша заявка принята.'
]);
logMessage("Sent confirmation message to user");
}
} else {
logMessage("Failed to save photo to $localFilePath");
}
}
}
} elseif (isset($updates['callback_query'])) {
$callbackQuery = $updates['callback_query'];
$chatId = $callbackQuery['message']['chat']['id'];
$data = $callbackQuery['data'];
logMessage("Received callback query: $data from chat ID $chatId");
// Обработка выбора адреса
$telegram->sendMessage([
'chat_id' => $chatId,
'text' => 'Вам необходимо загрузить фото чека и описать проблему, указав номер телефона для связи. Без этого мы не сможем решить вашу проблему!'
]);
logMessage("Sent address selection message to user");
// Сохраняем адрес кофемата в сессии пользователя
$sessionDir = __DIR__ . '/sessions/';
if (!file_exists($sessionDir)) {
mkdir($sessionDir, 0777, true);
}
file_put_contents($sessionDir . "$chatId.txt", $data);
}
} catch (Exception $e) {
logMessage("Error: " . $e->getMessage());
}
?>
Теперь разберем код, что за что отвечает, чтобы могли скорректировать его под свои задачи.
Подключение библиотеки /../vendor/autoload.php.
У меня папка vendor находиться выше каталога, где находиться файл bot.php. Если у вас по другому, то вам необходимо скорректировать путь до папки.
require __DIR__ . '/../vendor/autoload.php'; // Подключаем autoload файл из Composer
use Telegram\Bot\Api;
use Telegram\Bot\Keyboard\Keyboard;
Указываем API ключ в двух местах
$telegram = new Api('Сюда вставляем ваш API ключ');
$fileUrl = "https://api.telegram.org/file/botВставляем ваш ключ API/{$filePath}";
Во второй строке обратите внимание, что ключ вставляется после слова bot.
Создание функции, которая будет записывать все ошибки и данные при взаимодействии пользователя ботом в файл bot.log в папке logs.
function logMessage($message) {
$logFile = __DIR__ . '/logs/bot.log';
$currentTime = date('Y-m-d H:i:s');
if (!file_exists(dirname($logFile))) {
mkdir(dirname($logFile), 0777, true);
}
file_put_contents($logFile, "[$currentTime] $message\n", FILE_APPEND);
}
Создание функции, которая будет сохранять файлы в папке image, загруженные при создании заявки в вашу техподдержку.
function downloadFile($url, $saveTo) {
$ch = curl_init($url);
$fp = fopen($saveTo, 'wb');
if ($fp === false) {
logMessage("Failed to open file: $saveTo");
return false;
}
curl_setopt($ch, CURLOPT_FILE, $fp);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_exec($ch);
if (curl_errno($ch)) {
logMessage('cURL error: ' . curl_error($ch));
}
curl_close($ch);
fclose($fp);
return true;
}
Указываем название Телеграм канала.
$channelId = '@Ваштелеграмканал';
Название канала указывается в виде @canal
.
Создание кнопок на клавиатуре.
У меня 5 кофематов и пользователю нужно дать выбрать, тот автомат с которым проблемы, а техподдержке должен приходить фактический адрес автомата, который выбрал пользователь
$keyboard = Keyboard::make()
->inline()
->row([
Keyboard::inlineButton(['text' => 'Кофемат №1', 'callback_data' => 'Текст, которые передается в сообщение в ваш канал']),
Keyboard::inlineButton(['text' => 'Кофемат №2', 'callback_data' => 'Текст, которые передается в сообщение в ваш канал'])
])
->row([
Keyboard::inlineButton(['text' => 'Кофемат №3', 'callback_data' => 'Текст, которые передается в сообщение в ваш канал']),
Keyboard::inlineButton(['text' => 'Кофемат №4', 'callback_data' => 'Текст, которые передается в сообщение в ваш канал'])
])
->row([
Keyboard::inlineButton(['text' => 'Кофемат №5', 'callback_data' => 'Текст, которые передается в сообщение в ваш канал']),
]);
->row
создает колонки и в данном коде у нас получиться 2 колонки с 2мя кнопками
->row([
Keyboard::inlineButton(['text' => 'Кофемат №1', 'callback_data' => 'Текст, которые передается в сообщение в ваш канал']),
Keyboard::inlineButton(['text' => 'Кофемат №2', 'callback_data' => 'Текст, которые передается в сообщение в ваш канал'])
])
Как выглядят кнопки?

Вместо названий моих кнопок вы можете поменять на свои.'callback_data' => 'Текст
, которые передается в сообщение в ваш канал' Текст сообщения замените на свой.
Приветственное сообщение при нажатии на кнопку /start
Выше на скриншоте мы видим сообщение. Оно меняется в этом коде:
$telegram->sendMessage([
'chat_id' => $chatId,
'text' => 'Это техническая поддержка. Мы поможем с проблемой кофемата. Выберите адрес вашего автомата, далее загрузите фото чека с описанием проблемы и укажите ваш номер телефона для связи.',
'reply_markup' => $keyboard
]);
Проверки
Логика работы бота такая: пользователь должен выбрать кнопки с кофематами, загрузить фото с описанием проблемы, а это значит мы должны предотвратить отправку сообщений в канал поддержки без нажатия на кнопки, без фото и текста.
Код проверок выбора кнопок:
if (!file_exists($sessionFile)) {
$telegram->sendMessage([
'chat_id' => $chatId,
'text' => 'Пожалуйста, выберите ваш кофемат перед отправкой сообщения. Без этого мы не сможем решить вашу проблему'
]);
logMessage("Reminded user to select a coffee machine");
}
После выбора кнопок пользователь должен загрузить фото с описанием проблемы и если он отправляет простое сообщение, то показываем текст:
$telegram->sendMessage([
'chat_id' => $chatId,
'text' => 'Пожалуйста, отправьте фото чека с описанием проблемы и укажите ваш номер телефона. Без этого мы не сможем решить вашу проблему'
]);
logMessage("Sent reminder to user to send photo");
Если пользователь загружает фото, но добавляет текст к нему, то показываем ему текст:
if (empty($caption)) {
$telegram->sendMessage([
'chat_id' => $chatId,
'text' => 'Пожалуйста, отправьте еще раз фото чека с текстом описания проблемы и укажите ваш номер телефона для связи.'
]);
logMessage("Reminded user to send photo with caption");
}
Отправляем сообщение в канал техподдержки
Теперь, когда пользователь прошел все проверки и сделал все как надо отправляем сообщение в наш канал. Текст "Адрес кофемата" вы можете изменить на свой вариант:
$telegram->sendPhoto([
'chat_id' => $channelId,
'photo' => $fileId,
'caption' => "Адрес кофемата: $address\nОписание проблемы:\n$caption"
]);
Демонстрации работы бота

Как видим бот обработал успешно все ошибки и не дал пользователю отправить простое сообщение и без выбора кнопок и загрузки фото.
Вот и все! Успехов вам в вашем проекте!