Биб­лиоте­ка MadelineProto поз­воля­ет с лег­костью написать собс­твен­ный Telegram-кли­ент, который мож­но исполь­зовать по‑раз­ному — от отправ­ки сооб­щения все­му кон­такт‑лис­ту до взло­ма Telegram-акка­унтов. При этом биб­лиоте­ка не исполь­зует Bot API, что зна­читель­но упро­щает раз­работ­ку. В этой статье мы раз­берем базовые фун­кции кли­ента: авто­риза­цию, получе­ние спис­ка кон­тактов, извле­чение информа­ции о поль­зовате­лях и отправ­ку им сооб­щений.
 

Подготовительные мероприятия

Са­му биб­лиоте­ку MadelineProto уста­нав­ливать и обновлять не нуж­но. Скрип­ты устро­ены таким обра­зом, что, если файл биб­лиоте­ки отсутс­тву­ет, он будет заг­ружен авто­мати­чес­ки. Одна­ко тебе нуж­но позабо­тить­ся о двух вещах:

  • Биб­лиоте­ка тре­бует PHP 8.2+, поэто­му, если у тебя его нет, при­дет­ся где‑то раз­добыть. Как пра­вило, в пос­ледних вер­сиях Ubuntu имен­но эта вер­сия — ска­чай и раз­верни образ пос­ледней Ubuntu в вир­туаль­ной машине. Если Linux — твоя основная сис­тема и пере­уста­нав­ливать тебе ее сов­сем не хочет­ся, я рас­ска­жу, как обно­вить­ся.
  • Те­бе нуж­но соз­дать свое при­ложе­ние Telegram. Для это­го зарегис­три­руй­ся на my.telegram.org, перей­ди в раз­дел API development tools и запол­ни фор­му. Далее ты получишь api_id и api_hash — сох­рани эти зна­чения, они тебе при­годят­ся.

С регис­тра­цией при­ложе­ния раз­берешь­ся сам, там все прос­то, а вот для обновле­ния PHP до нуж­ной вер­сии тебе надо под­клю­чить репози­торий Ondřej Surý PPA. Вве­ди сле­дующие коман­ды:

sudo apt install software-properties-common -y && sudo add-apt-repository ppa:ondrej/php -y
sudo apt update && sudo apt upgrade

Пос­ле это­го вве­ди коман­ду уста­нов­ки PHP 8.2:

sudo apt install php8.2

Ес­ли у тебя раз­вернут Apache, то уста­нови еще один пакет и переза­пус­ти веб‑сер­вер:

sudo apt install libapache2-mod-php8.2
sudo systemctl restart apache2

За­тем вве­ди коман­ду php -v. Ты дол­жен уви­деть при­мер­но такой вывод:

PHP 8.2.0 (cli) (built: Dec 10 2022 10:52:42) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.2.0, Copyright (c) Zend Technologies
with Zend OPcache v8.2.0, Copyright (c), by Zend Technologies

Как видишь, ничего слож­ного.

 

Авторизация

На­пишем прос­тей­ший сце­нарий, выпол­няющий авто­риза­цию поль­зовате­ля. Для это­го нам понадо­бит­ся Telegram-акка­унт. Можешь тре­ниро­вать­ся на сво­ем акка­унте: не бес­покой­ся, ты всег­да смо­жешь поменять пароль и биб­лиоте­ка точ­но не уго­нит твою учет­ку.

<?php
$userDir = '';
$api_id = 11111; // Добавь сюда api_id
$api_hash = 'xxxxxxx'; // Добавь сюда api_hash
$phone = 'твой номер телефона'; // Номер телефона
if (!file_exists($userDir . '/madeline.php')) {
copy('https://phar.madelineproto.xyz/madeline.php', 'madeline.php');
}
include_once $userDir . '/madeline.php';
$settings = (new \danog\MadelineProto\Settings\AppInfo)
->setApiId($api_id)
->setApiHash($api_hash);
use danog\MadelineProto\Tools;
$MadelineProto = new \danog\MadelineProto\API($userDir . "/session", $settings);
$MadelineProto->phoneLogin($phone);
$authorization = $MadelineProto->completePhoneLogin(Tools::readLine('Enter the phone code: '));
if ($authorization['_'] === 'account.needSignup') {
echo "needSignup for $phone";
die();
}
if ($authorization['_'] === 'account.password') {
$authorization = $MadelineProto->complete2falogin(Tools::readLine('Please enter your password (hint '.$authorization['hint'].'): '));
}

