Спецификация API для работы с сервером Антиплагиата
Запрос к серверу:
Адрес: http://{xxx.xxx.xxx.xxx}:{порт}/etxt_antiplagiat
POST-параметры для различных действий:
1) Получение текущего состояния сервера:
try=1
2) Постановка пакета в очередь на проверку
xmlUrl={Адрес к xml-пакету с текстами на проверку}
xmlAnswerUrl={Адрес к скрипту, принимающего результаты проверки, должен в конце возвращать "ok" латинскими символами (без кавычек)}
xmlAnswerUrl={Адрес к скрипту, принимающего результаты проверки, должен в конце возвращать "ok" латинскими символами (без кавычек)}
Примечание:
- пакет успешно поставлен в очередь на проверку, если код ответа сервера Code=1
- если скрипт, принимающий результаты проверки не будет возвращать в конце "ok", то сервер антиплагиата будет вновь и вновь повторять попытки отправки результатов проверки
Ответ сервера (в JSON формате):
Code - Возвращает код ответа сервера:
0 - Неизвестная ошибка
1 - Задача на пакетную обработку принята
3 - Неверно указан (или вообще не указан) адрес к xml-файлу с пакетом документов для обработки
4 - Неверно указан адрес при запросе к серверу (должен быть в форме http://{xxx.xxx.xxx.xxx}:{порт}/etxt_antiplagiat)
5 - Неверно указан (или вообще не указан) адрес к скрипту, принимающего результаты обработки проверки
6 - Запрос на состояние сервера успешно обработан
7 - Отсутствует доступ к Интернет
8 - Принята команда на остановку сервера
9 - Запрос на получение трассировочной информации с сервера успешно обработан
10 - Запрос отклонен из-за невозможности проверить доступ клиента к сервису. Можно повторить попытку немного позже
11 - Запрос отклонен из-за отсутствия доступа к сервису (скорей всего из-за нулевого баланса)
12 - Запрос отклонен из-за превышения макс-го кол-ва пакетов в очереди (50 по умолчанию)
13 - Запрос отклонен из-за истечения срока тестового доступа
Description - Возвращает текстовое описание ответа сервера
NumPacketsInQueue - Возвращает текущее число пакетов в очереди
AvgDocumentTime - Возвращает среднее время обработки документа в пакете, в минутах. Будет меньше нуля, если пока эту характеристику невозможно вычислить
CurrentPacketTime - Текущее время обработки текущего пакета, в минутах (целое число).
Пример ответа:
{"Code":3,"Description":"В запросе отсутствует адрес с пакетом данных для обработки","NumPacketsInQueue":2,"AvgDocumentTime":-1.000000E+000, "CurrentPacketTime":0}
Получение результата:
Результат получает скрипт, адрес которого был указан в POST-параметре при запросе к серверу. Ему будут доступны следующие POST-поля:
XmlFileName - Имя файла Xml-пакета с документами
NumDocsInPacket - Число документов в пакете
PacketTime - Время обработки пакета, в минутах
DocumentTime - Среднее время обработки документа в пакете, в минутах
ServerType - Параметр, полученный от клиента в Xml-пакете при запросе
TotalWords - Общее число слов всех документов из пакета после канонизации
Xml - Результат проверки Xml-пакета, зашифрованный, а затем и кодированный в Base64
Error - Содержит ошибку, если не удалось проверить Xml-пакет (в этом случае все строковые POST-поля будут пустыми (кроме XmlFileName), а целочисленные - нулевые). Закодирован в Base64
Формат Xml-пакета (ЗАПРОС)
Примечания:
- символами [] обозначается опциональность присутствия узлов/атрибутов
<?xml version="1.0" encoding="UTF-8"?>
<root>
<serverType>{Строка}</serverType>
[<exceptions>
<url>{Url, игнорируемое при проверке на уникальность каждого документа из пакета, закодированный в Base64}</url>
...
<url>...</url>
<domain>{домен, игнорируемый при проверке на уникальность каждого документа из пакета, закодированный в Base64}</domain>
...
<domain>...</domain>
</exceptions>]
<entry>
<id>{Число}</id>
<type>{Строка}</type>
<uservars>{Любые пользовательские данные в Xml формате}</uservars>
<name>{Имя текста, закодированное в Base64}</name>
[<text>{Текст на проверку, закодированный в Base64}</text>]
[<exceptions>
<url>{Url, игнорируемое при проверке на уникальность, закодированный в Base64}</url>
...
<url>...</url>
<domain>{домен, игнорируемый при проверке на уникальность, закодированный в Base64}</domain>
...
<domain>...</domain>
</exceptions>]
</entry>
<entry>
...
</entry>
</root>
Формат Xml-пакета (ОТВЕТ)
Примечания:
- символами [...] обозначается опциональность присутствия узлов/атрибутов
- если уникальность проверенного текста = 100, то поле ftext отсутствует
- в полях url возвращаются (максимум 5) url страниц с наибольшим процентом совпадений с проверяемым текстом
<?xml version="1.0" encoding="UTF-8"?>
<root>
<numPacketsInQueue>{текущее число пакетов в очереди}</numPacketsInQueue>
[<exceptions>
<url>{Url, игнорировавшийся при проверке на уникальность каждого документа из пакета, закодированный в Base64}</url>
...
<url>...</url>
<domain>{домен, игнорировавшийся при проверке на уникальность каждого документа из пакета, закодированный в Base64}</domain>
...
<domain>...</domain>
</exceptions>]
<entry>
<id>{Число}</id>
<type>{Строка}</type>
<uservars>{Любые пользовательские данные в Xml формате}</uservars>
<name>{Имя текста, закодированное в Base64}</name>
[<ftext uniq={Уникальность текста(целое число)}>
{Проверенный текст с выделенными совпашими шинглами (в текстовом виде или картинке jpeg), закодированный в Base64}
</ftext>]
[<urls>
<url unique={уникальность проверяемого текста по отношению к содержимому данного url (целое число, от 0 до 100)}
color={цвет выделения совпавших шинглов в ftext, в виде #xxxxxx}
[title={содержимое тэга title страницы с данным Url, закодированное в Base64}]>
{Url, по которому нашлось совпадение с проверяемым текстом, закодированное в Base64}
</url>
<url ...> ...</url>
...
</urls>]
<statistics>
<download_ratio>{Отношение общего кол-ва скачанных страниц ко всем страницам (в процентах)}</download_ratio>
<total_pages>{Общее кол-во страниц, которое планировалось закачать}</total_pages>
</statistics>
[<exceptions>
<url>{Url, игнорировавшийся при проверке на уникальность, закодированный в Base64}</url>
...
<url>...</url>
<domain>{домен, игнорировавшийся при проверке на уникальность, закодированный в Base64}</domain>
...
<domain>...</domain>
</exceptions>]
</entry>
<entry>
...
</entry>
...
</root>
PHP-класс для общения с сервером Антиплагиата
<?
define('CHECK_KEY', 'ВАШ КЛЮЧ');
class EtxtAntiPlagiat
{
// путь до сервера антиплагиата
private $serverUrl = array (
'server' => 'xxx.xxx.xxx.xxx:yyy/etxt_antiplagiat',
);
// тип сервера по умолчанию
public $serverType = 'server';
// путь до веб части проверки
private $localServer = 'http://www.site.ru/';
// путь для получения результата
public $localUrl = 'http://www.site.ru/antiplagiat/upload.php';
// массив объектов на проверку
private $ItemsToCheck = array();
// папка для xml заданий
private $xmlPath = '/home/user/site.ru/htdocs/antiplagiat/xml/';
// типы объектов для проверки
private $typesObjects = array ('text');
// ключ использования шифра
public $useCrypt = 1;
// флаг соединения с сервером антиплагиата
public $isConnect = false;
// статус ошибки
public $Error = '';
// флаг-значение приоритета пакета
public $sort = 0;
// конструктор, параметр - имя xml файла
function EtxtAntiPlagiat($path = '', $my_crypt = 1, $serverType = 'server')
{
if ($path) $this->xmlPath .= $path;
$this->useCrypt = $my_crypt;
if ($this->useCrypt == 1) $this->useCrypt = CHECK_KEY;
$this->serverType = $serverType;
// пингуем сервер
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $this->serverUrl[$this->serverType]);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, "try=1");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_TIMEOUT, 5);
$json = curl_exec($ch);
curl_close($ch);
// устанавливаем результат пинга
$this->isConnect = $json ? $json : false;
$this->isConnect = str_replace('\,', ',', $this->isConnect);
if (($tmp = json_decode($this->isConnect)) && $tmp->Code == 7) {
$this->isConnect = false;
$this->Error = $tmp->Description;
}
}
// функция добавления объекта на проверку
public function addItemToCheck($data)
{
if (!$data['id'] || !in_array($data['type'], $this->typesObjects)) return false;
$this->ItemsToCheck[] = array ('id' => $data['id'], 'text' => (isset($data['text']) ? $this->codeText($data['text']) : ''), 'type' => $data['type'], 'name' => $this->codeText($data['name']), 'uservars' => isset($data['uservars']) ? $data['uservars'] : array());
return true;
}
// функция кодирования текста
private function codeText($text)
{
return base64_encode(@iconv('WINDOWS-1251', 'UTF-8//IGNORE', $text));
}
// функция выполнения запроса на проверку
public function execRequest($create = 1)
{
// пытаемся создать xml файл с заданиями
if ($create && !$this->createXml()) return false;
// отправляем запрос на сервер антиплагиата
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $this->serverUrl[$this->serverType]);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, "xmlUrl=".$this->localServer.'antiplagiat/xml/'.basename($this->xmlPath)."&xmlAnswerUrl=".$this->localUrl.($this->sort ? '&sort='.$this->sort : ''));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_TIMEOUT, 5);
$json = curl_exec($ch);
curl_close($ch);
return str_replace('\,', ',', $json);
}
// функция построения xml файла заданий
private function createXml()
{
$string = '<?xml version="1.0" encoding="UTF-8" ?'.'><root>';
$string .= '<serverType>'.$this->serverType.'</serverType>';
foreach ($this->ItemsToCheck as $item) {
$string .= '
<entry>
<id>'.$item['id'].'</id>
<type>'.$item['type'].'</type>';
if (isset($item['uservars']) && $item['uservars'] && is_array($item['uservars'])) {
$string .= '
<uservars>';
foreach ($item['uservars'] as $key => $uservar)
$string .= '
<'.$key.'>'.$uservar.'</'.$key.'>';
$string .= '
</uservars>';
}
$string .='
<name>'.$item['name'].'</name>'.
(isset($item['text']) && $item['text'] ? '<text>'.$item['text'].'</text>' : '').'
</entry>';
}
$string .= '</root>';
if (is_file($this->xmlPath)) unset($this->xmlPath);
// шифруем данные
if ($this->useCrypt) {
$td = mcrypt_module_open (MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_ECB, '');
mcrypt_generic_init ($td, $this->useCrypt, $this->useCrypt);
$string = mcrypt_generic ($td, $string);
mcrypt_generic_deinit ($td);
mcrypt_module_close ($td);
}
// сохраняем файл
$f = @fopen($this->xmlPath, 'w') or die("Failed open file # ".$this->xmlPath." # on write");
fwrite($f, $string, strlen($string));
fclose($f);
if (is_file($this->xmlPath)) return true;
return false;
}
}
?>
Постановка задания на проверку
// создаем объект для формирования запроса на проверку
$etxtPlagiat = new EtxtAntiPlagiat(md5(time()).'.xml', 1, 'server');
// доступность сервера
if (!$etxtPlagiat->isConnect || !($tmp = json_decode($etxtPlagiat->isConnect))) {
logging('*** Ошибка соединения с сервером Антиплагиата');
exit();
}
// выбираем объекты на проверку
$items = SELECT * FROM table
// добавляем все объекты на проверку
foreach ($items as $item) {
// добавляем объект на проверку
$etxtPlagiat->addItemToCheck(array('id' => $item['id'], 'text' => $item['text'], 'type' => $item['type'], 'name' => $item['title'], 'uservars' => $item['usersvars']));
}
// посылаем запрос серверу на проверку
if ($data = $etxtPlagiat->execRequest()) {
if ($data = json_decode($data)) {
logging(iconv('UTF-8', 'WINDOWS-1251//IGNORE', $data->Description));
exit();
}
}
Получение ответа от сервера
// проверка данных в ответе
if (!isset($_POST['Xml']) || !$_POST['Xml']) {
logging('Данные в ответе сервера не найдены');
exit();
}
$_POST['Xml'] = str_replace(' ', '+', $_POST['Xml']);
// расшифровываем данные
$td = mcrypt_module_open (MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_ECB, '');
mcrypt_generic_init ($td, CHECK_KEY, CHECK_KEY);
$_POST['Xml'] = mdecrypt_generic ($td, base64_decode($_POST['Xml']));
// устанавливаем перехват ошибок
libxml_use_internal_errors(true);
// пытаемся преобразовать xml в объект
if ($xml = simplexml_load_string($_POST['Xml'])) {
foreach ($xml->entry as $item) {
...
}
}