TrueCaller — это сервис по определению имени абонента при входящих звонках, а также блокировка спама. На смартфонах с CyanogenOS 12.1 он вшит в штатную звонилку. Также можете установить себе TrueDialler/TrueCaller с GooglePlay/AppStore/BlackBerryWorld/WindowsPhoneStore.

Если вы активировали данный функционал в вашем смартфоне, то ваша книга контактов полностью слита на сервера Truecaller'а? Проверить, есть ли ваш номер в базе можно по ссылке, например: https://www.truecaller.com/ru/74996813210 (необходима аутентификация).
 
На данный момент сервис насчитывает 1.6 миллиарда номеров по всему миру. Выписать свой номер из базы возможно по ссылке https://www.truecaller.com/unlist.

Прикручиваем Truecaller к Asterisk'у


На сайте Truecaller'а есть возможность определить имя абонента по номеру телефона. Вход на сайт возможен только через сторонние сервисы и социальные сети. Для аутентификации я выбрал Вконтакте (протокол Oauth).

1. Вручную войти на сайт truecaller.com, используя ранее зарегистрированную учетную запись вконтакте, разрешить доступ.
2. Необходимо создать внутреннюю БД для хранения уже однажды звонивших контактов. Это необходимо чтобы каждый раз не обращаться к сервису truecaller'а.
3. Написать скрипт прохождения аутентификации на сайте truecaller.com через сеть вконтакте, а также функцию для проверки номеров на наличие имени абонента.
 
Скрипт написан на PHP для удобства внедрения под AGI и общей читаемости.

Создадим БД в MySQL:

USE asterisk;

CREATE TABLE asterisk.phonebook (
  id int(11) NOT NULL AUTO_INCREMENT,
  create_date timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  number varchar(20) NOT NULL,
  name varchar(80) NOT NULL,
  temporary_contact tinyint(1) NOT NULL DEFAULT 1,
  PRIMARY KEY (id)
)
ENGINE = INNODB
AUTO_INCREMENT = 9
AVG_ROW_LENGTH = 8192
CHARACTER SET utf8
COLLATE utf8_general_ci;

PHP скрипт /var/lib/asterisk/agi-bin/phonebook.php (для тех кто делает через PHP-AGI, не забудьте раскомментировать соответствующие строчки, результат вы получите в переменную канала CID_NAME):

#!/usr/bin/php -q
<?php	
$error_level = error_reporting(0);
set_time_limit(30);

//require('phpagi.php');
//$agi = new AGI();

if (isset($argv[1])) {$num=$argv[1];} else {$num=NULL;}
$cookie_file='/tmp/asterisk_truecaller_vk.cookie';
$vk = array("login"=> "логин_вконтакте", "password"=> "пароль_вконтакте");
$mysql = array("hostname" => "localhost", "login"=> "root", "password"=> "пароль_mysql", "database"=> "asterisk");
	
if (!is_null($num))
{
	$callerid_name=get_num($num,$vk,$mysql,$cookie_file,true);
	return $callerid_name;
}
else
{
	echo "Номер телефона не задан\n";
	//$agi->set_variable("CID_NAME", "");
	return false;
}

// поиск номера на сервисе truecaller
function get_num($num,$vk,$mysql,$cookie_file,$isauth)
{
	// ищем в своей базе
	mysql_connect($mysql['hostname'],$mysql['login'],$mysql['password']); 
	mysql_select_db($mysql['database']) or die(mysql_error());
	mysql_query("SET NAMES 'utf8'"); mysql_query("SET CHARACTER SET 'utf8'"); mysql_query("SET SESSION collation_connection = 'utf8_general_ci'");
	
	$query = "SELECT * FROM phonebook WHERE `number`=$num";
	$res = mysql_query($query);
	$count = mysql_num_rows($res);
	if ($count>0)
	while ($row=mysql_fetch_array($res)) 
	{ 
		$name=$row['name'];
		echo "Найден контакт в MySQL '".$name."'\n";
		//$agi->set_variable("CID_NAME", "$name");
		return $name;
	}
	mysql_close();
	
	// ищем в truecaller
	if ($isauth)
	{
		$ch = curl_init();
		curl_setopt($ch, CURLOPT_URL, 'http://www.truecaller.com/ru/'.$num ); 
		curl_setopt($ch, CURLOPT_HEADER, 1);
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
		curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
		curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 3);
		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
		curl_setopt($ch, CURLOPT_COOKIEJAR, $cookie_file);
		curl_setopt($ch, CURLOPT_COOKIEFILE, $cookie_file);	
		$data = curl_exec($ch);
		curl_close($ch);	
		
		if (preg_match("/You need to sign in to view the result/i", $data)) {
		    echo "Необходима аутентификация TC\n";
		  	$isauth = oauth_vk($vk, $cookie_file);
		  	get_num($num,$vk,$mysql,$cookie_file,$isauth);
		  	
		} else {
		    preg_match("/<div class=\"detailView__nameText\">\n\s*(.+)\s\n\s*<\/div>/i", $data, $matches); 
		    if (count($matches)>0) 
		    {
			    $name=$matches[1]; 
			    echo "Найден контакт в TC '".$name."'\n";
			    
				mysql_connect($mysql['hostname'],$mysql['login'],$mysql['password']); 
				mysql_select_db($mysql['database']) or die(mysql_error());
				mysql_query("SET NAMES 'utf8'"); mysql_query("SET CHARACTER SET 'utf8'"); mysql_query("SET SESSION collation_connection = 'utf8_general_ci'");
				$query = "INSERT INTO phonebook (`name`,`number`) VALUE ('".$name."','".$num."')";
				$res = mysql_query($query);
				mysql_close();			    
			    
			    //$agi->set_variable("CID_NAME", "$name");
			    return $name;
		    } else {
		    	echo "Совпадения в TC не найдены\n";
		    	//$agi->set_variable("CID_NAME", "");
		    	return false;
		    }
		}	
	}
	else
	{
		echo "Аутентификация TC не была пройдена, попробуйте в следующий раз\n";
		//$agi->set_variable("CID_NAME", "");
		return false;
	}
}