Те­перь раз­берем­ся, что мы дела­ем. Для начала мы зада­ем необ­ходимые перемен­ные: $api_id, $api_hash, $userDir. С пер­выми дву­мя все ясно, а $userDir ты можешь исполь­зовать, если нуж­но, что­бы madeline.php заг­ружал­ся в какой‑то дру­гой, а не текущий каталог. Если эта перемен­ная содер­жит пус­тое зна­чение, madeline.php будет заг­ружать­ся в текущий каталог и отту­да инклу­дить­ся.

Да­лее мы соз­даем объ­ект с парамет­рами. В ста­рых вер­сиях MadelineProto для ука­зания парамет­ров при­ложе­ний исполь­зовал­ся ассо­циатив­ный мас­сив, поэто­му в интерне­те ты смо­жешь най­ти уста­рев­шие скрип­ты с исполь­зовани­ем этой биб­лиоте­ки, которые работать не будут как раз по этой самой при­чине — вмес­то мас­сива теперь исполь­зует­ся объ­ект с парамет­рами.

Этот объ­ект нуж­но передать конс­трук­тору \danog\MadelineProto\API(). Пер­вый его параметр — наз­вание катало­га с сес­сией вхо­дяще­го в учет­ку поль­зовате­ля, а вто­рой — это как раз объ­ект с парамет­рами. Обра­ти вни­мание: мы так­же исполь­зуем $userDir, что­бы каталог с сес­сией мог находить­ся в дру­гой пап­ке, если тебе так будет удоб­нее. Ког­да ты пишешь Telegram-кли­ент для одно­го поль­зовате­ля, то никаких проб­лем не воз­ника­ет. Но если ты пла­ниру­ешь исполь­зовать кли­ент для вхо­да мно­гих поль­зовате­лей, для под­держа­ния поряд­ка нуж­но будет дер­жать сес­сии в раз­ных катало­гах — для каж­дого поль­зовате­ля при­дет­ся соз­давать отдель­ную пап­ку. Думаю, это понят­но.

Ме­тод phoneLogin() при­меня­ется для вхо­да с исполь­зовани­ем номера телефо­на. Ука­жи номер телефо­на в том фор­мате, в котором ты ука­зыва­ешь его при вхо­де в Telegram, но без +.

Пос­ле это­го на твой телефон будет отправ­лено сооб­щение с 2FA-кодом. Как пра­вило, оно отправ­ляет­ся уже не по SMS, а в твой Telegram, поэто­му открой при­ложе­ние на телефо­не и перей­ди к чату с наз­вани­ем Telegram.

Ес­ли мес­сен­джер на тво­ем телефо­не не уста­нов­лен, скрипт сооб­щит об этом и умрет — сра­бота­ет вызов die().

Ес­ли для тво­его акка­унта уста­нов­лен пароль, скрипт зап­росит его. Разуме­ется, если пароль пра­виль­ный, то все хорошо, а вот если нет, ты получишь соот­ветс­тву­ющее сооб­щение от самой биб­лиоте­ки. Отдель­но обра­баты­вать исклю­чение неп­равиль­но вве­ден­ного пароля не ста­ну — ты при желании спра­вишь­ся сам.

Пос­ле вхо­да в акка­унт ты смо­жешь выпол­нять все­воз­можные дей­ствия, нап­ример зап­рашивать спи­сок кон­тактов, отправ­лять им сооб­щения, читать диало­ги (чаты), уда­лять сооб­щения и диало­ги. Биб­лиоте­ка MadelineProto поз­воля­ет в этом пла­не выпол­нять мно­го раз­личных дей­ствий. Для начала поп­робу­ем получить спи­сок кон­тактов.

 

Получаем список контактов

Ме­тод getContacts() получа­ет спи­сок кон­тактов из тво­его Telegram-акка­унта. Рас­смот­рим фраг­мент кода, выводя­щий кон­такты на экран:

$contacts = $MadelineProto->contacts->getContacts();
$users = $contacts['users'];
foreach ($users as $user) {
$firstname = $user['first_name'] ?? '';
$lastname = $user['last_name'] ?? '';
$username = $user['username'] ?? '';
$uphone = $user['phone'] ?? '';
echo "$user[id]\t$firstname\t$lastname\t$username\t$uphone\n";
}

Что делать с кон­такта­ми даль­ше? Да что угод­но. Нап­ример, мож­но сох­ранить все эти кон­такты в таб­лице базы дан­ных, что­бы они никуда не потеря­лись. А еще мож­но отпра­вить каж­дому кон­такту сооб­щение.

 

Сохраняем контакты в базу данных

Не будем изоб­ретать велоси­пед и уста­новим MySQL для работы с базой дан­ных:

