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

627 lines
25 KiB
PHP
Raw 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
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_NSFW_API = 'https://api.proxyapi.ru/openai/v1/vision';
//const OPENAI_NSFW_API = 'https://api.proxyapi.ru/openai/v1/chat/completions';
//const OPENAI_VISION_API = 'https://api.proxyapi.ru/openai/v1/vision';
const LOG_FILE = 'logs/scriptDS99.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 (isset($_SERVER['REQUEST_METHOD']) && $_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");
// Получаем документы из базы данных
$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 и загружаем файлы, получая ассоциативный массив fileIds (путь => file_id)
$uploadResult = createVectorStoreAndUploadFiles($filePaths);
if (!$uploadResult) {
logMessage("Ошибка создания Vector Store или загрузки файлов");
die("Ошибка создания Vector Store или загрузки файлов");
}
$vectorStoreId = $uploadResult['vectorStoreId'];
$uploadedFileIds = $uploadResult['fileIds'];
// Обновляем ассистента с Vector Store
if (!updateAssistantWithVectorStore($vectorStoreId)) {
logMessage("Ошибка обновления ассистента с Vector Store");
die("Ошибка обновления ассистента");
}
// Анализ документов теперь передаем также $uploadedFileIds для связи файла с анализом
$allResults = analyzeDocuments($documents, $uploadedFileIds);
if (empty($allResults)) {
logMessage("Ошибка: анализ документов не вернул результатов");
die("Ошибка: анализ документов не вернул результатов");
}
// Формирование итогового отчета
$report = generateReport($allResults);
logMessage("Итоговый отчет:\n" . $report);
echo $report;
logMessage("Обработка всех документов завершена.");
} else {
logMessage("Ошибка: запрос должен быть POST");
die("Ошибка: запрос должен быть POST");
}
// Функция для получения данных из базы Vtiger CRM
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 [];
}
}
// Функция для создания Vector Store и загрузки файлов
// Теперь возвращается массив с ключами 'vectorStoreId' и 'fileIds' (mapping путь => file_id)
function createVectorStoreAndUploadFiles($filePaths) {
logMessage("Создание Vector Store и загрузка файлов...");
$vectorStoreId = createVectorStore();
if (!$vectorStoreId) {
return null;
}
$uploadedFiles = []; // массив: путь => 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];
}
// Функция для создания Vector Store
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);
$decodedResponse = json_decode($response, true);
if ($httpCode !== 200 || !isset($decodedResponse['id'])) {
logMessage("Ошибка при создании Vector Store: " . json_encode($decodedResponse, JSON_UNESCAPED_UNICODE));
return null;
}
return $decodedResponse['id'];
}
// Функция для загрузки файла в OpenAI
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);
$decodedResponse = json_decode($response, true);
if ($httpCode !== 200 || !isset($decodedResponse['id'])) {
logMessage("Ошибка при загрузке файла: " . json_encode($decodedResponse, JSON_UNESCAPED_UNICODE));
return null;
}
return $decodedResponse['id'];
}
// Функция для добавления файла в Vector Store
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 (добавление файла в Vector Store): HTTP $httpCode - " . $response);
$decodedResponse = json_decode($response, true);
if ($httpCode !== 200 || !isset($decodedResponse['id'])) {
logMessage("Ошибка при добавлении файла в Vector Store: " . json_encode($decodedResponse, JSON_UNESCAPED_UNICODE));
return false;
}
return true;
}
// Функция для обновления ассистента с Vector Store
function updateAssistantWithVectorStore($vectorStoreId) {
$updateData = [
'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($updateData),
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 при обновлении ассистента: " . $curlError);
return false;
}
logMessage("Ответ OpenAI (обновление ассистента): HTTP $httpCode - " . $response);
$decodedResponse = json_decode($response, true);
if ($httpCode !== 200 || !isset($decodedResponse['id'])) {
logMessage("Ошибка при обновлении ассистента: " . json_encode($decodedResponse, JSON_UNESCAPED_UNICODE));
return false;
}
return true;
}
// Функция для анализа документов
// Теперь принимает второй параметр — массив fileIds
function analyzeDocuments($documents, $uploadedFileIds) {
$combinedFileIds = [];
$allResults = [];
// Собираем file_id для всех документов, прошедших NSFW-проверку
foreach ($documents as $doc) {
if (empty($doc['filepath']) || strpos($doc['filepath'], '_') === 0) {
logMessage("Ошибка: Путь к файлу пуст или некорректен: " . json_encode($doc, JSON_UNESCAPED_UNICODE));
continue;
}
// Проверка на NSFW
$isNSFW = checkNSFWWithOpenAI($doc['filepath']);
if ($isNSFW === null) {
$isNSFW = checkNSFWLocally($doc['filepath']);
}
if ($isNSFW) {
logMessage("⚠️ Файл содержит NSFW-контент: " . $doc['filepath']);
$allResults[] = [
'document' => $doc['title'],
'status' => 'NSFW',
'message' => 'Файл содержит NSFW-контент и отправлен на ручную модерацию.'
];
continue;
}
// Получаем file_id из массива загруженных файлов
$fileId = $uploadedFileIds[$doc['filepath']] ?? '';
if (!$fileId) {
logMessage("Ошибка: Не найден file_id для файла " . $doc['filepath']);
continue;
}
$combinedFileIds[] = $fileId;
}
// Если есть файлы для анализа, выполняем совокупный анализ
if (!empty($combinedFileIds)) {
$threadId = createThread();
if (!$threadId) {
logMessage("Ошибка создания треда в OpenAI");
die("Ошибка создания треда в OpenAI");
}
// Собираем строку из всех file_id
$fileIdsString = implode(", ", $combinedFileIds);
// Формируем сообщение для совокупного анализа
// Можно также уточнить в сообщении, что анализ проводится по совокупности документов
$analysis = analyzeDocumentWithAssistant($threadId, ASSISTANT_ID, $fileIdsString);
if ($analysis) {
logMessage("Анализ документов завершен: " . json_encode($analysis, JSON_UNESCAPED_UNICODE));
$allResults[] = [
'document' => 'Совокупный анализ',
'status' => 'Анализ завершен',
'analysis' => $analysis
];
} else {
logMessage("Ошибка анализа документов");
$allResults[] = [
'document' => 'Совокупный анализ',
'status' => 'Ошибка анализа',
'message' => 'Не удалось проанализировать документы.'
];
}
}
return $allResults;
}
// Функция для создания треда
function createThread() {
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => OPENAI_THREADS_API,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => 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("Ошибка cURL при создании треда: " . $curlError);
return null;
}
logMessage("Ответ OpenAI (создание треда): HTTP $httpCode - " . $response);
$decodedResponse = json_decode($response, true);
if ($httpCode !== 200 || !isset($decodedResponse['id'])) {
logMessage("Ошибка при создании треда: " . json_encode($decodedResponse, JSON_UNESCAPED_UNICODE));
return null;
}
return $decodedResponse['id'];
}
// Функция для анализа документа с использованием ассистента
// Теперь принимает третий параметр $fileId и передает его в сообщение
function analyzeDocumentWithAssistant($threadId, $assistantId, $fileId) {
logMessage("Анализ документа с использованием ассистента: thread_id=$threadId, assistant_id=$assistantId, fileId=$fileId");
$messageContent = "Проанализируй документ";
if (!empty($fileId)) {
$messageContent .= " с файлом id: " . $fileId;
}
$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("Ошибка cURL при отправке сообщения в тред: " . $curlError);
return null;
}
logMessage("Ответ OpenAI (сообщение в тред): HTTP $httpCode - " . $response);
$decodedResponse = json_decode($response, true);
if ($httpCode !== 200 || !isset($decodedResponse['id'])) {
logMessage("Ошибка при отправке сообщения в тред: " . json_encode($decodedResponse, 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("Ошибка cURL при запуске ассистента: " . $curlError);
return null;
}
logMessage("Ответ OpenAI (запуск ассистента): HTTP $httpCode - " . $response);
$decodedResponse = json_decode($response, true);
if ($httpCode !== 200 || !isset($decodedResponse['id'])) {
logMessage("Ошибка при запуске ассистента: " . json_encode($decodedResponse, JSON_UNESCAPED_UNICODE));
return null;
}
$runId = $decodedResponse['id'];
// Ожидание завершения обработки ассистентом
do {
sleep(2);
$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("Ошибка cURL при проверке статуса запуска: " . $curlError);
return null;
}
logMessage("Ответ OpenAI (статус запуска): HTTP $httpCode - " . $response);
$decodedResponse = json_decode($response, true);
$status = $decodedResponse['status'] ?? 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("Ошибка cURL при получении сообщений: " . $curlError);
return null;
}
logMessage("Ответ OpenAI (сообщения): HTTP $httpCode - " . $response);
$decodedResponse = json_decode($response, true);
if ($httpCode !== 200 || !isset($decodedResponse['data'])) {
logMessage("Ошибка при получении сообщений: " . json_encode($decodedResponse, JSON_UNESCAPED_UNICODE));
return null;
}
logMessage("Результаты анализа: " . json_encode($decodedResponse['data'], JSON_UNESCAPED_UNICODE));
return $decodedResponse['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 .= "**Анализ:** " . json_encode($result['analysis'], JSON_UNESCAPED_UNICODE) . "\n";
} else {
$report .= "**Сообщение:** " . $result['message'] . "\n";
}
$report .= "\n";
}
return $report;
}
/* ===================== NSFW-фильтрация и OCR ===================== */
function checkNSFWWithOpenAI($filePath) {
logMessage("Запуск NSFW-проверки через OpenAI для файла: $filePath");
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => OPENAI_NSFW_API,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => [
'file' => new CURLFile($filePath),
'purpose' => 'nsfw'
],
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 при проверке NSFW через OpenAI: " . $curlError);
return null;
}
logMessage("Ответ OpenAI (NSFW-проверка): HTTP $httpCode - " . $response);
$decodedResponse = json_decode($response, true);
if ($httpCode === 200 && isset($decodedResponse['nsfw'])) {
return $decodedResponse['nsfw'];
} else {
logMessage("Ошибка проверки NSFW через OpenAI: " . json_encode($decodedResponse, JSON_UNESCAPED_UNICODE));
return null;
}
}
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;
}
logMessage("Локальная NSFW-проверка завершена: NSFW не обнаружен для файла: $imageToCheck");
return false;
}