// авторизация через кнопку "зайти через вконтакте"
function oauth_vk($vk, $cookie_file)
{
	unlink($cookie_file);
	
	$ch = curl_init();
	curl_setopt($ch, CURLOPT_URL, 'https://oauth.vk.com/authorize?client_id=4951501&scope=friends%2Coffline&redirect_uri=http%3A%2F%2Fwww.truecaller.com%2Fsign-in%2Fvk&response_type=code&state=KKoLuT0vbWEOXfqIW9C0yAvoX7uoEDszIrVOxYSr');
	curl_setopt($ch, CURLOPT_HEADER, 1);
	curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
	curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
	curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 3);
	curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
	curl_setopt($ch, CURLOPT_COOKIEJAR, $cookie_file); // сохранять куки в файл 
	curl_setopt($ch, CURLOPT_COOKIEFILE, $cookie_file);	
	$data = curl_exec($ch);
	curl_close($ch);
	
	preg_match("/<form method=\"post\" action=\"(.+)\">/i", $data, $matches); if (count($matches)>0) $action=$matches[1]; 
	preg_match("/<input type=\"hidden\" name=\"_origin\" value=\"(.+)\">/i", $data, $matches); if (count($matches)>0) $origin=$matches[1];
	preg_match("/<input type=\"hidden\" name=\"ip_h\" value=\"(.+)\" \/>/i", $data, $matches); if (count($matches)>0) $ip_h=$matches[1];
	preg_match("/<input type=\"hidden\" name=\"lg_h\" value=\"(.+)\" \/>/i", $data, $matches); if (count($matches)>0) $lg_h=$matches[1];
	preg_match("/<input type=\"hidden\" name=\"to\" value=\"(.+)\">/i", $data, $matches); if (count($matches)>0) $to=$matches[1];

	if (isset($action) && isset($origin) && isset($ip_h) && isset($lg_h) && isset($to))
	{	
		$ch = curl_init();
		curl_setopt($ch, CURLOPT_URL, $action );
		curl_setopt($ch, CURLOPT_HEADER, 1);
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
		curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
		curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 3);
		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
		curl_setopt($ch, CURLOPT_COOKIEJAR, $cookie_file);
		curl_setopt($ch, CURLOPT_COOKIEFILE, $cookie_file);
		curl_setopt($ch, CURLOPT_POST, 1);
		curl_setopt($ch, CURLOPT_POSTFIELDS, array(
		    '_origin'=>$origin,
		    'ip_h'=>$ip_h,
		    'lg_h'=>$lg_h,
		    'to'=>$to,
		    'email'=>$vk['login'],
		    'pass'=>$vk['password']
		));	
		$data = curl_exec($ch);
		curl_close($ch);
		
		preg_match('/Location: (http\:\/\/www\.truecaller\.com\/sign\-in\/vk\?code\=.+)\&state.+/', $data, $matches); if (count($matches)>0) $location=$matches[1];
		
		if (isset($location))
		{
			$ch = curl_init();
			curl_setopt($ch, CURLOPT_URL, $location);
			curl_setopt($ch, CURLOPT_HEADER, 1);
			curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
			curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
			curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 3);
			curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
			curl_setopt($ch, CURLOPT_COOKIEJAR, $cookie_file);
			curl_setopt($ch, CURLOPT_COOKIEFILE, $cookie_file);	
			$data = curl_exec($ch);
			curl_close($ch);
			
			if (preg_match("/\<title\>Signed In \| Truecaller\<\/title\>/i", $data)) {
			    echo "Аутентификация VK пройдена успешно\n";
			  	return true;
			} else {
			    echo "Ошибка при прохождении авторизации через VK / не найдена строка о успешной авторизации\n";
			    return false;
		    }	
		} else {
		    echo "Ошибка при прохождении авторизации через VK / переменная location не получена\n";
		    return false;
		}
	} else {
	    echo "Ошибка при прохождении авторизации через VK / не все переменные получены action='".$action."', origin='".$origin."', ip_h='".$ip_h."', lg_h='".$lg_h."', to='".$to."'\n";
	    return false;
	}		
}

