Пишем Telegram бота на ООП PHP

Мы уже создали простого бота, создали свой класс для работы с API, написали класс обработки webhook, теперь нужно все собрать, что бы получился рабочий бот, который можно будет без проблем масштабировать под разные задачи.

Состоять наш скрипт будет пока что из трех файлов: init.php - файл с настройками, webhook.php - файл обработки вебхуков и Bot.php - файл с классом для работы с API.

Так называемой "точной входа" у нас будет webhook.php и поэтому в нем в самом верху подключим файлы init.php и Bot.php через require_once.

init.php

В этот файл мы поместим наши константы. Зачем нам это нужно, когда можно константы поместить в файл webhook? Это мы начинаем думать на будущее. Ведь webhook.php это обработчик входящих данных, а вдруг нам нужно будет без обработки вебхуков отправлять сообщения пользователям бота? К примеру, ежедневные гороскопы. Тогда придется дублировать константы, а так мы их просто еще раз подключим в нужном файле.


<?php

//Константа для id администратора бота
const ADMIN_ID = '12345678910';

//Константа с API токеном
const BOT_TOKEN = '1234567890:ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890';

Bot.php

Уже рассматривали код этого класса в этом уроке. Просто класс для работы с API Telegram Bot.


<?php

class Bot {
    //бот токен.
    private $token;
    //Данные которые мы получим через webhook
    public $data;
    //Массив с данными о пользователе у которого диалог с ботом
    public $user;

    //создаем экземпляр бота, при создании бота указываем токен
    public function __construct($token) {
        //сохраняем в свойства полученный токен
        $this->token = $token;
        //получаем данные от webhook
        $this->data = json_decode(file_get_contents('php://input'), true);
        //записываем информарция о пользователе
        $this->setUser();
    }

    //Функция что бы установить пользователя в свойство user
    public function setUser() {
        //исходя из типа полученного update записываем информацию о текущем чате
        if($this->getType() == "callback_query") {
            $this->user = $this->data['callback_query']['message']['chat'];
        } elseif ($this->getType() == "inline_query") {
            $this->user = $this->data['inline_query']['from'];
        } else {
            $this->user = $this->data['message']['chat'];
        }
    }

    //получение id чата
    public function getChatId(){
        return $this->user['id'];
    }

    //Функция что бы получить тип сообщения
    //Другие типы сообщений мы рассмотрим в следующих уроках
    public function getType(){
        if (isset($this->data['callback_query'])) {
            return "callback_query";
        } elseif (isset($this->data['inline_query'])) {
            return "inline_query";
        } elseif (isset($this->data['message']['text'])) {
            //если это простой текст боту, то вернем "message".
            return "message";
        } elseif (array_key_exists('message', $this->data)) {
            return "object_message";
        } else {
            return false;
        }
    }

    //функция что бы получить текст сообщения из полученных данных
    public function getText(){
        if ($this->getType() == "callback_query") {
            return $this->data['callback_query']['data'];
        } elseif ($this->getType() == "inline_query") {
            return $this->data['inline_query']['query'];
        }
        return $this->data['message']['text'];
    }

    /*
    отправляем запрос к API Telegram, функция получает метод отправки
    запроса и массив данных, отправляет запрос и возвращает результат в виде массива.
    Подробней в https://docs.telegid.me/start/first-bot
    */

    public function sendApiQuery($method, $data = array()) {
        $ch = curl_init('https://api.telegram.org/bot' . $this->token . '/' . $method);
        curl_setopt_array($ch, [
            CURLOPT_POST => count($data),
            CURLOPT_POSTFIELDS => http_build_query($data),
            CURLOPT_SSL_VERIFYPEER => 0,
            CURLOPT_RETURNTRANSFER => 1,
            CURLOPT_TIMEOUT => 10
        ]);
        $res = json_decode(curl_exec($ch), true);
        curl_close($ch);
        return $res;
    }
}

webhook.php

Файл для обработки вебхуков. По сути функционал нашего бота. Подробно расписал в комментариях как он работает, но если по простому то: подключаем файлы с настройками и классом обработки API. Создаем экземпляр класса WebHook. Который определяет что за сообщения мы получили и в зависимости от текста реагирует по разному.


<?php

//Подключаем файл с настройками
require_once __DIR__ . '/init.php';

//Подключаем класс для работы с API
require_once __DIR__ . '/Bot.php';

