Скрипт для проверки e-mail и сбора ссылок активации (PHP+Human Emulator)
Решил я все таки написать пост про то как мы хуманом собираем почту, раз уж сегодня у меня выдался вынужденный выходной. Скрипт сам написан на PHP и вроде бы как может работать и без хуман эмулятора (не проверял). Сразу хочу сказать – основная идея данного поста описать наш алгоритм проверки почты при многопотоке, а выложенные ниже скрипт – это бонус. Я не гарантирую что он у вас будет работать и не буду отвечать на вопросы типа “Почему у меня не работает?”, я слишком хреновый кодер чтобы разбирать почему кого-то что-то не работает.
Итак, приступим. Начнем с того что всю почту мы шлем на один ящик на своем домене, назовем его [email protected]. Если вы хотите регать аккаунты на гмейлы с точками – просто делаете форвардинг всей почты на ваш ящик на вашем домене. Мы же регаем в основном на свои домены, на рендомные ящики типа [email protected]. Делается это примерно так (на примере панели ISPManger). Идем в раздел “Домены“->”Почтовые домены” и добавляем новый почтовый домен, заполняем поля вот так:
понятное дело, что вместо domain.com – ваш домен.
Далее, “Учетные записи“->”Почтовые ящики” и добавляем новый ящик:
Если хотите слать с двух доменов, например, то добавляете как на первой картинке еще один домен domain2.com, но перенаправляем все равно на [email protected], ну и ящик добавлять не надо, будет у вас с двух доменов идти все в один ящик. Причем ящики можно теперь делать совершенно случайные:
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
Все письма с этих и любых других ящиках окажутся в одном ящике – [email protected].
Скрипт наш мы поделим условно на несколько частей(у нас он является частью большой системы потому буду вырезать куски): входящие данные, функция сбора почты, модуль проверки почты для многопотока, парсинг ссылки активации. Общий алгоритм проверки почты для многопотока будет в модуле проверки почты. Далее в комментариях к коду он будет описан, но чтобы проще было читать код – опишу алго еще раз.
Допустим у нас есть 10 потоков регера какого нибудь блогсервиса, каждый регается на свое мыло в формате логин@domain.com (где логин – уникальное слово для каждого аккаунта). И все сваливается в ящик [email protected]. У нам имеется папка mail_tmp, куда мы складываем все письма, и временный файл mail_lock. Итак, запустили мы скрипт, первый поток доходит до проверки почты, и проверяет наличие нужного файла в папке mail_tmp. Нужный файл – это файл имя которого логин@domain.com.txt, то есть для каждого потока этот файл будет свой.
Так как поток первый – то в папке пусто, поэтому поток создает файл mail_lock, это означает что другие потоки, дойдя до проверки почты, будут стоять на паузе пока файл существует. Далее поток переходит к проверке почты и циклически, указанное количество раз, пытается проверить почту. В почте мы ищем все письма заголовок которых содержит нужные нам данные (ну что то вроде: “Сервис ВасяПупкин и ко приветствует вас”). И все найденные письма сохраняются в папку mail_tmp, каждое под своим именем (имя файла, как я уже говорил – равно почтовому ящику на который регаем аккаунт). Выглядеть папка будет как-то так:
Письма наши удаляются с ящика при скачивании, то есть то письмо которое забираем себе в папку – удаляем. После чего поток завершает работу с почтовым сервером, удаляет файл mail_lock и продолжает свою работу. Второй поток, увидев что файла нету – проверяет папку mail_tmp на наличие своего письма, с большой вероятностью оно уже там будет, если да – то почту он даже не проверяет, а забирает письмо и работает себе дальше. Если же письма нет то создается файл mail_lock и повторяется вышеописанная процедура.
Входящие данные
Сейчас будет огромный кусок кода сразу с комментариями что для чего, код вырезал как есть.
$DEF_xhe_path = "G:\\XWeb\\Human Emulator MT\\"; //путь к уставновленному хуман эмулятору, обязательно с экранированными слешами $DEF_script_path = $DEF_xhe_path."My Scripts\\tramplins v4.0\\"; //путь к скрипту, обязательно с экранированными слешами $DEF_data_path = $DEF_script_path."data\\"; //путь к обновляемым данным для переменных, обязательно с экранированными слешами $DEF_const_data_path = $DEF_data_path."const data\\"; //путь к данным котоыре не удаляются из файла, обязательно с экранированными слешами $DEF_mail_tmp_dir = $DEF_script_path."mail_tmp\\"; //путь к папке куда сохраняем почту $DEF_register_attempts = 3; //количество попыток регистрации с одинаковыми данными $DEF_attempts_fails_to_stop = 7; //количество попыток регистрации с разными данными /* количество попыток регистрации влияет на циклы, то есть пробуем регнуться $DEF_register_attempts раз с одинаковыми данными, если не выходит то меняем данные и пробуем еще $DEF_register_attempts раз, данные меняются $DEF_attempts_fails_to_stop раз данные органичения позволяют не слить весь баланс с антигейта за ночь из-за случайной ошибки в скрипте либо изменений в сервисе */ $DEF_wait_for_mail_recheck = 5; //секунд, время ожидания для перепроверки почты $DEF_mail_recheck_attempts = 12; //количество перепроверок почты $DEF_mail_full_recheck_attempts = 3; //количество циклов проверок почты /* используется при проверке почты, каждые $DEF_wait_for_mail_recheck секунд скрипт пытается проверить почту на наличие нужных писем количество попыток использования равно $DEF_mail_recheck_attempts, при рекомендуемых значениях ($DEF_wait_for_mail_recheck = 5 и $DEF_mail_recheck_attempts = 12) максимальное время ожидания письма составляет минуту, после чего цикл повторяется еще $DEF_mail_full_recheck_attempts раз и в случае неудачи рега поулчает статус фейла и должна пробовать регнуться заново */ $DEF_max_mail_lock_time = 300; //секунд /*максимальное время жизни файла mail_lock, данный файл нужен при многопотоке, когда один из потоков проверяет ящик - создается этот файл если файл существует то остальные потоки ждут вообще логика проверки почты у нас такая: 1. поток №1 хочет проверить почту, видит что файла нет, создает файл, приступает к проверке почты алго проверки почты: - почта проверяется до тех пор пока в папке mail_tmp не появится файл с именем ящика на который должно прийти письмо, либо пока не пройдет время заданное переменными $DEF_wait_for_mail_recheck и $DEF_mail_recheck_attempts - перед первой проверкой ящика ВСЕГДА проверяется наличие в папке mail_tmp нужного файла 2. пока создан файл mail_lock (находится в корневой папке софта) потоки №2, №3...№50 ждут 3. как только поток №1 завершит работу - он удаляет файл mail_lock, и следующий в очереди поток идет в алгоритм проверки почты, зачастую бывает что первый поток уже скачал все нужные письма, поэтому перед проверкой почты кажыдй поток всегда проверяет сначала наличие файла в папке, и если файл есть то проверка почты не запускается для этого потока 4. обычно время проверки ящика у нас равно 1 минуте (задаем в $DEF_wait_for_mail_recheck и $DEF_mail_recheck_attempts), однако бывают случаи когда првоеряющий почту поток вылетел или мы сами его закрыли, тогда остальные потоки будут ждать $DEF_max_mail_lock_time от времени СОЗДАНИЯ файла(по дефолту 5 минут, 300 сек), после чего файл удаляется и следующий в очереди поток начинает алгоритм проверки почты */
Функция сбора почты
Не забываем поменять domain.com на ваш домен.
//поулчаем с ящика ВСЮ почту с указанным сабжектом (тема письма) и создаем по указанному пути файл имя которого равно имени ящика на который было ОТПРАВЛЕНО письмо(обычно это не тот ящик который мы првоеряем) function libGetAllMailWithSubj($host="{mail.domain.com:143/novalidate-cert}INBOX",$login="[email protected]",$pass="qwerty",$path_to_mail_tmp,$subj_str,$txt_decode=0){ imap_timeout(1,120); $mbox = imap_open ($host,$login,$pass); for($i=1;$i<=imap_num_msg($mbox);$i++){ $header = imap_header($mbox,$i,0,200); // sleep(3); // $to_address = $header->toaddress; $arr_to_address = $header->to; $to_address = $arr_to_address[0]->mailbox."@".$arr_to_address[0]->host; $mail_filename = $path_to_mail_tmp.$to_address.".txt"; // $subject = imap_utf8($header->fetchsubject); $subject = $header->fetchsubject; if(!is_file($mail_filename)){ // $mail_text = imap_body($mbox,$i); // перестало рабоать хз почему $mail_text = imap_fetchbody($mbox,$i,1); if($txt_decode==1){ $mail_text = imap_qprint($mail_text); } if($txt_decode==2){ $mail_text = imap_base64($mail_text); } // echo $i.": ".$subject." ~ ".$subj_str." - ".strstr($subject,$subj_str)."<br>"; if(strstr($subject,$subj_str)){ libCreateFile($mail_filename,$mail_text); imap_delete ($mbox, $i); } } } imap_expunge($mbox); imap_close($mbox); }
Модуль проверки почты
Первый кусок кода – это сам модуль, общий алгоритм его работы я описал выше, а сам код писался очнеь давно, потому без камментов
<?php clearstatcache(); if((time()-filemtime("mail_lock")) >$DEF_max_mail_lock_time){ @unlink("mail_lock"); clearstatcache(); } $pro_url_res = 0; $mail_check_fail =0 ; $mail_check_count = 0; sleep(5); do{ clearstatcache(); if(!is_file($DEF_mail_tmp_dir.$user_mail.".txt")){ $mail_read = 0; $mail_lock = 0; $mail_check_num = 0; do{ clearstatcache(); $mlock_file = $DEF_script_path."mail_lock"; $mail_lock = is_file($mlock_file); if($mail_lock == 0){ libCreateFile("mail_lock",$argv[1]); libGetAllMailWithSubj("{".$mail_server.":143/novalidate-cert}INBOX",$mail_login,$mail_pass,$DEF_mail_tmp_dir,$mail_subj_to_check,$mail_subj_encode); unlink($mlock_file); if(is_file($DEF_mail_tmp_dir.$user_mail.".txt")) $mail_read = 1; else{ sleep($DEF_wait_for_mail_recheck); $mail_check_num++; } } else{ sleep($DEF_wait_for_mail_recheck); $mail_check_num++; if(is_file($DEF_mail_tmp_dir.$user_mail.".txt")) $mail_read = 1; } if($mail_check_num>=$DEF_mail_recheck_attempts) $mail_read = 1; }while($mail_read == 0); } else{ $pro_url_res = 1; } $mail_check_count++; if($mail_check_count>$DEF_mail_full_recheck_attempts){ $mail_check_fail = 1; } }while($pro_url_res == 0||$mail_check_fail == 1); ?>
А следующий это уже вызов модуля в регере:
//--------------READING MAIL---------------------- $mail_server = "mail.domain.com"; $mail_login = "[email protected]"; $mail_pass = "qwerty"; $mail_subj_to_check = "=?iso-8859-11?B?ZGVrLWQuY29tIC0gwtS5tNW06c25w9G6ytnoysHSqtShIOC056G01bTN?= =?iso-8859-11?B?t6TNwQ==?="; $mail_subj_encode = 0; //если сабжект зашифрован пробуем вместо 0 подставить1 или 2 include($DEF_script_path."lib\\modules\\read_mail.php"); //сохраняет письмо в файл, в случае фейла выдает переменную $mail_check_fail равную 1 /* если почта проверена успешно - вытягиваем из текста письма урл активации и переходим по нему если же во время проверки почты произошла ошибка то ставим $i_attempts=$DEF_register_attempts, чтобы скрипт больше не пытался регнуться с этими данными */
$mail_subj_to_check – это наш сабжект письма, иногда, когда язык какой то типа испанского – он выглядит вот так, смотреть его нужно в исходнике письма, ну либо в фукнции была строка
echo $i.": ".$subject." ~ ".$subj_str." - ".strstr($subject,$subj_str)."<br>";
Если ее раскомментировать – то окно дебага хумана выдаст сначала реальный сабжект письма, а потом тот что у нас в переменной, и останется только в переменную подставить реальный сабжект. Главное чтобы письмо в ящике во время дебага было одно, а то офигеете от количества инфы.
После отработки модуль выдает переменную $mail_check_fail, равную 0 если все ок и 1 если письма нет. Ну и в конце всей это радости регуляркой вытягиваем урл активации, естественно http://www.blogservis.com/register/ меняем на ваши значения, иногда регулярка нужна чуть посложнее.
if(!$mail_check_fail){ $mail_text = file_get_contents($DEF_mail_tmp_dir.$user_mail.".txt"); preg_match_all('|http://www.blogservis.com/register/(.*?)\n|',$mail_text,$pro_url_arr); $pro_url = "http://www.blogservis.com/register/".$pro_url_arr[1][0]; $browser->navigate($pro_url); $browser->wait_for(90,2); ... }
Конечно кажется на первый взгляд что это огромный сложный код, но на самом деле я написал его несколько лет назад и для большинства сервисов меняю только сабжект письма и регулярку, изредка попадаются более сложные письма, тогда приходится немного править код конечно.
P.S. еще в коде используется функция libCreateFile, вот её исходник
//создаем файл $path со строкой $txt_data function libCreateFile($path,$txt_data){ $file=fopen($path,"w+"); flock ($file,LOCK_EX); fputs($file,$txt_data); fflush ($file); flock ($file,LOCK_UN); // close out file fclose($file); }