Бот в telegram, который следит за доменом
Привет жителям Хабра.
В попытках изучения PHP и долгих раздумьях, пришла в голову мысль, для практики написать какого-нибудь бота на PHP, без использования фрэймворков.
Выбор пал на бота, который будет получать информацию о жизни домена (whois), затем доставать оттуда дату регистрации и добавлять этот домен в БД (mysql), с последующий привязкой к пользователю и уведомлением в telegram.
Исходный код в моем репозитории на гите.
Мне захотелось использовать MVC структуру, что оказалось на мой взгляд не совсем верным, так как view не был задействован и это уже нельзя назвать mvc, но да ладно…
Структура приложения
Contollerrs — Связующее звено, соединяет 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 часов.
crintab -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:)