sudo apt install mysql-server mysql-client

От­крой кли­ент MySQL, нап­ример так:

mysql -u root

Ес­ли ты уже успел задать пароль для поль­зовате­ля root, добавь к пре­дыду­щей коман­де параметр -p, что­бы прог­рамма зап­росила пароль поль­зовате­ля.

Мы не будем соз­давать новую базу дан­ных, а ста­нем исполь­зовать уже име­ющуюся с име­нем test:

use test;

Соз­дай таб­лицу contacts:

CREATE TABLE `contacts` (
`id` bigint NOT NULL AUTO_INCREMENT,
`uid` varchar(50) NOT NULL,
`fname` varchar(200) DEFAULT NULL,
`lname` varchar(200) DEFAULT NULL,
`username` varchar(200) DEFAULT NULL,
`tid` varchar(250) DEFAULT NULL,
`phone` varchar(50) DEFAULT NULL,
PRIMARY KEY(id)
);

Наз­начение полей:

  • id — прос­то счет­чик кон­такта, он же пер­вичный ключ, что­бы было удоб­нее работать с таб­лицей;
  • uid — номер поль­зовате­ля, которо­му при­над­лежит текущая учет­ная запись (что­бы кон­такты от раз­ных поль­зовате­лей не переме­шива­лись);
  • fname — имя кон­такта (first_name);
  • lname — фамилия кон­такта (last_name);
  • username — имя поль­зовате­ля кон­такта;
  • tid — Telegram ID кон­такта;
  • phone — телефон кон­такта (если дос­тупен для счи­тыва­ния).

Те­перь нуж­но вый­ти из MySQL и про­дол­жить кодинг:

exit

Вер­немся к нашему фраг­менту вывода кон­тактов. Рас­ширим его, добавив код встав­ки кон­тактов в таб­лицу contacts:

$contacts = $MadelineProto->contacts->getContacts();
$users = $contacts['users'];
foreach ($users as $user) {
$firstname = $user['first_name'] ?? '';
$lastname = $user['last_name'] ?? '';
$username = $user['username'] ?? '';
$uphone = $user['phone'] ?? '';
// Настоятельно рекомендуется перед добавлением в БД:
$firstname = $mysqli->real_escape_string($firstname);
$lastname = $mysqli->real_escape_string($lastname);
$username = $mysqli->real_escape_string($username);
$uphone = $mysqli->real_escape_string($uphone);
echo "$user[id]\t$firstname\t$lastname\t$username\t$uphone\n";
// Проверяем, есть ли контакт в списке
$sql = "select * from contacts where uid = "$phone" and tid = $user[id]";
$result = $mysqli->query($sql) or die($mysqli->error);
if ($result->num_rows == 0) {
// Создаем новый контакт
$sql = "insert into contacts values(0, "$phone", "$firstname", "$lastname", "$username", "$user[id]", "$uphone")";
$mysqli->query($sql) or die($mysqli->error);
}
}

Ра­зуме­ется, где‑то в скрип­те до это­го цик­ла ты дол­жен под­клю­чить­ся к БД, но я все‑таки рас­счи­тываю, что минималь­ные навыки прог­рамми­рова­ния на PHP у тебя есть:

$mysqli = new mysqli($dbhost,$dbuser,$dbpass,$db);
if ($mysqli->connect_errno) {
die($mysqli->connect_error);
}

Ког­да ты взгля­нешь на этот код, ста­нет понят­но, зачем мы сох­раняли Telegram ID в спис­ке кон­тактов — по нему мы про­веря­ем, добав­лен такой кон­такт для текуще­го поль­зовате­ля или нет. А поле uid мы добави­ли с одной целью — что­бы наша таб­личка мог­ла хра­нить кон­такты раз­ных акка­унтов: не соз­давать же для это­го отдель­ные таб­лицы (хотя при необ­ходимос­ти тоже мож­но).

Так вот, если для текуще­го поль­зовате­ля кон­такт с задан­ным Telegram ID не сущес­тву­ет в таб­лице contacts, мы выпол­няем SQL-опе­ратор insert для встав­ки кон­такта в таб­лицу.

 

Отправляем сообщения пользователям из контакт-списка

Пер­вым делом хочу пре­дос­теречь: что­бы твой акка­унт не заб­локиро­вали за спам, луч­ше не устра­ивать мас­совую рас­сылку всем сра­зу. Для отправ­ки сооб­щения исполь­зует­ся метод sendMessage, которо­му нуж­но передать два парамет­ра — ID поль­зовате­ля Telegram, которо­му отправ­ляет­ся сооб­щение (обра­ти вни­мание: не номер телефо­на), а так­же само сооб­щение:

