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

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

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

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

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

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

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

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

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

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

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

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

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

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

Перед использованием самого каллбек апи придётся так же для начала подтвердить свой каллбек-скрипт, отдав вк нужную строку. Все запросы от каллбека будут лететь в формате 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; //Отправляем ответ в виде массива
	}
}

Рабочий пример, так сказать, демо, можно посмотреть вот здесь. )