понедельник, 8 ноября 2010 г.

Life:)SMS. Нет API? Сделаем!

Являюсь обладателем сим-карты от мобильного оператора life:). И недавно обратил внимание на возможность отправлять смс-ки через их сайт. 
Особенности проекта: 
  • ежедневно 3 бесплатных смс на абонентов life:) для незарегистрированных пользователей.
  • ежедневно 3 бесплатных смс на всех мобильных операторов украины, для абонентов life:)
  • гарантированная доставка смс, senderid - ваш номер мобильного, для абонентов life:)
  • акционные смс-пакеты (100 смс за 12 грн / месяц), остальные тарифицируются по стандартным расценкам. 
Недостатки:
  • отсутствие API для работы с смс-ками.
Пишем класс на PHP под linux эмулирующий работу API для сайта life:)sms.

<?php class lifesms {
   private 
$_urlsms = "https://www.life.com.ua/sms/";
   private 
$_urlbot = "https://www.life.com.ua/sms/antiBot.html";
   private 
$_urlreg = "https://www.life.com.ua/sms/j_security_check";
   private 
$_urlredirect = "";
   private 
$_session = array( );
   private 
$_user = "free";
   private 
$_code = "";
   private 
$_number = "";
   private 
$_pass = "";
   private 
$_auth = false;
   private 
$_free = 0;
   private 
$_debug = false;
   private 
$_error = "";

   private function 
debug( $in ) {
     if( 
$this->_debug ) {
       echo 
"<pre>";
       
print_r( $in );
       echo 
"</pre>";
     }
   }
   public function 
geterror( ) {
     return 
$this->_error;
   }
   public function 
setdebug( $stat ) {
     
$this->debug( "DEBUG ALREADY RUN" );
     
$this->_debug = $stat;
     
$this->debug( "DEBUG RUNNING" );
   }
   private function 
getstream( $in ) {
     
$this->debug( "PARSE RESULT STREAM" );
     foreach( 
$in as $response ) {
       if( 
substr( strtolower( $response ), 0, 10 ) == 'location: ' ) {
         
$this->_urlredirect = substr( $response, 10 );
       }
       if( 
substr( strtolower( $response ), 0, 12 ) == 'set-cookie: ' ) {
         
$pos = strpos( $response, ";" ); if( $pos === false ) $pos = 12;
         
$this->_session[ ] = substr( $response, 12, $pos - 12 );
       }
     }
     
$this->_session = array_unique( $this->_session );
   }
   private function 
setstream( $query = false, $ajax = false ) {
     
$opts = array(
       
'http' => array(
         
'method' => "GET",
         
'header' => array(
           
"Accept: " . $_SERVER['HTTP_ACCEPT'],
           
"Accept-language: " . $_SERVER['HTTP_ACCEPT_LANGUAGE'],
           
"Cookie: " . implode( ";", $this->_session ),
           
"Referer: " . $this->_urlredirect
         
),
         
'user_agent' => $_SERVER['HTTP_USER_AGENT']
       )
     );
     if( 
$query ) {
       
$query = http_build_query( $query );
       
$opts[ 'http' ][ 'method' ] = 'POST';
       
$opts[ 'http' ][ 'header' ][ ] = "Content-type: application/x-www-form-urlencoded; charset=UTF-8";
       
$opts[ 'http' ][ 'header' ][ ] = "Content-Length: " . strlen( $query );
       
$opts[ 'http' ][ 'content' ] = $query;
     }
     if( 
$ajax ) {
       
$opts[ 'http' ][ 'header' ][ ] = "X-Prototype-Version: 1.6.0.1";
       
$opts[ 'http' ][ 'header' ][ ] = "X-Requested-With: XMLHttpRequest";
     }
     
$this->debug( 'CREATE CONTEXT STREAM' );
     
$this->debug( $opts );
     return 
stream_context_create( $opts ); 
   }
   public function 
isready( ) {
     
$file = file_get_contents( $this->_urlsms, false, $this->setstream( ) );
     
$this->getstream( $http_response_header );
     
preg_match( '/id="freeSmsRemain"\>(.*)\</', $file, $tmp );
     
$this->_free = $tmp[ 1 ];
     
$this->debug( $this );
     
$this->debug( $http_response_header );
     if( 
$this->_user == "user" && strpos( $this->_urlredirect, "smsFree" ) !== false ) {
       
$this->debug( 'USER SESSION FAILED' );
       return 
false;
     }
     return 
true;
   }
   public function 
setfree( ) {
     
$this->debug( "SET USER TO FREE" );
     
$this->_user = "free";
     
$this->_session = array( );
     return 
$this->isready( );
   }
   private function 
auth( ) {
     
file_put_contents( "./img.jpg", file_get_contents( $this->_urlbot, false, $this->setstream( ) ) );
     `
convert -compress none img.jpg img.tif`;
     `
tesseract img.tif res nobatch digits`;
     
$arr = file( "res.txt", FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES );
     
$antibot = $arr[ 0 ];
     
$query = array(
       
"j_username" => "{$this->_code}{$this->_number}",
       
"j_prefix" => "{$this->_code}",
       
"j_usernumber" => "{$this->_number}",
       
"j_password" => "{$this->_pass}",
       
"j_antibot" => "{$antibot}",
       
"login" => "Enter"
     
);
     
$file = file_get_contents( $this->_urlreg, false, $this->setstream( $query ) );
     
$this->getstream( $http_response_header );
     
$this->debug( $this );
     
$this->debug( $http_response_header );
     if( 
strpos( $this->_urlredirect, "smsFree" ) !== false ) {
       
$this->_auth = false;
       
preg_match( '/showMessage\(\){(.*)alert\(\"(.*)\"\)(.*)}/smU', $file, $tmp );
       
$this->debug( 'USER AUTH FAILED' );
       
$this->debug( $tmp[ 2 ] );
       
$this->_error = $tmp[ 2 ];
       return 
False;
     }
     
$this->debug( 'USER AUTH OK' );
     
$this->_auth = true;
     return 
True;
   }
   public function 
setuser( $code = "", $number = "", $pass = "" ) {
     
$this->setfree( );
     
$this->_code = $code;
     
$this->_number = $number;
     
$this->_pass = $pass;
     
$this->_user = "user";
     
$this->debug( "SET NEW USER OPTIONS" );
     return 
$this->auth( );
   }
   public function 
sendsms( $code, $number, $text, $signature = "" ) {
     if( 
$this->_user == "free" ) {
       
file_put_contents( "./img.jpg", file_get_contents( $this->_urlbot . "?type=sms", false, $this->setstream( ) ) );
       `
convert -compress none img.jpg img.tif`;
       `
tesseract img.tif res nobatch digits`;
       
$arr = file( "res.txt", FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES );
       
$antibot = $arr[ 0 ];
       
$query = array(
         
"smsNumberPrefix" => "{$code}",
         
"smsNumber" => "{$number}",
         
"promotionTextObj.id" => 0,
         
"text" => "{$text}",
         
"signature" => "{$signature}",
         
"antibot" => "{$antibot}",
         
"save" => "Send SMS"
       
);
       
$file = file_get_contents( $this->_urlsms . "smsFree.html", false, $this->setstream( $query ) );
       
$this->getstream( $http_response_header );
       
//$this->debug( $file );
       
$this->debug( $this );
       
$this->debug( $http_response_header );
       if( 
preg_match( '/main\"(.*)class=\"error\"(.*)\/\>(.*)<br \/\>/smU', $file, $tmp ) ) {
         
$this->_error = $tmp[ 3 ];
       }
       if( 
preg_match( '/infomessages\"(.*)class=\"message\"(.*)5px;\">(.*)<\/span>/smU', $file, $tmp ) ) {
         
$this->_error = $tmp[ 3 ];
       }
       if( 
$this->_error ) {
         
$this->debug( $this->_error );
         return 
False;
       }
       return 
True;
     }
     if( 
$this->_user == "user" && $this->_auth == true ) {
       
$query = array(
         
"text" => "{$text}",
         
"sendDate" => "",
         
"notifyMe" => false,
         
"noCache" => round( microtime( true ) * 1000 )
       );
       
$url = "sms.html?ajax=true&sms.contacts=38{$code}{$number}&sms.contactIds=null&campaignId=";
       
$file = @file_get_contents( $this->_urlsms . $url, false, $this->setstream( $query, true ) );
       
$this->getstream( $http_response_header );
     }
     return 
False;
   }
 }



