Files
crm.clientright.ru/code4.php

719 lines
30 KiB
PHP
Raw Normal View History

<?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';
// Подключение к БД (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;
}
// 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. Попытка извлечь текст напрямую (например, pdftotext)
$extractedText = extractText($doc['filepath']);
if (empty($extractedText)) {
// Если прямое извлечение не дало результата, запускаем OCR
$extractedText = doOCR($doc['filepath']);
}
// 3. Если текста все равно нет используем Vision для описания изображения
if (empty($extractedText)) {
$extractedText = describeImageWithVision($doc['filepath']);
}
// 4. Получаем контекст из базы знаний (stub-функция)
$knowledgeContext = getKnowledgeBaseContext($doc['filepath']);
// 5. Объединяем извлеченный текст и контекст
$finalContent = $extractedText . "\n" . $knowledgeContext;
// 6. Получаем file_id для привязки (если имеется)
$fileId = $uploadedFileIds[$doc['filepath']] ?? '';
// 7. Анализируем документ через ассистента
$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");
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => OPENAI_VISION_API . "/describe",
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 в описании изображения: " . $curlError);
return '';
}
logMessage("Ответ Vision (описание): HTTP $httpCode - " . $response);
$decoded = json_decode($response, true);
return $decoded['description'] ?? '';
}
/**
* Функция 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['nsfw'])) {
return $decoded['nsfw']; // предполагается булевое значение
}
logMessage("Ошибка анализа NSFW через стандартный Vision endpoint: " . json_encode($decoded, JSON_UNESCAPED_UNICODE));
return null;
}
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);
}
function checkNSFWLocally($filePath) {
logMessage("Запуск локальной проверки NSFW для файла: $filePath");
$extension = strtolower(pathinfo($filePath, PATHINFO_EXTENSION));
$imageToCheck = $filePath;
if ($extension === 'pdf') {
$outputImage = tempnam(sys_get_temp_dir(), 'pdf_img_') . '.png';
$command = "convert -density 150 " . escapeshellarg($filePath) . "[0] -quality 90 " . escapeshellarg($outputImage);
exec($command, $output, $returnVar);
if ($returnVar !== 0) {
logMessage("Ошибка конвертации PDF в изображение для локальной NSFW проверки.");
return null;
}
$imageToCheck = $outputImage;
}
// Используем функцию 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
];
// Отправка сообщения в тред
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => OPENAI_THREADS_API . "/$threadId/messages",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode($messageData),
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 null;
}
logMessage("Ответ (сообщение): HTTP $httpCode - " . $response);
$decoded = json_decode($response, true);
if ($httpCode !== 200 || !isset($decoded['id'])) {
logMessage("Ошибка отправки сообщения: " . json_encode($decoded, JSON_UNESCAPED_UNICODE));
return null;
}
// Запуск ассистента
$runData = ['assistant_id' => $assistantId];
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => OPENAI_THREADS_API . "/$threadId/runs",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode($runData),
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 null;
}
logMessage("Ответ (запуск ассистента): HTTP $httpCode - " . $response);
$decodedRun = json_decode($response, true);
if ($httpCode !== 200 || !isset($decodedRun['id'])) {
logMessage("Ошибка запуска ассистента: " . json_encode($decodedRun, JSON_UNESCAPED_UNICODE));
return null;
}
$runId = $decodedRun['id'];
// Ожидание завершения обработки ассистентом с максимальным таймаутом
$maxIterations = 30; // максимум 30 итераций (примерно 60 секунд)
$iterations = 0;
do {
sleep(2);
$iterations++;
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => OPENAI_THREADS_API . "/$threadId/runs/$runId",
CURLOPT_RETURNTRANSFER => true,
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 null;
}
logMessage("Статус запуска (итерация $iterations): HTTP $httpCode - " . $response);
$decodedStatus = json_decode($response, true);
$status = $decodedStatus['status'] ?? null;
if ($iterations >= $maxIterations) {
logMessage("Превышен максимальный таймаут ожидания завершения работы ассистента.");
return null;
}
} while ($status === 'queued' || $status === 'in_progress');
if ($status !== 'completed') {
logMessage("Запуск ассистента завершился с ошибкой: $status");
return null;
}
// Получение результата
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => OPENAI_THREADS_API . "/$threadId/messages",
CURLOPT_RETURNTRANSFER => true,
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 null;
}
logMessage("Ответ (сообщения): HTTP $httpCode - " . $response);
$decodedMessages = json_decode($response, true);
if ($httpCode !== 200 || !isset($decodedMessages['data'])) {
logMessage("Ошибка получения сообщений: " . json_encode($decodedMessages, JSON_UNESCAPED_UNICODE));
return null;
}
logMessage("Результаты анализа: " . json_encode($decodedMessages['data'], JSON_UNESCAPED_UNICODE));
return $decodedMessages['data'];
}
/* ===================== Формирование отчета ===================== */
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;
}