На сайте telegram я наткнулся на ссылку на неофициальный opensource php клиент. Да-да! Можно использовать telegram под php, и там даже есть поддержка звонков! Это чудо называется madelineProto. Оно может подключаться к серверам используя криптографическую магию и отдавать нужные мне данные в виде нормального, человеческого ассоциативного массива.
1. Регистрация своего клиента.
Нам всеже потребуется регистрация и смс авторизация в телеграме…
Если аккаунт в телеграме уже есть, остается зарегистрировать свое приложение/клиент, и получить ключи для доступа к серверам telegram.
Это стандартная процедура, похожая на аналогичную у соц. сетей для доступа к API. Инструкция для создания своих ключей.
После регистрации клиента нам потребуются только «App api_id» и «App api_hash» со страницы my.telegram.org/apps
2. Установка madelineProto.
Для работы требуется php7, но в Readme написано, что есть способ запустить на php5.6.
С запуском на MacOs с php7 из пакета Mamp, и простеньком хостинге за 150 руб в мес проблем не возникло.
Процесс не хитрый: скачать релиз, установить зависимости через composer и можно приступать к настройке.
Для уменьшения размера я удалил лишние зависимости и оставил только danog, paragonie и phpseclib. На работе клиента это никак не сказалось.
3. Настройка madelineProto и первый запуск.
Все примеры по использованию и настройке описаны в репозитории клиента, но я приведу свой код с комментариями.
На этом этапе потребуется авторизовать новое подключение и ввести код верификации, который придет в ранее авторизованный telegram клиент. Запускать код желательно из консоли (но есть и веб режим). Если настраиваете клиент для другого человека, то нельзя пересылать код авторизации через telegram, иначе он будет аннулирован. Но можно пересылать его в виде скриншота или писать текстом.
Количество авторизаций, которые можно запросить, ограничено. Если что-то не срабатывает — не стоит много раз подряд запускать код, иначе Телеграм заблокирует отправку подтверждений на сутки или более.
Я, к сожалению, узнал об этой особенности на своей шкуре. Обычной тех. поддержки у телеграма, кстати, тоже нет, так что в случае блокировки придется ждать 🙂
//мой список настроек минимален, остальные - по умолчанию. Cписок всех значений по умолчанию можно посмотреть в ReadMe клиента на github.
$settings = [
'app_info' => [ // Эти данные мы получили после регистрации приложения на https://my.telegram.org
'api_id' => XXXXX,
'api_hash' => XXXXXXXXXX,
],
'logger' => [ // Вывод сообщений и ошибок
'logger' => 3, // выводим сообещения через echo
'logger_level' => 4, // выводим только критические ошибки.
],
//для доступа может потребоваться socks5 прокси
//если прокси не требуется, то этот блок можно удалить.
'connection_settings' => [
'all' => [
'proxy' => '\SocksProxy',
'proxy_extra' => [
'address' => 'xxx.xxx.xxx.xxx',
'port' => 1234,
'username' => '',//Можно удалить если логина нет
'password' => '',//Можно удалить если пароля нет
],
],
],
'serialization' => [
'serialization_interval' => 300,
//Очищать файл сессии от некритичных данных.
//Значительно снижает потребление памяти при интенсивном использовании, но может вызывать проблемы
'cleanup_before_serialization' => true,
],
];
$MadelineProto = new \danog\MadelineProto\API('session.madeline', $settings);
$MadelineProto->start();
В корне проекта будут создан файл «session.madeline», в котором, в бинарном виде, будут храниться данные нашей сессии. При повторном запуске авторизовываться заново не придется. Возобновление сессии работает довольно быстро. У меня на инициализацию клиента уходит около 800 мс на зарубежном сервере.
Для обновления настроек достаточно обновить массив и перезапустить скрипт. Удалять файл сессии не требуется.
4. Получение постов из произвольного открытого telegram канала.
После того как
$data = array(
'peer' => '@'.$val['url'], //название_канала, должно начинаться с @, например @breakingmash, все остальные параметры, кроме limit, можно оставить равными 0
'offset_id' => $val['offset_id']?:0,
'offset_date' => $val['offset_date']?:0,
'add_offset' => $val['add_offset']?:0,
'limit' => $val['limit']?:10, //Количество постов, которые вернет клиент
'max_id' => $val['max_id']?:0, //Максимальный id поста
'min_id' => $val['min_id']?:0, //Минимальный id поста - использую для пагинации, при 0 возвращаются последние посты.
'hash' => 0
);
$response = $MadelineProto->messages->getHistory($data);
Так как у меня обновляется много каналов за один раз, то имеет смысл использовать одну и туже сессию, а не тратить по 2 секунды на каждый канал.
После выполнения мы получаем массив с нужным нам количеством сообщений/постов, разбитых по каналам. Так же передаются данные о медиа-вложениях.
Дальше остается сохранить текст поста, при наличии фото/видео получить превью и подпись к медиа файлу и сформировать ссылку для просмотра поста.
4. Получение медиа-вложений.
К счастью, с недавнего времени, telegram внедрил html превью постов, поэтому можно не сохранять на свой сервер бинарные данные, полученные из клиента, а просто взять ссылку на фото и видео, хранящееся на серверах телеграма.
По названию канала и id поста формируем ссылку формата: t.me/НАЗВАНИЕ_КАНАЛА/ID_ПОСТА?embed=1, например t.me/breakingmash/4193?embed=1
Ну а дальше все просто:
private function telegram_media_parse($posts_data, $source){
include_once(ROOT_DIR.'/libs/phpQuery.php'); //для парсинга html использую очень быструю и удобную библиотеку phpQuery
foreach ($posts_data as &$post_data) {
if (!empty($post_data['media'])){
$file_contents = self::loader($post_data['post_url'],'site');//Через curl получаем html код страницы поста.
$document = phpQuery::newDocumentHTML($file_contents); //Формируем dom-дерево из html строки
$post_data['post_image'] = preg_replace('/[\s\S]*background-image:[ ]*url\(["\']*([\s\S]*[^"\'])["\']*\)[\s\S]*/u','$1',$document->find($source['rules']['post_img_path'])->eq(0)->attr('style')); //адрес изображения хранится в background-image свойстве.
$post_data['post_description'] = $document->find($source['rules']['post_text_path'])->eq(0)->text(); //Получаем caption медиафайла.
}
unset($post_data['media']);
}
unset($post_data);
return $posts_data;
}
На этом парсинг закончен и можно сохранять посты в базу или вывести на странице.
Надеюсь, что мой первый пост будет кому нибудь полезен. Ссылку на свой агрегатор не оставляю, так как не уверен, разрешено ли это.