//Запускаем обработку полученных данных
new WebHook();

//Создание класса
class WebHook
{
    //свойство в которое запишем id пользователя
    private $user_id;
    //свойство класса в которое мы установим объект для работы с API
    private $bot;

    public function __construct()
    {
        //Создаем объект бота для работы API
        $this->bot = new Bot(BOT_TOKEN);

        //Получаем id пользователя через функцию getChatId
        $this->user_id = $this->bot->getChatId();

        //запускаем обработку webhook
        $this->init();
    }


    public function init()
    {
        //Определяем тип получаемых данных
        $type = $this->bot->getType();
        if ($type == 'message') { //простой текст
            $this->type_message();
        } else {
            $this->bot->sendApiQuery('sendMessage', [
                'text' => 'Произошла ошибка, такие данные мы пока не умеем обрабатывать',
                'chat_id' => $this->user_id,
            ]);
            //прерываем работу скрипта
            exit;
        }
    }

    //получили текстовое сообщение и обработаем его
    public function type_message() {
        //Получаем текст сообщения
        $message = $this->bot->getText();
        //Конструкция switch case для обработки сообщения
        switch ($message) {
            case '/start':
                //получили текст start и проверяем от админа сообщение или нет
                if($this->user_id == ADMIN_ID) {
                    $text = 'Привет, создатель!';
                } else {
                    $text = 'Здравствуйте, товарищ!';
                }
                $this->bot->sendApiQuery('sendMessage', ['chat_id' => $this->user_id, 'text' => $text]);
                break;
            case '/joke':
                //Получили текст joke и отправляем шутку
                $this->bot->sendApiQuery('sendMessage', ['chat_id' => $this->user_id, 'text' => $this->getJokeText()]);
                break;
            default:
                //получили не start и не joke, отправляем сообщение заглушку
                $this->bot->sendApiQuery('sendMessage', ['chat_id' => $this->user_id, 'text' => 'Я пока не умею обрабатывать такие данные. Подождите когда разработчик придумает как реагировать на такие сообщения.']);
                break;
        }
    }

    //Функция для выдачи случайного анекдота
    private function getJokeText() {
        //массив текстов
        $array = [
            'Они жили счастливо до конца жизни, пока не узнали, что другие живут дольше и гораздо счастливее.',
            'Жизнь делится на два этапа — сначала нет ума, потом здоровья.',
            'Курить вредно, пить противно, а умирать здоровым обидно.',
            'Мы учимся на своих ошибках и потом от этих ошибок лечимся.',
            'Рецепт простейших бутербродов: просто уложите кусочек хлеба на другой кусочек хлеба.',
            'Классика — это разновидность литературы, которую люди предпочитают хвалить, а не читать.',
            'Странный этот мир: двое смотрят одно и то же, а видят прямо противоположное.',
            'Только познав чёрную полосу в жизни, вы начинаете ценить серую.',
            'Честный человек, мечтающий стать политиком, должен помнить, что такое перевоплощение в принципе невозможно.',
            'В жизни настоящего программиста есть только две женщины: Ася и Клава. Ну, не считая матери. Хотите сладких снов? — Спите в торте!',
            'Какая крыша не любит быстрой езды!',
            'Когда вы начинаете вникать в суть любой распродажи, помните, что в русском языке слова «скидка» и «кинуть» имеют один и тот же корень.',
            'Все мужчины одинаковы, только зарплаты у них разные.',
            'Он лучше всех знал, как всё делать... Правда, ничего у него не получалось.',
            'Если они постоянно смеются над вами, это означает, что вы приносите радость людям.',
            'У каждого человека столько тщеславия, сколько ему не хватает интеллекта.',
            'В России многое изменилось за пять лет, почти ничего за двести лет.',
            'Синоптики, как и сапёры, ошибаются только один раз. Но каждый день.',
            'Если бы не мои ноги, меня бы здесь не было.',
            'Если в человеке все прекрасно, то может быть это не наш человек?',
        ];
        //возвращаем случайное значение массива
        return $array[rand(0, count($array)-1)];
    }
}

Готово! Теперь у нас есть скрипт PHP бота, который мы может как угодно переделывать и расширять функционал. В следующих уроках мы попробуем сделать более сложного бота.

Опубликовано: 21 декабря 2023
Автор: Семен

Предыдущий урок:
Пишем свой class обработки webhook