Как создать бота в вк или про VK Callback API

Недавно на одном проекте мне пришлось столкнуться с ботом вк, который «живёт» в сообщениях группы. До этого я имел самый разный опыт создания ботов из личных страничек. Для того, чтобы бот мог нормально отвечать на сообщения приходилось делать самые разные извращения с кроном, таймерами, «запоминанием» сообщений и другими разными ужасами.
Как же я удивился, когда начав курить VK API групп касаемо сообщений, я увидел это чудо — Callback API.

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

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

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

Так вот, в этом callback запросе вк может уведомлять обо всём: новых сообщениях группы, исходящих от группы сообщениях, видео, аудио, комментариях, новых подписчиках и так далее. И мы, прочитав этот запрос, можем решить, как на этот запрос реагировать.
Прямо как платёжные системы. Магия.

Давайте по порядку. Для того, чтобы воспользоваться callback api для создания бота или ещё чего-бы то ни было, нужно:

Сначала, конечно же, создать группу.
Открыть раздел «Управление сообществом», в котором справа будет вот такое меню:

Выбираем работу с API, где и имеем все настройки.

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

Откройте её и посмотрите, что там есть. Сразу можете указать в типах событий «Входящее сообщение», остальные пока не трогаем, иначе они будут без нужды напрягать сервер как вк, так и Ваш.

Предлагаю сделать вот что:
Пусть бот будет отвечать на сообщения этим же сообщением, которое ему прислали, только задом-наперёд, ахах

Для начала давайте слепим два файла. callback.php и vk.class.php. Класс нужен чисто для удобства, чтобы не громоздить большую кучу кода в одном файлике. Сделаем всё аккуратно

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

Перед использованием самого каллбек апи придётся так же для начала подтвердить свой каллбек-скрипт, отдав вк нужную строку. Все запросы от каллбека будут лететь в формате JSON (уже говорил) и вк как бы сам показывает, как они будут выглядеть. Первый из них, это confirmation, который будет смотреться примерно так:

{"type":"confirmation","group_id":ИДГРУППЫ}

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

<?php
    $body = file_get_contents('php://input'); //Получаем в $body json строку
    $arr = json_decode($body, true); //Разбираем json запрос на массив в переменную $arr
    
    if ($arr['type'] == 'confirmation') { //Если нам пришел запрос на подтверждение callback скрипта, то
        exit("xxxxxxxx");  //отдаём в ответ свой код подтверждения выданный вк и останавливаем скрипт, дальше ему ничего не требуется
    }
    //Если скрипт выполняется дальше, значит это не confirmation, а одно из уведомлений.
    //Т.к. на данном этапе мы обрабатываем только входящее письмо, значит это входящее письмо
    if ($arr['type'] == 'message_new') { //Проверим на всякий случай, точно ли это входящее письмо
        function cir_strrev($stroka){ //Так как функция strrev не умеет нормально переворачивать кириллицу, нужен костыль через массив. Создадим функцию
            preg_match_all('/./us', $stroka, $array);
            return implode('',array_reverse($array[0]));
        }
    
        //Значит точно входящее. Можно уже и наш класс подключить
        include_once ('vk.class.php'); //Меж дела подключаем наш vk.class.php
        
        //Сразу и создадим этот класс, который будет написан чуть позже
        //Сюда пишем ключ апи, который создавали в самом начале
        $vk = new vk('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx');
        
        $sms = $arr['object']['body']; //Получаем текст сообщения, которое нам пришло.
        //О структуре этого массива который прилетел нам от вк можно узнать из официальной документации. Ссылка выше, до кода
        
        //Сразу и user_id получим, которому нужно отправлять всё это назад
        $vk_id = $arr['object']['user_id'];
        
        //Перевернём строку задом-наперёд используя php функцию strrev
        $sms_rev = cir_strrev($sms);
        
        //Используем наш ещё не написанный класс, для отправки сообщения в ответ
        $vk->send($vk_id, $sms_rev);
    }
    exit('ok'); //Обязательно возвращаем "ok", иначе вк отправит уведомление несколько раз

Теперь, собственно, сам vk.class.php:

<?php
//Задаём класс
class VK {
    public $token = ''; //Создаём публичную переменную для токена, который нужно отправлять каждый раз при использовании апи вк
    public function __construct($token) {
        $this->token = $token; //Забиваем в переменную токен при конструкте класса
    }
    
    public function send($id, $message) {   //Задаём публичную функцию send для отправки сообщений
        //Заполняем массив $data инфой, которую мы через api отправим до вк. О функции api "messages.send" можно почитать в официальной документации вк
        $data = array(
            'peer_id'      => $id,
            'message'      => $message,
            'v'            => '5.46', //Версия для функции. Её передавать нужно обязательно. Узнать нужную можно через официальную документацию вк
        );
        //Получаем ответ через функцию отправки до апи, которую создадим ниже
        $out = $this->request('messages.send', $data);
        //И пусть функция вернёт ответ. Правда в данном примере мы это никак не будем использовать, пусть будет задаток на будущее
        return $out;
    }  
    
    public  function request($method, $data = array()) {
        $curl = curl_init(); //мутим курл-мурл в переменную. Для отправки предпочтительнее использовать курл, но можно и через file_get_contents если сервер не поддерживает
        
        $data['access_token'] = $this->token; //токен, который нужно отправить вместе с запросом тоже нужно добавить в дату
        curl_setopt($curl, CURLOPT_URL, 'https://api.vk.com/method/' . $method); //Ссылки до разных методов апи вк выглядят так: https://api.vk.com/method/И_ТУТ_САМ_МЕТОД, поэтому метод вполне можно забивать в эту функцию и без всяких ссылок
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'POST'); //Отправляем через POST
        curl_setopt($curl, CURLOPT_POST, true);
        curl_setopt($curl, CURLOPT_POSTFIELDS, $data); //Сами данные отправляемые
        
        $out = json_decode(curl_exec($curl), true); //Получаем результат выполнения, который сразу расшифровываем из JSON'a в массив для удобства
        
        curl_close($curl); //Закрываем курл
        
        return $out; //Отправляем ответ в виде массива
    }
}