Привет жителям Хабра.

В процессе изучения PHP, пришла в голову мысль, для практики написать какого-нибудь бота на PHP, без использования фрэймворков.

Выбор пал на бота, который будет получать информацию о жизни домена (whois), затем доставать оттуда дату регистрации и добавлять этот домен в БД (mysql), с последующий привязкой к пользователю и уведомлением в telegram.

Исходный код в моем репозитории на гите.

Мне захотелось использовать MVC структуру, что оказалось на мой взгляд не совсем верным, так как view не был задействован и это уже нельзя назвать mvc, но да ладно…

Структура приложения




Controllers — Связующее звено, соединяет model и логику приложения.
Models — Файлы «бизнес-логики» приложения (Старался «запихнуть» сюда весь sql).
Core — Файлы «ядра» приложения.
Library — Библиотеки, использовал библиотеку для парсинга информации о доменном имени.

Маршрутизация


Файл маршрутизации (routes.php) расположил в каталоге core.
В приложении добавлены 2 адреса:

/bot — По этому адресу идет telegramm (необходимо установить webhook на этот адрес «uri/bot»).
/check — По этому адресу ломиться wget с помощью cron 1 в день (в 12 часов), об этом чуть позже.

BotController


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

json_decode(file_get_contents('php://input'), JSON_OBJECT_AS_ARRAY);

php://input — получаем тело POST запроса

CheckerController


При переходе в данный контроллер, срабатывает скрипт, который проверяет все добавленные домены и ssl сертификаты на окончание срока действия, с интервалом:

  • текущая дата
  • 2 дня
  • 7 дней
  • 30 дней

И отправляет уведомления если дата срока действия домена и ssl сертификата заканчивается.

Добавление пользователей


Когда пользователь пишет сообщение боту webhook telegram, отправляет его на наш сайт, далее нам приходит сообщение в json формате, которое нам нужно декодировать, и преобразовать в массив для дальнейшей работы.



Будем работать с массивом message.

Получаем из массива message['chat']: id , first_name
где:

id — идентификатор чата
first_name — Имя пользователя

А из массива message['text'], получаем отправленную пользователем команду.

Находим пользователя в таблице users, если его нет, создаем


// Ищем пользователя
$sql= "SELECT user_id FROM users WHERE chat_id=?";
$stmt = $db->prepare($sql);
$stmt->execute([$chat_id]);
$rows = $stmt->fetch(PDO::FETCH_ASSOC);
return (int)$rows['user_id'];


// Создаем пользователя
$sql = 'INSERT INTO users (user_name, chat_id) VALUES (:user_name, :chat_id)';
$insert = $db->prepare($sql);
$insert->execute([':user_name' => $name, ':chat_id' => $chat_id]);
return true;

Добавление Доменов и SSL


При отправке команды /addDomain url боту, получаем url домена из команды и получаем данные о регистрации домена с помощью этой библиотеки.

Получаем домен


Нам приходит ответ в виде текста:



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


preg_match('/Registry\sExpiry\sDate:\s(.*)\\r/', $date, $matches);
if (!$matches[1]){
    preg_match('/paid-till:\s*(.*)\\n/', $date, $matches);
}
$matches[1] = $this->formatDate($matches[1]);
return $matches[1];

Получаем SSL


SSL сертификат решил получать с помощью openssl для linux.


$getDomainSSL = shell_exec("echo | openssl s_client -servername $url -connect $url:443 2>/dev/null | openssl x509 -noout -dates");
preg_match('~notAfter=(\w+)\s(\d+)\s.+\s(\d+)~', $getDomainSSL, $matches);
$date =  $matches[2].$matches[1].$matches[3];
$date =  date("Y.m.d", strtotime($date));
$date = str_replace('.','-',$date);
return $date;

Таким образом получаем:

echo | openssl s_client -servername google.com -connect google.com:443 2>/dev/null | openssl x509 -noout -dates
notBefore=Jan  7 15:47:12 2020 GMT
notAfter=Mar 31 15:47:12 2020 GMT

Разбираем полученное с помощью регулярных выражений

preg_match('~notAfter=(\w+)\s(\d+)\s.+\s(\d+)~', $getDomainSSL, $matches);



Остается только добавить полученные данные в таблицу.


$sql = 'INSERT INTO domains (domain_name, date_start, date_end, date_end_ssl) VALUES (:domain_name, :date_start, :date_end, :date_end_ssl)';
$insert = $db->prepare($sql);
$insert->execute([':domain_name' => $url, ':date_end' => $exp, ':date_end_ssl'  => $ssl_date]);

Привязываем домен и ssl к пользователю


Полученные данные нужно просто записать в промежуточную таблицу для того чтобы не «размножать» домены.


$sql = 'INSERT INTO domain_users (user_id, domain_id) VALUES (:user_id, :domain_id)';
$insert = $db->prepare($sql);
$insert->execute([':user_id' => $user_id, ':domain_id' => $domain_id]);

Проверка даты окончания домена


Когда wget переходит по адресу /check, происходит выборка всех доменов и ssl сертификатов, у которых срок действия подходит к концу, и если такие есть, отправляет сообщение в чат к которому привязан данный домен.


$db = $this->db;

$sql= "
	SELECT user_name, chat_id, domain_name, date_end 
	FROM domain_users JOIN users USING (user_id)
	JOIN domains USING (domain_id)
	WHERE (
		domains.date_end = CURDATE() OR 
		domains.date_end = CURDATE() + INTERVAL 2 DAY OR 
		domains.date_end = CURDATE() + INTERVAL 7 DAY  OR 
		domains.date_end = CURDATE() + INTERVAL 30 DAY
	)
";
$stmt = $db->prepare($sql);
$stmt->execute();
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);

CRON


Крон в данном проекте нужен для организации перехода по адресу '/check', каждый день в 12 часов.

crontab -e
0 12 * * * wget url/check

TOR PROXY


Пару слов о отправки сообщений в telegram.

К сожалению на территории мой страны (Россия), блокируют telegram, в том числе и его api.
Приходится использовать прокси, мой выбор пал на tor proxy (еще с прошлого проекта).

Его необходимо просто установить.

sudo apt-get install tor

Затем прокси будет доступен по порту 9050.


curl_setopt($myCurl, CURLOPT_PROXYTYPE, 7);
curl_setopt($myCurl, CURLOPT_PROXY, "127.0.0.1:9050");

Всем спасибо за прочтение данной статьи!

Эта моя первая статья, поэтому не судите строго :)

Комментируйте данный пост, с удовольствием приму любую критику.

Исходный код проекта в моем репозитории на github :)