Files
crm.clientright.ru/miniapp_msg.php
Fedor 01c4fe80b5 chore: snapshot current working tree changes
Save all currently accumulated repository changes as a backup snapshot for Gitea so no local work is lost.
2026-03-26 14:19:01 +03:00

362 lines
17 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
/*********************************************************************************
* Endpoint для добавления комментария из мини-приложения.
* Куда писать: contact_id → к контакту; ticket_id → к заявке (HelpDesk); project_id → к проекту.
* Передаётся ровно один из них (при нескольких — приоритет: ticket_id > project_id > contact_id).
* Вход: JSON — объект или массив { "message" (обяз.) + один из contact_id / ticket_id / project_id }.
* Опционально: contact_id как автор (customer); file_id, mime_type, file_size, filename, botname.
* Канал: miniapp.
********************************************************************************/
error_reporting(E_ALL);
ini_set('display_errors', '1');
include_once 'modules/Users/Users.php';
include_once 'include/utils/CommonUtils.php';
include_once 'include/utils/utils.php';
require_once('include/Webservices/Utils.php');
require_once 'include/Webservices/Create.php';
require_once 'include/Webservices/Revise.php';
require_once 'include/utils/WhatsApp.php';
require_once 'includes/Loader.php';
vimport ('includes.runtime.Globals');
vimport ('includes.runtime.BaseModel');
vimport ('includes.runtime.LanguageHandler');
$str = file_get_contents('php://input');
$logstring = date('Y-m-d H:i:s').' '.$str.PHP_EOL;
file_put_contents('logs/miniapp_msg.log', $logstring, FILE_APPEND);
$input = json_decode($str, true);
if (json_last_error() !== JSON_ERROR_NONE) {
$logstring = date('Y-m-d H:i:s').' Ошибка парсинга JSON: '.json_last_error_msg().PHP_EOL;
file_put_contents('logs/miniapp_msg.log', $logstring, FILE_APPEND);
header('Content-Type: application/json; charset=utf-8');
echo json_encode(array('success' => false, 'error' => 'Invalid JSON'));
exit;
}
// Единообразно: всегда массив элементов
if (isset($input['message']) && (isset($input['contact_id']) || isset($input['ticket_id']) || isset($input['ticketid']) || isset($input['project_id']))) {
$items = array($input);
} else {
$items = is_array($input) ? $input : array();
}
if (!is_array($items) || empty($items)) {
$logstring = date('Y-m-d H:i:s').' Ожидается объект или массив с message и одним из: contact_id, ticket_id, project_id'.PHP_EOL;
file_put_contents('logs/miniapp_msg.log', $logstring, FILE_APPEND);
header('Content-Type: application/json; charset=utf-8');
echo json_encode(array('success' => false, 'error' => 'Expected message + one of contact_id, ticket_id, project_id'));
exit;
}
global $adb;
$user = Users::getActiveAdminUser();
$allowed_types = array(
'text/plain',
'image', 'image/png', 'image/jpeg', 'image/jpg',
'application/pdf',
'application/msword', 'application/excel', 'application/vnd.ms-excel',
'application/vnd.oasis.opendocument.text', 'application/vnd.oasis.opendocument.spreadsheet',
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
);
$images = array('image', 'image/png', 'image/jpeg', 'image/jpg');
$all_bot_tokens = array(
'klientprav_bot' => '6118440594:AAGpqeudXF9wHSZ7vpAsXQ4Jp5XXlUoqB1A',
'lexpriority_bot' => '6631721508:AAG0FIXhlprPGeeb_6zh5gjOpNdWzSWIRS8',
'clientright_bot' => '7919885676:AAEPfOt-oLl2rR1FhlGDd_yqk6ScXJr43T0'
);
$results = array();
foreach ($items as $idx => $data) {
$contact_id = isset($data['contact_id']) ? (int)$data['contact_id'] : 0;
$ticket_id = isset($data['ticket_id']) ? (int)$data['ticket_id'] : (isset($data['ticketid']) ? (int)$data['ticketid'] : 0);
$project_id = isset($data['project_id']) ? (int)$data['project_id'] : 0;
$message = isset($data['message']) ? $data['message'] : '';
// Куда вешаем комментарий: приоритет ticket_id > project_id > contact_id
if ($ticket_id > 0) {
$related_to = '6x' . $ticket_id;
$target_type = 'HelpDesk';
$target_id = $ticket_id;
} elseif ($project_id > 0) {
$related_to = '33x' . $project_id;
$target_type = 'Project';
$target_id = $project_id;
} elseif ($contact_id > 0) {
$related_to = '12x' . $contact_id;
$target_type = 'Contacts';
$target_id = $contact_id;
} else {
$results[] = array('success' => false, 'error' => 'Required one of contact_id, ticket_id, project_id');
continue;
}
if ($message === '' && !isset($data['file_id']) && empty($data['url']) && empty($data['newfile'])) {
$results[] = array('success' => false, 'target_type' => $target_type, 'target_id' => $target_id, 'error' => 'message or file_id required');
continue;
}
// Текст комментария и проверка файла (как в telegram.php)
$msg = '';
$file_id = isset($data['file_id']) ? $data['file_id'] : null;
$external_url = isset($data['url']) ? trim($data['url']) : '';
$external_newfile = isset($data['newfile']) ? trim($data['newfile']) : '';
if ($external_url === '' && $external_newfile !== '' && preg_match('#^https?://#i', $external_newfile)) {
$external_url = $external_newfile;
}
$external_filename = isset($data['file_name']) ? $data['file_name'] : (isset($data['filename']) ? $data['filename'] : '');
$mime_type = isset($data['mime_type']) ? $data['mime_type'] : '';
$file_size = isset($data['file_size']) ? (int)$data['file_size'] : 0;
$filename = isset($data['filename']) ? $data['filename'] : 'file';
$botname = isset($data['botname']) ? $data['botname'] : 'clientright_bot';
if (!array_key_exists($botname, $all_bot_tokens)) {
$botname = 'clientright_bot';
}
if ($file_id) {
if ($file_size > 5242880) {
$msg = 'Передан файл более 5 мб - НЕ загружен!';
} elseif (!in_array($mime_type, $allowed_types)) {
$msg = 'Передан файл недопустимого формата - НЕ загружен!';
}
if (!isset($message) || $message === '') {
$message = $msg !== '' ? $msg : 'Файл во вложении';
} elseif ($msg !== '') {
$message .= PHP_EOL.'======='.PHP_EOL.$msg;
}
} elseif ($external_url !== '' || $external_newfile !== '') {
if (!isset($message) || $message === '') {
$message = 'Файл во вложении';
}
}
try {
$params = array(
'commentcontent' => $message,
'related_to' => $related_to,
'channel' => 'miniapp',
'assigned_user_id' => $user
);
$logstring = date('Y-m-d H:i:s').' [miniapp] Параметры для vtws_create: '.json_encode($params).PHP_EOL;
file_put_contents('logs/miniapp_msg.log', $logstring, FILE_APPEND);
$comment = vtws_create('ModComments', $params, $user);
$commentid = (int)substr($comment['id'], 3);
$logstring = date('Y-m-d H:i:s').' [miniapp] Ответ API (id, channel в ответе): id='.($comment['id'] ?? '?').', channel='.(isset($comment['channel']) ? $comment['channel'] : '(нет в ответе)').PHP_EOL;
file_put_contents('logs/miniapp_msg.log', $logstring, FILE_APPEND);
// Явно пишем channel в БД (vtws_create может не сохранить поле, если оно не в маппинге модуля)
$adb->pquery('UPDATE vtiger_modcomments SET channel = ? WHERE modcommentsid = ?', array('miniapp', $commentid));
$logstring = date('Y-m-d H:i:s').' [miniapp] Выполнен UPDATE vtiger_modcomments SET channel=miniapp WHERE modcommentsid='.$commentid.PHP_EOL;
file_put_contents('logs/miniapp_msg.log', $logstring, FILE_APPEND);
// Проверка: что реально записалось в БД по channel
$check = $adb->pquery('SELECT modcommentsid, channel, commentcontent FROM vtiger_modcomments WHERE modcommentsid = ?', array($commentid));
if ($adb->num_rows($check) > 0) {
$row_channel = $adb->query_result($check, 0, 'channel');
$logstring = date('Y-m-d H:i:s').' [miniapp] Проверка БД: modcommentsid='.$commentid.' channel='.var_export($row_channel, true).PHP_EOL;
file_put_contents('logs/miniapp_msg.log', $logstring, FILE_APPEND);
} else {
$logstring = date('Y-m-d H:i:s').' [miniapp] Проверка БД: запись modcommentsid='.$commentid.' не найдена'.PHP_EOL;
file_put_contents('logs/miniapp_msg.log', $logstring, FILE_APPEND);
}
if ($contact_id > 0) {
$query = 'UPDATE vtiger_modcomments SET customer = ?, userid = 0 WHERE modcommentsid = ?';
$adb->pquery($query, array($contact_id, $commentid));
}
$logstring = date('Y-m-d H:i:s').' Коммент к '.$target_type.' '.$target_id.' создан, commentid='.$commentid.PHP_EOL;
file_put_contents('logs/miniapp_msg.log', $logstring, FILE_APPEND);
// Вложение: скачать по прямой ссылке (S3) и привязать к комментарию
if ($external_url !== '' && $msg === '') {
$filename_from_url = '';
$parsed_path = parse_url($external_url, PHP_URL_PATH);
if (!empty($parsed_path)) {
$filename_from_url = basename($parsed_path);
}
$filename_to_use = $external_filename !== '' ? $external_filename : ($filename_from_url !== '' ? $filename_from_url : 'file');
$context = stream_context_create(array('http' => array('timeout' => 20)));
$file_data = @file_get_contents($external_url, false, $context);
if ($file_data === false) {
$logstring = date('Y-m-d H:i:s').' [miniapp] Не удалось скачать файл по URL: '.$external_url.PHP_EOL;
file_put_contents('logs/miniapp_msg.log', $logstring, FILE_APPEND);
} else {
$current_id = $adb->getUniqueID("vtiger_crmentity");
$date_var = date('Y-m-d H:i:s');
$ownerid = $user;
if ($contact_id > 0) {
$ownerRes = $adb->pquery('SELECT smownerid FROM vtiger_crmentity WHERE crmid = ? AND deleted = 0', array($contact_id));
$ownerid = $adb->num_rows($ownerRes) ? $adb->query_result($ownerRes, 0, 'smownerid') : $user;
$ownerid = getUserIdMiniapp($ownerid, $contact_id);
} else {
$ownerRes = $adb->pquery('SELECT smownerid FROM vtiger_crmentity WHERE crmid = ? AND deleted = 0', array($target_id));
$ownerid = $adb->num_rows($ownerRes) ? $adb->query_result($ownerRes, 0, 'smownerid') : $user;
}
$upload_file_path = decideFilePath();
$full_path = $upload_file_path . $current_id . "_" . $filename_to_use;
file_put_contents($full_path, $file_data);
$detected_mime = $mime_type;
if ($detected_mime === '' || $detected_mime === 'application/octet-stream') {
$detected_mime = @mime_content_type($full_path);
}
if (!$detected_mime) {
$detected_mime = 'application/octet-stream';
}
if (in_array($detected_mime, $images) || strpos($detected_mime, 'image/') === 0) {
$PDFPath = jpg2pdf($full_path);
@unlink($full_path);
$file_parts = pathinfo($filename_to_use);
$filename_to_use = (isset($file_parts['filename']) ? $file_parts['filename'] : 'file') . '.pdf';
$detected_mime = 'application/pdf';
$full_path = $PDFPath;
}
$adb->pquery(
"INSERT INTO vtiger_crmentity (crmid, smcreatorid, smownerid, setype, createdtime, modifiedtime) VALUES (?,?,?,?,?,?)",
array($current_id, $ownerid, $ownerid, 'ModComments Attachment', $adb->formatDate($date_var, true), $adb->formatDate($date_var, true))
);
$adb->pquery(
"INSERT INTO vtiger_attachments(attachmentsid, name, type, path, storedname) VALUES (?,?,?,?,?)",
array($current_id, $filename_to_use, $detected_mime, $upload_file_path, $filename_to_use)
);
$adb->pquery("INSERT INTO vtiger_seattachmentsrel(crmid, attachmentsid) VALUES (?,?)", array($commentid, $current_id));
$adb->pquery('UPDATE vtiger_modcomments SET filename = ? WHERE modcommentsid = ?', array($current_id, $commentid));
}
}
// Вложение: скачать через Telegram API и привязать к комментарию (как в telegram.php)
$file_path = null;
$bot_token = $all_bot_tokens[$botname];
if ($file_id && $msg === '') {
$tokens_to_try = array();
if (isset($all_bot_tokens[$botname])) {
$tokens_to_try[] = array('name' => $botname, 'token' => $all_bot_tokens[$botname]);
}
foreach ($all_bot_tokens as $name => $token) {
if ($name !== $botname) {
$tokens_to_try[] = array('name' => $name, 'token' => $token);
}
}
foreach ($tokens_to_try as $bot_info) {
$try_token = $bot_info['token'];
$try_name = $bot_info['name'];
$url = 'https://api.telegram.org/bot'.$try_token.'/getFile';
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_POST, true);
curl_setopt($curl, CURLOPT_TIMEOUT, 10);
curl_setopt($curl, CURLOPT_POSTFIELDS, array('file_id' => $file_id));
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
$output = curl_exec($curl);
$output = json_decode($output, true);
curl_close($curl);
if (!empty($output['ok'])) {
$file_path = $output['result']['file_path'];
$bot_token = $try_token;
break;
}
}
if ($file_path) {
$url = 'https://api.telegram.org/file/bot'.$bot_token.'/'.$file_path;
$current_id = $adb->getUniqueID("vtiger_crmentity");
$date_var = date('Y-m-d H:i:s');
$ownerid = $user;
if ($contact_id > 0) {
$ownerRes = $adb->pquery('SELECT smownerid FROM vtiger_crmentity WHERE crmid = ? AND deleted = 0', array($contact_id));
$ownerid = $adb->num_rows($ownerRes) ? $adb->query_result($ownerRes, 0, 'smownerid') : $user;
$ownerid = getUserIdMiniapp($ownerid, $contact_id);
} else {
$ownerRes = $adb->pquery('SELECT smownerid FROM vtiger_crmentity WHERE crmid = ? AND deleted = 0', array($target_id));
$ownerid = $adb->num_rows($ownerRes) ? $adb->query_result($ownerRes, 0, 'smownerid') : $user;
}
$upload_file_path = decideFilePath();
if ($filename === 'image') {
$slash = strpos($file_path, '/');
$filename = $slash !== false ? substr($file_path, $slash + 1) : 'image.jpg';
}
file_put_contents($upload_file_path . $current_id . "_" . $filename, file_get_contents($url));
if (in_array($mime_type, $images)) {
$PDFPath = jpg2pdf($upload_file_path . $current_id . "_" . $filename);
unlink($upload_file_path . $current_id . "_" . $filename);
$file_parts = pathinfo($filename);
$filename = (isset($file_parts['filename']) ? $file_parts['filename'] : 'file') . '.pdf';
$mime_type = 'application/pdf';
}
$adb->pquery(
"INSERT INTO vtiger_crmentity (crmid, smcreatorid, smownerid, setype, createdtime, modifiedtime) VALUES (?,?,?,?,?,?)",
array($current_id, $ownerid, $ownerid, 'ModComments Attachment', $adb->formatDate($date_var, true), $adb->formatDate($date_var, true))
);
$adb->pquery(
"INSERT INTO vtiger_attachments(attachmentsid, name, type, path, storedname) VALUES (?,?,?,?,?)",
array($current_id, $filename, $mime_type, $upload_file_path, $filename)
);
$adb->pquery("INSERT INTO vtiger_seattachmentsrel(crmid, attachmentsid) VALUES (?,?)", array($commentid, $current_id));
$adb->pquery('UPDATE vtiger_modcomments SET filename = ? WHERE modcommentsid = ?', array($current_id, $commentid));
}
}
// Всплывашка: уведомление ответственного за сущность (тикет / проект / контакт)
$ownerRes = $adb->pquery('SELECT smownerid FROM vtiger_crmentity WHERE crmid = ? AND deleted = 0', array($target_id));
$userid_owner = $adb->num_rows($ownerRes) ? $adb->query_result($ownerRes, 0, 'smownerid') : null;
if ($userid_owner) {
if ($target_type === 'HelpDesk') {
$link = 'module=HelpDesk&view=Detail&record='.$target_id.'&app=SUPPORT';
} elseif ($target_type === 'Project') {
$link = 'module=Project&view=Detail&record='.$target_id.'&app=SUPPORT';
} else {
$link = 'module=Contacts&view=Detail&record='.$target_id.'&app=MARKETING';
}
$title = 'Новое сообщение (miniapp)';
$exist = $adb->pquery('SELECT id FROM vtiger_vdnotifierpro WHERE userid = ? AND crmid = ? AND title = ? AND status = 5', array($userid_owner, $target_id, $title));
if ($adb->num_rows($exist) > 0) {
$adb->pquery('UPDATE vtiger_vdnotifierpro SET modifiedtime = ? WHERE id = ?', array(date('Y-m-d H:i:s'), $adb->query_result($exist, 0, 'id')));
} else {
$adb->pquery(
'INSERT INTO vtiger_vdnotifierpro (userid, modulename, crmid, modiuserid, link, title, action, modifiedtime, status) VALUES (?,?,?,0,?,?,?,?,5)',
array($userid_owner, $target_type, $target_id, $link, $title, '', date('Y-m-d H:i:s'))
);
}
}
$results[] = array('success' => true, 'target_type' => $target_type, 'target_id' => $target_id, 'comment_id' => $commentid);
} catch (WebServiceException $ex) {
$logstring = date('Y-m-d H:i:s').' Ошибка: '.$ex->getMessage().PHP_EOL;
file_put_contents('logs/miniapp_msg.log', $logstring, FILE_APPEND);
$results[] = array('success' => false, 'target_type' => isset($target_type) ? $target_type : '', 'target_id' => isset($target_id) ? $target_id : 0, 'error' => $ex->getMessage());
}
}
header('Content-Type: application/json; charset=utf-8');
echo json_encode(array('success' => true, 'results' => $results));
function getUserIdMiniapp($userid, $contactid) {
global $adb;
$query = 'SELECT e.smownerid AS userid FROM vtiger_project p
LEFT JOIN vtiger_crmentity e ON e.crmid = p.projectid
WHERE e.deleted = 0 AND p.linktoaccountscontacts = ? AND p.projectstatus <> "completed"';
$result = $adb->pquery($query, array($contactid));
if ($adb->num_rows($result) === 1) {
return $adb->query_result($result, 0, 'userid');
}
return $userid;
}
?>