$MadelineProto->messages->sendMessage([
'peer' => $user['id'],
'message' => "$firstname, с Новым годом!"
]);

Ес­ли кон­тактов в тво­ем спис­ке не очень мно­го, можешь запус­кать этот код без проб­лем. А вот если кон­тактов хотя бы нес­коль­ко десят­ков, при­думай, как изме­нить алго­ритм, что­бы он отправ­лял сооб­щения не всем сра­зу, а пооче­ред­но — при пер­вом запус­ке пер­вым десяти кон­тактам, при вто­ром — сле­дующим десяти и так далее. Делай переры­вы меж­ду запус­ками кли­ента. В общем, все инди­виду­аль­но: иног­да ты можешь отпра­вить тысячу сооб­щений и тебя не заб­локиру­ют за спам, а можешь отправ­лять с переры­вами, осто­рож­ничать, и твою учет­ку заб­локиру­ют на пятисо­том сооб­щении.

 

Собираем все вместе

А вот пол­ный код кли­ента, получа­юще­го спи­сок кон­тактов и сох­раня­юще­го его в таб­лицу contacts:

<?php
$userDir = '';
$api_id = 11111; // Добавь сюда api_id
$api_hash = 'xxxxxxx'; // Добавь сюда api_hash
$phone = 'твой номер телефона'; // Номер телефона
$dbhost = 'localhost';
$dbuser = 'root';
$dbpass = '';
$db = 'test';
// Подключаемся к БД
$mysqli = new mysqli($dbhost, $dbuser, $dbpass, $db);
if ($mysqli->connect_errno) {
die($mysqli->connect_error);
}
// Загружаем библиотеку
if (!file_exists($userDir . '/madeline.php')) {
copy('https://phar.madelineproto.xyz/madeline.php', 'madeline.php');
}
include_once $userDir . '/madeline.php';
// Формируем объект с параметрами
$settings = (new \danog\MadelineProto\Settings\AppInfo)
->setApiId($api_id)
->setApiHash($api_hash);
use danog\MadelineProto\Tools;
$MadelineProto = new \danog\MadelineProto\API($userDir . "/session", $settings);
// Вход под именем пользователя
$MadelineProto->phoneLogin($phone);
$authorization = $MadelineProto->completePhoneLogin(Tools::readLine('Enter the phone code: '));
if ($authorization['_'] === 'account.needSignup') {
echo "needSignup for $phone";
die();
}
if ($authorization['_'] === 'account.password') {
$authorization = $MadelineProto->complete2falogin(Tools::readLine('Please enter your password (hint '.$authorization['hint'].'): '));
}
// Получаем список контактов
$contacts = $MadelineProto->contacts->getContacts();
$users = $contacts['users'];
foreach ($users as $user) {
$firstname = $user['first_name'] ?? '';
$lastname = $user['last_name'] ?? '';
$username = $user['username'] ?? '';
$uphone = $user['phone'] ?? '';
// Настоятельно рекомендуется перед добавлением в БД:
$firstname = $mysqli->real_escape_string($firstname);
$lastname = $mysqli->real_escape_string($lastname);
$username = $mysqli->real_escape_string($username);
$uphone = $mysqli->real_escape_string($uphone);
echo "$user[id]\t$firstname\t$lastname\t$username\t$uphone\n";
// Проверяем, есть ли контакт в списке
$sql = "select * from contacts where uid = "$phone" and tid = $user[id]";
$result = $mysqli->query($sql) or die($mysqli->error);
if ($result->num_rows == 0) {
// Создаем новый контакт
$sql = "insert into contacts values(0, "$phone", "$firstname", "$lastname", "$username", "$user[id]", "$uphone")";
$mysqli->query($sql) or die($mysqli->error);
}
}

Итак, наш скрипт впол­не работос­пособен, но воз­можнос­тей у него пока еще не слиш­ком мно­го. В сле­дующий раз мы про­дол­жим изу­чение биб­лиоте­ки и рас­смот­рим, как мож­но получить и сох­ранить в базу дан­ных диало­ги (чаты) из Telegram.

Продолжение доступно только участникам

Материалы из последних выпусков становятся доступны по отдельности только через два месяца после публикации. Чтобы продолжить чтение, необходимо стать участником сообщества «Xakep.ru».

Присоединяйся к сообществу «Xakep.ru»!

Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее

  • Подпишись на наc в Telegram!

    Только важные новости и лучшие статьи

    Подписаться

  • Подписаться
    Уведомить о
    0 комментариев
    Межтекстовые Отзывы
    Посмотреть все комментарии