пример использования:
$a = new lifesms( ); 
$a->setdebug( true ); 
$a->setuser( "код_оператора", "номер_телефона", "супер_пароль" ); 
//$a->setfree( ); 
$a->sendsms( "код_оператора", "номер_телефона", "текст_сообщения" ); 
//echo $a->geterror( );
//$a->isready( ); 


Результаты тестирования.

Скрипт был написан на скорую руку и описывает общую картину работы нашего API с сайтом life:)sms. Без проблем отправляет сообщения в режиме setfree( ) (незарегистрированного пользователя). Главное обрабатывать ответы от сервера, т.к. иногда наш ocr (tasseract) не всегда верно обрабатывает цифры из-за плохого качества captcha-картинки. В режиме setuser отправка смс почти всегда заканчивается ответом false, но эта ошибка связана из-за специфики отправки самого сообщения в режиме зарегистрированного пользователя (функция file_get_contents не корректно обрабатывает длинные url'ы с кучей передаваемых параметров). Возможно ошибка скрывается не в функции, а моей обработке данных. Необходимо это дело дописать и на основании имеющейся API можно прикручивать возможность отправки довольно дешевых смс-сообщений к др.проектам.

2 комментария:

  1. огромный респект!
    правда сразу не сработало, пришлось вникать в код:
    - исправить "./img.jpg" на "img.jpg";
    - chmod 777 на каталог с классом;
    - установить пакет tesseract-ocr;

    P.S. Сколько времени ушло на это добро?

    ОтветитьУдалить
  2. Рад, что кому-то пригодилось. )))

    1. "./img.jpg" - говорит, что файл находится в текущем каталоге.
    2. необходимые права на каталог выставляются согласно настроек сервера.
    3. про tesseract-ocr упоминал в предыдущем посте - http://freetime-at-work.blogspot.com/2010/11/tesseract.html

    Времени на написание наброска-кода немного ушло, а вот найти само свободное время было трудновато )))

    ОтветитьУдалить