Files
crm.clientright.ru/code6.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

710 lines
30 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
// Настройки OpenAI API и Vision API
const OPENAI_API_KEY = 'sk-GS24OxHQYfq8ErW5CRLoN5F1CfJPxNsY';
const OPENAI_ASSISTANT_API = 'https://api.proxyapi.ru/openai/v1/assistants';
const OPENAI_FILES_API = 'https://api.proxyapi.ru/openai/v1/files';
const OPENAI_THREADS_API = 'https://api.proxyapi.ru/openai/v1/threads';
const OPENAI_VECTOR_STORES_API = 'https://api.proxyapi.ru/openai/v1/vector_stores';
const OPENAI_VISION_API = 'https://api.proxyapi.ru/v1/chat/completions'; // Для анализа изображений и NSFW
const LOG_FILE = 'logs/scriptDS3.log';
// ID и имя ассистента
const ASSISTANT_ID = 'asst_suGt51aoepXUkJiC0t3vobeG';
const ASSISTANT_NAME = 'Clientright';
// Для корректной обработки кириллицы (при необходимости)
setlocale(LC_ALL, 'ru_RU.UTF-8');
// Подключение к БД (Vtiger CRM)
$dsn = 'mysql:host=localhost;port=3306;dbname=ci20465_72new;charset=utf8mb4';
$user = 'ci20465_72new';
$password = 'EcY979Rn';
try {
$pdo = new PDO($dsn, $user, $password, [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
} catch (PDOException $e) {
logMessage("Ошибка подключения к БД: " . $e->getMessage());
die("Ошибка подключения к БД");
}
function logMessage($message) {
if (!is_dir('logs')) {
mkdir('logs', 0777, true);
}
file_put_contents(LOG_FILE, date('Y-m-d H:i:s') . " - " . $message . "\n", FILE_APPEND | LOCK_EX);
}
/* ===================== Основной скрипт ===================== */
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$input = json_decode(file_get_contents('php://input'), true);
$id = $input['id'] ?? null;
if (!$id) {
logMessage("Ошибка: отсутствует ID документа");
die("Ошибка: отсутствует ID документа");
}
logMessage("Начало обработки документа с ID: $id");
// Получение данных из CRM
$documents = fetchDocumentData($pdo, $id);
if (empty($documents)) {
logMessage("Документы не найдены для ID: $id");
die("Документы не найдены для ID: $id");
}
logMessage("Документы получены из БД: " . json_encode($documents, JSON_UNESCAPED_UNICODE));
// Получение путей файлов
$filePaths = array_column($documents, 'filepath');
// Загрузка файлов в Vector Store и получение mapping (путь → file_id)
$uploadResult = createVectorStoreAndUploadFiles($filePaths);
if (!$uploadResult) {
logMessage("Ошибка создания Vector Store или загрузки файлов");
die("Ошибка создания Vector Store или загрузки файлов");
}
$vectorStoreId = $uploadResult['vectorStoreId'];
$uploadedFileIds = $uploadResult['fileIds'];
// Обновление ассистента с указанием векторного хранилища
if (!updateAssistantWithVectorStore($vectorStoreId)) {
logMessage("Ошибка обновления ассистента с Vector Store");
die("Ошибка обновления ассистента");
}
// Анализ документов с учетом NSFW, OCR, Vision и знаний из базы
$allResults = analyzeDocuments($documents, $uploadedFileIds);
if (empty($allResults)) {
logMessage("Ошибка: анализ документов не вернул результатов");
die("Ошибка: анализ документов не вернул результатов");
}
// Формирование итогового отчета
$report = generateReport($allResults);
logMessage("Итоговый отчет:\n" . $report);
echo $report;
logMessage("Обработка всех документов завершена.");
} else {
logMessage("Ошибка: запрос должен быть POST");
die("Ошибка: запрос должен быть POST");
}
/* ===================== Функции для работы с CRM и Vector Store ===================== */
function fetchDocumentData($pdo, $id) {
logMessage("Получение данных документа из CRM по ID: $id");
$sql = "
SELECT
n.title,
CASE
WHEN a.storedname IS NOT NULL
THEN CONCAT(a.path, a.attachmentsid, '_', a.storedname)
ELSE CONCAT(a.path, a.attachmentsid, '_', a.name)
END AS filepath
FROM
vtiger_senotesrel r
LEFT JOIN
vtiger_notes n ON n.notesid = r.notesid
LEFT JOIN
vtiger_crmentity e ON e.crmid = r.notesid
LEFT JOIN
vtiger_seattachmentsrel r2 ON r2.crmid = r.notesid
LEFT JOIN
vtiger_attachments a ON a.attachmentsid = r2.attachmentsid
WHERE
r.crmid = ?
AND e.deleted = 0
AND (a.type = 'application/pdf' OR a.type = 'application/octet-stream')
";
try {
$stmt = $pdo->prepare($sql);
$stmt->execute([$id]);
$documents = $stmt->fetchAll(PDO::FETCH_ASSOC);
logMessage("Документы получены из CRM: " . json_encode($documents, JSON_UNESCAPED_UNICODE));
return $documents;
} catch (PDOException $e) {
logMessage("Ошибка при выполнении запроса к CRM: " . $e->getMessage());
return [];
}
}
function createVectorStoreAndUploadFiles($filePaths) {
logMessage("Создание Vector Store и загрузка файлов...");
$vectorStoreId = createVectorStore();
if (!$vectorStoreId) return null;
$uploadedFiles = []; // mapping: путь → file_id
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];
}
function createVectorStore() {
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => OPENAI_VECTOR_STORES_API,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode(['name' => 'Vector Store']),
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'Authorization: Bearer ' . OPENAI_API_KEY,
'OpenAI-Beta: assistants=v2'
]
]);
$response = curl_exec($curl);
$httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
$curlError = curl_error($curl);
curl_close($curl);
if ($curlError) {
logMessage("Ошибка cURL при создании Vector Store: " . $curlError);
return null;
}
logMessage("Ответ OpenAI (создание Vector Store): HTTP $httpCode - " . $response);
$decoded = json_decode($response, true);
if ($httpCode !== 200 || !isset($decoded['id'])) {
logMessage("Ошибка при создании Vector Store: " . json_encode($decoded, JSON_UNESCAPED_UNICODE));
return null;
}
return $decoded['id'];
}
function uploadFileToOpenAI($filePath) {
logMessage("Загрузка файла в OpenAI: $filePath");
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => OPENAI_FILES_API,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => [
'file' => new CURLFile($filePath),
'purpose' => 'assistants'
],
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . OPENAI_API_KEY,
'OpenAI-Beta: assistants=v2'
]
]);
$response = curl_exec($curl);
$httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
$curlError = curl_error($curl);
curl_close($curl);
if ($curlError) {
logMessage("Ошибка cURL при загрузке файла: " . $curlError);
return null;
}
logMessage("Ответ OpenAI (загрузка файла): HTTP $httpCode - " . $response);
$decoded = json_decode($response, true);
if ($httpCode !== 200 || !isset($decoded['id'])) {
logMessage("Ошибка при загрузке файла: " . json_encode($decoded, JSON_UNESCAPED_UNICODE));
return null;
}
return $decoded['id'];
}
function addFileToVectorStore($vectorStoreId, $fileId) {
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => OPENAI_VECTOR_STORES_API . "/$vectorStoreId/files",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode(['file_id' => $fileId]),
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'Authorization: Bearer ' . OPENAI_API_KEY,
'OpenAI-Beta: assistants=v2'
]
]);
$response = curl_exec($curl);
$httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
$curlError = curl_error($curl);
curl_close($curl);
if ($curlError) {
logMessage("Ошибка cURL при добавлении файла в Vector Store: " . $curlError);
return false;
}
logMessage("Ответ OpenAI (добавление файла): HTTP $httpCode - " . $response);
$decoded = json_decode($response, true);
if ($httpCode !== 200 || !isset($decoded['id'])) {
logMessage("Ошибка добавления файла: " . json_encode($decoded, JSON_UNESCAPED_UNICODE));
return false;
}
return true;
}
function updateAssistantWithVectorStore($vectorStoreId) {
$data = [
'tool_resources' => [
'file_search' => [
'vector_store_ids' => [$vectorStoreId]
]
]
];
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => OPENAI_ASSISTANT_API . "/" . ASSISTANT_ID,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_POSTFIELDS => json_encode($data),
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'Authorization: Bearer ' . OPENAI_API_KEY,
'OpenAI-Beta: assistants=v2'
]
]);
$response = curl_exec($curl);
$httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
$curlError = curl_error($curl);
curl_close($curl);
if ($curlError) {
logMessage("Ошибка обновления ассистента: " . $curlError);
return false;
}
logMessage("Ответ OpenAI (обновление ассистента): HTTP $httpCode - " . $response);
$decoded = json_decode($response, true);
if ($httpCode !== 200 || !isset($decoded['id'])) {
logMessage("Ошибка обновления ассистента: " . json_encode($decoded, JSON_UNESCAPED_UNICODE));
return false;
}
return true;
}
/* ===================== Логика анализа документов ===================== */
/**
* Функция analyzeDocuments:
* Проверяет документ на NSFW через Vision.
* Если NSFW найден, помечает для ручной модерации.
* Иначе пытается извлечь текст (сначала с помощью встроенного извлечения, затем через OCR).
* Если текста нет, вызывает Vision для описания изображения.
* Получает контекст из базы знаний и отправляет данные в OpenAI Assistants для финального анализа.
*/
function analyzeDocuments($documents, $uploadedFileIds) {
$results = [];
foreach ($documents as $doc) {
if (empty($doc['filepath']) || strpos($doc['filepath'], '_') === 0) {
logMessage("Неверный путь: " . json_encode($doc, JSON_UNESCAPED_UNICODE));
continue;
}
// Инициализация переменных
$isNSFW = false;
$extractedText = '';
$imageDescription = '';
// 1. Проверка NSFW через Vision API
$isNSFW = checkNSFWWithVision($doc['filepath']);
if ($isNSFW === null) {
$isNSFW = checkNSFWLocally($doc['filepath']);
}
if ($isNSFW) {
logMessage("NSFW обнаружен: " . $doc['filepath']);
$results[] = [
'document' => $doc['title'],
'status' => 'NSFW',
'message' => 'Файл содержит NSFW-контент и отправлен на ручную модерацию.'
];
continue;
}
// 2. Попытка извлечь текст напрямую
$extractedText = extractText($doc['filepath']);
// 3. Если прямое извлечение не дало результата, запускаем OCR
if (empty($extractedText)) {
$extractedText = doOCR($doc['filepath']);
}
// 4. Если текста все равно нет используем Vision для описания изображения
if (empty($extractedText)) {
$imageDescription = describeImageWithVision($doc['filepath']);
}
// 5. Получаем контекст из базы знаний
$knowledgeContext = getKnowledgeBaseContext($doc['filepath']);
// 6. Объединяем извлеченный текст, описание изображения и контекст
$finalContent = $extractedText . "\n" . $imageDescription . "\n" . $knowledgeContext;
// 7. Получаем file_id для привязки (если имеется)
$fileId = $uploadedFileIds[$doc['filepath']] ?? '';
// 8. Анализируем документ через ассистента
$threadId = createThread();
if (!$threadId) {
logMessage("Ошибка создания треда для " . $doc['filepath']);
continue;
}
$analysis = analyzeDocumentWithAssistant($threadId, ASSISTANT_ID, $fileId, $finalContent);
if ($analysis) {
logMessage("Анализ завершен: " . json_encode($analysis, JSON_UNESCAPED_UNICODE));
$results[] = [
'document' => $doc['title'],
'status' => 'Анализ завершен',
'analysis' => $analysis
];
} else {
logMessage("Ошибка анализа " . $doc['filepath']);
$results[] = [
'document' => $doc['title'],
'status' => 'Ошибка анализа',
'message' => 'Не удалось проанализировать документ.'
];
}
}
return $results;
}
/**
* Функция extractText пытается извлечь текст напрямую (например, через pdftotext).
* Если извлечение не удалось, возвращает пустую строку.
*/
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);
exec($command, $output, $returnVar);
if ($returnVar !== 0 || !file_exists($outputFile)) {
logMessage("Ошибка извлечения текста из PDF: $filePath");
return '';
}
$text = file_get_contents($outputFile);
unlink($outputFile);
return $text;
}
/**
* Функция doOCR использует локальную OCR-систему (например, Tesseract).
*/
function doOCR($filePath) {
logMessage("Запуск OCR для файла: $filePath");
$outputFile = tempnam(sys_get_temp_dir(), 'ocr_') . '.txt';
$command = "tesseract " . escapeshellarg($filePath) . " " . escapeshellarg($outputFile) . " -l rus";
exec($command, $output, $returnVar);
if ($returnVar !== 0 || !file_exists($outputFile . ".txt")) {
logMessage("Ошибка OCR для файла: $filePath");
return '';
}
$text = file_get_contents($outputFile . ".txt");
unlink($outputFile . ".txt");
return $text;
}
/**
* Функция describeImageWithVision вызывает OpenAI Vision для получения описания изображения.
*/
function describeImageWithVision($filePath) {
logMessage("Запуск описания изображения через Vision для файла: $filePath");
// Читаем содержимое файла и кодируем его в base64
$imageData = base64_encode(file_get_contents($filePath));
$data = [
"model" => "gpt-4-vision-preview",
"messages" => [
[
"role" => "user",
"content" => [
[
"type" => "text",
"text" => "Опиши это изображение подробно. Если это документ, прочитай и опиши его содержимое."
],
[
"type" => "image_url",
"image_url" => [
"url" => "data:image/jpeg;base64,$imageData"
]
]
]
]
],
"max_tokens" => 500
];
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.openai.com/v1/chat/completions",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode($data),
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);
if ($curlError) {
logMessage("Ошибка cURL в описании изображения: " . $curlError);
return '';
}
logMessage("Ответ Vision (описание): HTTP $httpCode - " . $response);
$decoded = json_decode($response, true);
if (isset($decoded['choices'][0]['message']['content'])) {
return $decoded['choices'][0]['message']['content'];
} else {
logMessage("Ошибка при получении описания изображения: " . json_encode($decoded));
return '';
}
}
/**
* Функция checkNSFWWithVision использует OpenAI Vision для анализа NSFW-контента.
*/
function checkNSFWWithVision($filePath) {
logMessage("NSFW-проверка через стандартный Vision endpoint для файла: $filePath");
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => OPENAI_VISION_API . "/analyze",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => [
'file' => new CURLFile($filePath)
],
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . OPENAI_API_KEY,
'OpenAI-Beta: vision'
]
]);
$response = curl_exec($curl);
$httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
$curlError = curl_error($curl);
curl_close($curl);
if ($curlError) {
logMessage("Ошибка cURL при проверке NSFW через Vision: " . $curlError);
return null;
}
logMessage("Ответ Vision (анализ): HTTP $httpCode - " . $response);
$decoded = json_decode($response, true);
if ($httpCode !== 200 || isset($decoded['detail'])) {
logMessage("Ошибка анализа NSFW через стандартный Vision endpoint: " . json_encode($decoded, JSON_UNESCAPED_UNICODE));
return null;
}
return $decoded['nsfw'] ?? null;
}
/**
* Функция classifyImage использует Python-скрипт (NudeClassifier) для анализа изображения.
*/
function classifyImage($imagePath) {
$absolutePath = realpath($imagePath);
if (!$absolutePath) {
logMessage("ERROR: Не удалось получить абсолютный путь для " . $imagePath);
return [];
}
logMessage("DEBUG: Абсолютный путь для классификации: " . $absolutePath);
$escapedPath = escapeshellarg($absolutePath);
logMessage("DEBUG: Экранированный путь для классификации: " . $escapedPath);
// Выполнение команды Python для запуска NudeClassifier
$command = "python3 -c \"import json; from nudenet import NudeClassifier; classifier = NudeClassifier(); print(json.dumps(classifier.classify($escapedPath)))\"";
logMessage("DEBUG: Выполнение команды: " . $command);
$output = shell_exec($command);
logMessage("DEBUG: Вывод команды: " . $output);
if ($output === null) {
logMessage("ERROR: shell_exec вернул null при выполнении NudeClassifier");
return [];
}
return json_decode(trim($output), true);
}
/**
* Функция convertPdfToImages:
* Создает директорию для изображений, если её нет.
* Конвертирует PDF в изображения с помощью ImageMagick (convert) с density=300 и quality=90.
* При этом принудительно устанавливается локаль LC_ALL=en_US.UTF-8, чтобы корректно обрабатывать имена файлов с кириллицей.
* Возвращает список созданных изображений или пустой массив при ошибке.
*/
function convertPdfToImages($pdfPath, $outputDir) {
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);
logMessage("DEBUG: Вывод convert: " . implode("\n", $output));
if ($returnVar !== 0) {
logMessage("Ошибка при конвертации PDF в изображения.");
return [];
}
return glob($outputDir . '/*.jpg');
}
/**
* Функция checkNSFWLocally выполняет локальную проверку NSFW.
* Если файл PDF, конвертирует его в изображения с помощью convertPdfToImages и анализирует первую страницу.
*/
function checkNSFWLocally($filePath) {
logMessage("Запуск локальной проверки NSFW для файла: $filePath");
$extension = strtolower(pathinfo($filePath, PATHINFO_EXTENSION));
$imageToCheck = $filePath;
if ($extension === 'pdf') {
$outputDir = sys_get_temp_dir() . '/pdf_images_' . md5($filePath);
$images = convertPdfToImages($filePath, $outputDir);
if (empty($images)) {
logMessage("Ошибка конвертации PDF в изображения для локальной NSFW проверки.");
return null;
}
// Используем первую страницу для проверки NSFW
$imageToCheck = $images[0];
}
// Используем функцию classifyImage для анализа изображения
$classification = classifyImage($imageToCheck);
if (empty($classification)) {
logMessage("DEBUG: Нет данных проверки NSFW для изображения '$imageToCheck'.");
return false;
}
$absImagePath = realpath($imageToCheck);
if (isset($classification[$absImagePath])) {
$unsafeProbability = $classification[$absImagePath]['unsafe'] ?? 0;
logMessage("DEBUG: Для изображения '$absImagePath' получено unsafeProbability = " . $unsafeProbability);
if ($unsafeProbability > 0.8) {
logMessage("⚠️ Обнаружено NSFW-изображение: $absImagePath (unsafe = " . $unsafeProbability . ")");
return true;
} else {
logMessage("DEBUG: unsafeProbability для '$absImagePath' ниже порогового значения (0.8).");
}
} else {
logMessage("DEBUG: Классификатор не вернул данные для '$absImagePath'.");
}
return false;
}
/**
* Stub-функция для получения контекста из базы знаний.
*/
function getKnowledgeBaseContext($filePath) {
// Здесь можно реализовать поиск в базе знаний.
// Пока возвращаем статическую информацию.
return "Статическая информация: нормы и законы РФ, судебные прецеденты...";
}
/**
* Функция analyzeDocumentWithAssistant отправляет финальный запрос ассистенту.
* Дополнительно передаёт извлечённый текст и контекст.
*/
function analyzeDocumentWithAssistant($threadId, $assistantId, $fileId, $content) {
logMessage("Анализ документа: thread_id=$threadId, fileId=$fileId");
$messageContent = "Проанализируй документ";
if (!empty($fileId)) {
$messageContent .= " (file_id: $fileId)";
}
$messageContent .= ". Содержимое для анализа:\n" . $content;
$messageData = [
'role' => 'user',
'content' => $messageContent
];
// ... (остальной код остается без изменений до получения результата)
// Здесь, вероятно, выполняется вызов OpenAI API через cURL
logMessage("Ответ (сообщения): HTTP $httpCode - " . $response);
$decodedMessages = json_decode($response, true);
if ($httpCode !== 200 || !isset($decodedMessages['data'])) {
logMessage("Ошибка получения сообщений: " . json_encode($decodedMessages, JSON_UNESCAPED_UNICODE));
return null;
}
// Извлекаем содержимое последнего сообщения ассистента
$assistantMessage = $decodedMessages['data'][0]['content'][0]['text']['value'] ?? '';
$moderationVerdict = "";
// Попробуем найти строку с вердиктом
if (preg_match('/Вердикт:\s*(Прошло модерацию|Не прошло модерацию)/ui', $assistantMessage, $matches)) {
$moderationVerdict = trim($matches[1]); // Получаем сам текст вердикта
}
// Логируем извлеченный вердикт модерации
logMessage("DEBUG: Извлеченный вердикт модерации: " . ($moderationVerdict ?: "Не найден"));
$final_output = [
"status" => "complete",
"content" => $assistantMessage,
"moderationVerdict" => $moderationVerdict
];
logMessage("DEBUG: Извлеченный контент: " . $assistantMessage);
logMessage("DEBUG: Извлеченный вердикт модерации: " . $moderationVerdict);
logMessage("Результаты анализа: " . json_encode($final_output, JSON_UNESCAPED_UNICODE));
return $final_output;
}
/* ===================== Формирование отчета ===================== */
function generateReport($allResults) {
if (empty($allResults)) {
logMessage("Ошибка: Нет данных для отчета");
return "Ошибка: Нет данных для отчета";
}
$report = "### Итоговый отчет по документам\n\n";
foreach ($allResults as $result) {
$report .= "**Документ:** " . $result['document'] . "\n";
$report .= "**Статус:** " . $result['status'] . "\n";
if (isset($result['analysis'])) {
$report .= "**Анализ:**\n";
if (isset($result['analysis']['nsfw_status'])) {
$report .= "- NSFW статус: " . ($result['analysis']['nsfw_status'] ? "Обнаружен" : "Не обнаружен") . "\n";
}
if (isset($result['analysis']['image_description'])) {
$report .= "- Описание изображения: " . $result['analysis']['image_description'] . "\n";
}
if (isset($result['analysis']['text_content'])) {
$report .= "- Извлеченный текст: " . (strlen($result['analysis']['text_content']) > 100 ?
substr($result['analysis']['text_content'], 0, 100) . "..." :
$result['analysis']['text_content']) . "\n";
}
if (isset($result['analysis']['ai_analysis'])) {
$report .= "- AI анализ: " . $result['analysis']['ai_analysis'] . "\n";
}
} else {
$report .= "**Сообщение:** " . $result['message'] . "\n";
}
$report .= "\n";
}
return $report;
}