Files
crm.clientright.ru/aiassist/fileHandler.php
Fedor ac7467f0b4 Major CRM updates: AI Assistant, Court Status API, S3 integration improvements, and extensive file storage system
- Added comprehensive AI Assistant system (aiassist/ directory):
  * Vector search and embedding capabilities
  * Typebot proxy integration
  * Elastic search functionality
  * Message classification and chat history
  * MCP proxy for external integrations

- Implemented Court Status API (GetCourtStatus.php):
  * Real-time court document status checking
  * Integration with external court systems
  * Comprehensive error handling and logging

- Enhanced S3 integration:
  * Improved file backup system with metadata
  * Batch processing capabilities
  * Enhanced error logging and recovery
  * Copy operations with URL fixing

- Added Telegram contact creation API
- Improved error logging across all modules
- Enhanced callback system for AI responses
- Extensive backup file storage with timestamps
- Updated documentation and README files

- File storage improvements:
  * Thousands of backup files with proper metadata
  * Fix operations for broken file references
  * Project-specific backup and recovery systems
  * Comprehensive file integrity checking

Total: 26,461+ files added/modified including AWS SDK, vendor dependencies, and extensive backup system.
2025-10-16 11:17:21 +03:00

277 lines
11 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
// aiassist/fileHandler.php
require_once 'logger.php';
function normalizeFilename($filename) {
$filename = iconv('UTF-8', 'UTF-8//IGNORE', $filename);
return preg_replace('/[^\w\.]+/u', '_', $filename);
}
function cleanQueryText($text) {
$text = preg_replace('/[\x00-\x1F\x7F]/u', ' ', $text);
$text = preg_replace('/\s+/', ' ', $text);
return trim($text);
}
function extractText($filePath) {
$extension = strtolower(pathinfo($filePath, PATHINFO_EXTENSION));
if ($extension !== 'pdf') {
return '';
}
$outputFile = tempnam(sys_get_temp_dir(), 'txt_') . '.txt';
$command = "pdftotext " . escapeshellarg($filePath) . " " . escapeshellarg($outputFile);
logMessage("Выполняем команду для извлечения текста: " . $command);
exec($command, $output, $returnVar);
if ($returnVar !== 0) {
logMessage("Ошибка pdftotext: " . implode("\n", $output));
return '';
}
if (!file_exists($outputFile)) {
logMessage("Файл pdftotext не создан: $filePath");
return '';
}
$text = file_get_contents($outputFile);
unlink($outputFile);
$cleanText = trim(preg_replace('/\pC+/u', '', $text));
if (mb_strlen($cleanText, 'UTF-8') < 200) {
logMessage("Извлечённый текст для $filePath слишком короткий или не содержит полезной информации. Запуск OCR.");
return '';
}
logMessage("Извлечённый текст для $filePath (первые 500 символов): " . mb_substr($cleanText, 0, 500, 'UTF-8'));
return $cleanText;
}
function doOCR($filePath) {
logMessage("🔍 Запуск OCR. Сначала конвертируем PDF в изображения: $filePath");
// Создаем временную папку для изображений
$imageDir = sys_get_temp_dir() . "/ocr_images_" . uniqid();
$images = convertPdfToImages($filePath, $imageDir);
if (empty($images)) {
logMessage("❌ Ошибка: Не удалось преобразовать PDF в изображения");
return '';
}
logMessage("✅ PDF преобразован в " . count($images) . " изображений, запускаем OCR");
$outputText = '';
foreach ($images as $image) {
$text = runOCRonImage($image);
$outputText .= "\n" . $text;
}
logMessage("✅ OCR завершен. Извлеченный текст:\n" . mb_substr($outputText, 0, 500, 'UTF-8'));
return trim($outputText);
}
// Функция OCR для обработки каждого изображения
function runOCRonImage($imagePath) {
$outputFile = tempnam(sys_get_temp_dir(), 'ocr_') . '.txt';
$command = "tesseract " . escapeshellarg($imagePath) . " " . escapeshellarg($outputFile) . " -l rus --psm 6 --oem 1";
logMessage("🔍 OCR на изображении: $command");
exec($command, $output, $returnVar);
if ($returnVar !== 0) {
logMessage("❌ Ошибка Tesseract: " . implode("\n", $output));
return '';
}
if (!file_exists($outputFile . ".txt")) {
logMessage("❌ Ошибка: OCR-файл не создан");
return '';
}
$text = file_get_contents($outputFile . ".txt");
unlink($outputFile . ".txt");
return $text;
}
function convertPdfToImages($pdfPath, $outputDir) {
if (!file_exists($pdfPath)) {
logMessage("Файл не существует: $pdfPath");
return [];
}
if (!file_exists($outputDir)) {
mkdir($outputDir, 0777, true);
logMessage("Создана директория для изображений: $outputDir");
}
$imagePattern = $outputDir . '/page-%03d.jpg';
$command = "LC_ALL=en_US.UTF-8 convert -density 300 " . escapeshellarg($pdfPath) . " -quality 90 " . escapeshellarg($imagePattern);
logMessage("Выполняем команду: " . $command);
exec($command . " 2>&1", $output, $returnVar);
if ($returnVar !== 0) {
logMessage("Ошибка при конвертации PDF в изображения.");
return [];
}
return glob($outputDir . '/*.jpg');
}
/*
function checkNSFWWithVision($filePath) {
logMessage("NSFW-проверка через URL " . NSFW_MODERATION_API . " для файла: $filePath");
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => NSFW_MODERATION_API,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => [
'file' => new CURLFile($filePath)
],
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
'Authorization: Bearer ' . OPENAI_API_KEY
]
]);
$response = curl_exec($curl);
$httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
$curlError = curl_error($curl);
curl_close($curl);
logMessage("Ответ NSFW: HTTP $httpCode - " . $response);
if ($curlError) {
logMessage("Ошибка cURL при проверке NSFW: " . $curlError);
return null;
}
$decoded = json_decode($response, true);
if ($httpCode !== 200 || isset($decoded['detail'])) {
logMessage("Ошибка анализа NSFW: " . json_encode($decoded, JSON_UNESCAPED_UNICODE));
return null;
}
return $decoded['nsfw'] ?? null;
}
*/
/*function checkNSFWWithVision($filePath) {
logMessage("NSFW-проверка через URL https://api.proxyapi.ru/v1/moderation для файла: $filePath");
$curl = curl_init();
curl_setopt_array($curl, [
// CURLOPT_URL => "https://api.proxyapi.ru/v1/moderation",
CURLOPT_URL => NSFW_MODERATION_API,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => [
'file' => new CURLFile($filePath)
],
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . OPENAI_API_KEY
]
]);
$response = curl_exec($curl);
$httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
$curlError = curl_error($curl);
curl_close($curl);
logMessage("Ответ NSFW: HTTP $httpCode - " . $response);
if ($curlError) {
logMessage("Ошибка cURL при проверке NSFW: " . $curlError);
return null;
}
$decoded = json_decode($response, true);
// Проверяем статус ответа
if ($httpCode !== 200 || isset($decoded['detail'])) {
logMessage("Ошибка анализа NSFW: " . json_encode($decoded, JSON_UNESCAPED_UNICODE));
return null;
}
return $decoded['data'][0]['nsfw'] ?? null;
}
*/
function checkNSFWWithVision($filePath) {
logMessage("⚠️ NSFW-проверка отключена для файла: $filePath");
return null;
}
/**
* Формирует объединённое сообщение по документам.
* Если текст можно извлечь напрямую, то передаётся только ссылка (ID файла).
* Если не удалось извлечь текст, используется OCR, и включается полученный текст.
* Также производится проверка на цензуру.
*
* @param array $documents Массив документов (с полями 'filepath' и 'title').
* @param array $uploadedFileIds Ассоциативный массив с путями к файлам => их идентификаторами.
* @return string Объединённое сообщение для передачи в GPT.
*/
function analyzeDocuments($documents, $uploadedFileIds) {
$combinedMessages = [];
foreach ($documents as $doc) {
if (empty($doc['filepath'])) {
logMessage("Неверный путь: " . json_encode($doc, JSON_UNESCAPED_UNICODE));
continue;
}
// Получаем fileID, полученный при загрузке
$fileId = $uploadedFileIds[$doc['filepath']] ?? еизвестный_ID';
// Выполняем проверку на NSFW
$nsfw = checkNSFWWithVision($doc['filepath']);
if ($nsfw !== null && $nsfw > 0.8) {
$messageBlock = "Документ: " . $doc['title'] . "\n";
$messageBlock .= "Файл (ID: " . $fileId . ") не прошёл проверку цензуры.\n";
} else {
// Пытаемся извлечь текст напрямую
$text = extractText($doc['filepath']);
if (!empty($text)) {
// Если файл читаемый, передаём только ссылку (ID файла)
$messageBlock = "Документ: " . $doc['title'] . "\n";
$messageBlock .= "Файл передан с ID: " . $fileId . ". Текст не загружен, так как файл читаемый.\n";
} else {
// Если не удалось извлечь текст, пробуем OCR
$ocrText = doOCR($doc['filepath']);
if (!empty($ocrText)) {
$messageBlock = "Документ: " . $doc['title'] . "\n";
$messageBlock .= "Файл передан с ID: " . $fileId . ". OCR текст:\n" . $ocrText . "\n";
} else {
$messageBlock = "Документ: " . $doc['title'] . "\n";
$messageBlock .= "Файл передан с ID: " . $fileId . ". Не удалось извлечь текст.\n";
}
}
}
$combinedMessages[] = $messageBlock;
}
return implode("\n-----------------\n", $combinedMessages);
}
/**
* Создает Vector Store, загружает файлы и возвращает массив с идентификаторами.
*
* @param array $filePaths Массив путей к файлам.
* @return array|null Массив с ключами 'vectorStoreId' и 'fileIds' (ассоциативный массив [путь => fileID]), или null при ошибке.
*/
function createVectorStoreAndUploadFiles($filePaths) {
logMessage("Создание Vector Store и загрузка файлов...");
$vectorStoreId = createVectorStore();
if (!$vectorStoreId) {
logMessage("Ошибка: не удалось создать Vector Store.");
return null;
}
$uploadedFiles = [];
foreach ($filePaths as $filePath) {
logMessage("Загрузка файла: $filePath");
if (!file_exists($filePath)) {
logMessage("Ошибка: Файл не существует: $filePath");
continue;
}
$fileId = uploadFileToOpenAI($filePath);
if (!$fileId) {
logMessage("Ошибка загрузки файла: $filePath");
continue;
}
if (!addFileToVectorStore($vectorStoreId, $fileId)) {
logMessage("Ошибка добавления файла в Vector Store: $filePath");
} else {
logMessage("Файл успешно добавлен в Vector Store: $filePath");
$uploadedFiles[$filePath] = $fileId;
}
}
return ['vectorStoreId' => $vectorStoreId, 'fileIds' => $uploadedFiles];
}
?>