?>

У меня диалплан на LUA, поэтому в extensions.lua:

local call = {}
call.cid_num = channel["CALLERID(num)"]:get()
call.cid_name = ""

-- ищем телефонный номер в базе
local handle = io.popen("/var/lib/asterisk/agi-bin/phonebook.php "..call.cid_num)
local founded_name = handle:read("*a")
handle:close()
app.Noop(founded_name)
_, _, call.cid_name = string.find(founded_name,"Найден%sконтакт%sв.+%s'(.+)'")
channel["CALLERID(name)"]:set(call.cid_name)

В данном скрипте нет учета блокировки спам контактов. Данная статья описана как «обзорная» по возможности интеграции столь чудесного сервиса Truecaller с вашей PBX.

Комментарии (10)


  1. webportal
    14.09.2015 15:09

    У них же апи есть. Чем не устроило? Лимиты есть через апи и через ваш вариант?


    1. alex_bosenko
      14.09.2015 15:24

      API есть, но похоже в закрытом варианте. Нет описания где и как получать API_KEY для доступа. www.truecaller.com/developer — страница не найдена.

      На ТехКранче от 2013 года были озвучено о открытии API с ссылкой на цены. В данный момент данная ссылка ценами тоже мертва.


      1. webportal
        15.09.2015 16:52

        https://dev.truecaller.com/docs да точно https://dev.truecaller.com/login есть логин нет реги.


    1. alex_bosenko
      14.09.2015 15:29

      По лимитам запросов ничего сказать не могу, т.к. нагрузочное тестирование не проводилось. Но надо понимать, что слишком частое обращение рано или поздно неизбежно приведет к блокировке. Поэтому и сделана прослойка в виде таблицы MySQL для сохранения имен ранее звонивших абонентов.


  1. CANMOS
    14.09.2015 15:17

    Благодарю, будем ждать развития темы в следующих статьях.


  1. realscorp
    15.09.2015 08:45

    Ух ты, не знал об сервисе Truecaller. Спасибо за информацию, будет очень полезно иногда определять, кто звонил.


  1. Truecaller
    21.09.2015 17:59

    Привет! Мы рады оказаться на Хабре.
    alex_bosenko, мы заметили ваш пост и хотели бы немного подробнее рассказать как же все-таки работает Truecaller.
    Truecaller — это приложение полностью основанное на разрешениях от пользователей, это означает, что на каждом шаге взаимодействия с приложением пользователь получает информацию о том, какие разрешения ему необходимо дать приложению для получения полного функционала.

    Приложение, скаченное с Google Play/Apple App Store не загружает телефонную книгу и тем более не делает номера публично доступными.

    Мы следуем правилам и принципам всех магазинов приложений.

    image

    Доступ к телефонной книге пользователя на Android/iPhone необходим только для построения социального графа, который нужен для использования функции “Найти”. Пользователи не могут получить доступ к номеру другого пользователя Truecaller до тех пор, пока пользователь сам не выдаст такого разрешения (с лимитом до 2го уровня связей). По умолчанию, не публичные номера не отображаются и вместо номера вы увидите пометку “номер доступен по запросу’. Это значит, что для получения номера необходимо отправить запрос пользователю, при этом вы всегда можете отправить отказ или не использовать данную фунцкию.

    Для устройств с CyanogenOS работа Truecaller не является обязательной — вы можете согласиться или отказаться от использования во всплывающем окне, а также в настройках.

    image image image

    Для других OS, в том числе Windows Phone и BlackBerry, пользователей спрашивают готовы ли они помочь сообществу Truecaller стать наиболее полным и аккуратным в определении номеров, которые уже известны в их телефоне. Функция называется “Расширенный поиск” и она отключена по умолчанию. Пользователи, которые включили данную функцию могут отключить ее в любой момент в настройках аккаунта (что удалит все контакты, полученные от пользователя, в нашей системе).

    У Truecaller более 150 миллионов пользователей по всему миру и ежедневно мы помогаем пользователям понять кто же им все-таки звонит. Наши пользователи сами выбирают путь помощи друг другу и тем самым, экономят время и нервы при звонках с незнакомых номеров или от спама.

    Для тех, кто котел бы удалить свой номер из нашей базы, вы можете сделать это через наш вебсайт в любое время (www.truecaller.com).
    Для тех, кто желает знать кому принадлежит пропущенный вызов, определять входящие вызовы или блокировать спам звонки, рекомендуем ссылку


    1. alex_bosenko
      21.09.2015 20:31

      Спасибо за разъяснения. Хотелось бы узнать о возможности подключения к API для возможности полноценной интеграции вашего сервиса. На Вашем сайте я не нашел информации.


    1. dannyzubarev
      21.09.2015 22:10

      Присоединюсь к alex_bosenko в вопросе доступа к API. Спасибо.


      1. Truecaller
        22.09.2015 13:41

        Спасибо за вопрос и внимание к нашему API, к сожалению, API больше не доступен.