Files
crm.clientright.ru/wappi.php

337 lines
18 KiB
PHP
Executable File
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 для вызова снаружи вебхуками входящих сообщений в WhatsApp
* All Rights Reserved.
* Contributor(s): Илья Руденко itsaturn@yandex.ru
********************************************************************************/
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'); //Вынимаем пришедшие данные вот таким оригинальным образом - это просто строка, НЕ массив!
//$str = '{"messages":[{"wh_type":"incoming_message","profile_id":"527d54d0-b2af","id":"2DC4B20ABFD7CA7A39F60DF8009C10CF","body":"Тест","type":"chat","from":"79298407770@c.us","to":"79286601171@c.us","senderName":"Илья Руденко","chatId":"79298407770@c.us","timestamp":"2023-04-06T18:02:20+03:00","time":1680793340,"from_where":"phone","contact":null,"is_forwarded":false,"stanza_id":""}]}';
$str = str_replace('[', '', $str);
$str = str_replace(']', '', $str);
$data = json_decode($str, true); // Формируем массив
if ($data['messages']['chatId'] == $data['messages']['from']) {
$logstring = date('Y-m-d H:i:s').' '.$str.PHP_EOL; // Пишем в лог то, что будем обрабатывать, но только если это личное сообщение - мусор из чатов не интересен
file_put_contents('wa_inbound.log', $logstring, FILE_APPEND);
}
$wh_type = $data['messages']['wh_type']; //Тип события
$type = $data['messages']['type']; //Тип сообщения
$message = $data['messages']['body']; // Тело сообщения
$profile = $data['messages']['profile_id']; // Идентификатор пользователя, кому предназначено сообщение
$from = substr($data['messages']['from'], 0, 11); //Номер отправителя
// Отбираем только входящие текстовые сообщения, отправленные в личку (не в общие чаты)
if ($wh_type == 'incoming_message' and $data['messages']['chatId'] == $data['messages']['from'] and ($type == 'chat' or $type == 'document' or $type == 'image')) {
$logstring = date('Y-m-d H:i:s').' Ищем клиента с телефоном: '.$from.PHP_EOL;
file_put_contents('wa_inbound.log', $logstring, FILE_APPEND);
$adb = PearDatabase::getInstance();
// Т.к. в половине случаев базе номера хранятся без первой цифры или с восьмерки, нам придется искать по всем трем вариантам
$short = substr($from, 1);
$start8 = '8'.substr($from, 1);
$query = 'select p.crmid, w.id, p.setype, e.smownerid
from vtiger_pbxmanager_phonelookup p
left join vtiger_ws_entity w on w.name = p.setype
left join vtiger_crmentity e on e.crmid = p.crmid
where (p.fnumber = ? or p.fnumber = ? or p.fnumber = ?) and e.deleted = 0
order by CASE WHEN p.setype = "Contacts" THEN 1 ELSE 2 END, p.crmid desc limit 1';
$result = $adb->pquery($query, array($from, $short, $start8));
$user = Users::getActiveAdminUser(); // Получаем пользователя, под которым будем создавать коммент
if ($adb->num_rows($result) == 0) {
$logstring = date('Y-m-d H:i:s').' По телефону не нашли - будем создавать'.PHP_EOL;
file_put_contents('wa_inbound.log', $logstring, FILE_APPEND);
$sender = $data['messages']['senderName']; // Отправитель сообщения
$space = mb_strpos($sender, ' ');
if ($space > 0) {
$firstname = mb_substr($sender, 0, $space);
$lastname = mb_substr($sender, $space + 1);
} else {
$firstname = $sender;
$lastname = '-';
}
try {
$params = array (
'firstname' => $firstname,
'lastname' => $lastname,
'mobile' => $from,
'assigned_user_id' => $user
);
if ($profile == '4f2d8b57-3889') {
$params['cf_1740'] = '000';
}
$logstring = date('Y-m-d H:i:s').' Массив: '.json_encode($params).PHP_EOL;
file_put_contents('wa_inbound.log', $logstring, FILE_APPEND);
$contact = vtws_create('Contacts', $params, $user);
$output = 'ID : '.$contact['id'];
$customer = substr($contact['id'], 3);
$crmid = $contact['id'];
$setype = 'Contacts';
$userid = substr($user, 3);
} catch (WebServiceException $ex) {
$output = $ex->getMessage();
}
$logstring = date('Y-m-d H:i:s').' Создание Контакта : '.$output.PHP_EOL;
file_put_contents('wa_inbound.log', $logstring, FILE_APPEND);
} else {
$crmid = $adb->query_result($result, 0, 'id').'x'.$adb->query_result($result, 0, 'crmid'); // Сразу конкатенируем идентификатор справочника с айдишником через Х
$customer = $adb->query_result($result, 0, 'crmid'); // голый id клиента для апдейта
$setype = $adb->query_result($result, 0, 'setype'); // Модуль клиента - он Lead или Contact
$userid = $adb->query_result($result, 0, 'smownerid'); // id ответственного по найденному клиенту
$logstring = date('Y-m-d H:i:s').' Найден клиент с ID: '.$crmid.PHP_EOL;
file_put_contents('wa_inbound.log', $logstring, FILE_APPEND);
}
// Если нам прислали файл или картинку, то их надо раскодировать и собственно создать файлы в системе
$msg = '';
if ($type == 'document' or $type == 'image') {
$mime_type = $data['messages']['mimetype']; // Имя файла нам передали натурально
$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');
if (!in_array($mime_type, $allowed_types)) {
// Недопустимого формата
$msg = 'Передан файл недопустимого формата (не Документ, не PDF и не картина) - НЕ загружен!';
$logstring = date('Y-m-d H:i:s').' '.$msg.PHP_EOL;
file_put_contents('wa_inbound.log', $logstring, FILE_APPEND);
Reply($from, 'Файл '.$filename.' не доставлен!'.PHP_EOL.PHP_EOL.'Принимаются только картинки, PDF и документы Word и Excel', $profile);
} else {
$filecontent = base64_decode($message);
$file_size = strlen($filecontent);
if ($file_size > 5242880) {
// Размером более 5мб
$msg = 'Передан файл более 5 мб - НЕ загружен!';
$logstring = date('Y-m-d H:i:s').' '.$msg.PHP_EOL;
file_put_contents('wa_inbound.log', $logstring, FILE_APPEND);
Reply($from, 'Файл '.$filename.' не доставлен!'.PHP_EOL.PHP_EOL.'Объем файла не должен превышать 5Мб', $profile);
} else {
// Размер и тип подходящие
if ($type == 'document') {
$filename = $data['messages']['file_name']; // Имя файла нам передали натурально
} else {
// Используем правильное имя файла для изображений
$filename = isset($data['messages']['file_name']) ? $data['messages']['file_name'] : 'image.jpg';
$logstring = date('Y-m-d H:i:s').' Имя файла для изображения: '.$filename.PHP_EOL;
file_put_contents('wa_inbound.log', $logstring, FILE_APPEND);
}
}
}
// Если появилась ошибка
if ($msg <> '') {
// То она и есть текст комментария
$message = $msg;
} else {
// Проверяем есть ли caption в сообщении
if (isset($data['messages']['caption']) && !empty($data['messages']['caption'])) {
$message = $data['messages']['caption'];
$logstring = date('Y-m-d H:i:s').' Используем caption как текст комментария: '.$message.PHP_EOL;
file_put_contents('wa_inbound.log', $logstring, FILE_APPEND);
} else {
$message = 'Файл во вложении';
}
}
}
// А теперь добавим коммент к найденному или созданному Контакту
try {
$params = array (
'commentcontent' => $message,
'related_to' => $crmid,
'channel' => 'WhatsApp',
'assigned_user_id' => vtws_getWebserviceEntityId('Users', $user->id)
);
$logstring = date('Y-m-d H:i:s').' Массив: '.json_encode($params).PHP_EOL;
file_put_contents('wa_inbound.log', $logstring, FILE_APPEND);
$comment = vtws_create('ModComments', $params, $user);
$output = 'ID : '.$comment['id'];
// Почему-то при попытке сразу прописать customer при создании коммента, в половине случаев вылетает ошибка
// поэтому сначала просо создаем комментарий, а потом отдельным апдейтом корректируем, что это не просто коммент, а ответ от клиента
$commentid = substr($comment['id'], 3);
$query = 'update vtiger_modcomments set customer = ?, userid = 0 where modcommentsid = ?';
$result = $adb->pquery($query, array($customer, $commentid));
} catch (WebServiceException $ex) {
$output = $ex->getMessage();
}
$logstring = date('Y-m-d H:i:s').' Создание коммента: '.$output.PHP_EOL;
file_put_contents('wa_inbound.log', $logstring, FILE_APPEND);
// Файл есть и он валидный - надо скачать и загрузить в коммент
if (($type == 'document' or $type == 'image') and $msg == '') {
$current_id = $adb->getUniqueID("vtiger_crmentity");
$date_var = date('Y-m-d H:i:s');
$ownerid = getUserId($userid, $customer); // Если у клиента есть активный Проект - достанем оттуда ответственного
$upload_file_path = decideFilePath();
file_put_contents($upload_file_path . $current_id . "_" . $filename, $filecontent);
$logstring = date('Y-m-d H:i:s').' Файл '.$filename.' типа '.$mime_type.' сохранен в storage'.PHP_EOL;
file_put_contents('wa_inbound.log', $logstring, FILE_APPEND);
// ОТКЛЮЧАЕМ автоматическую конвертацию изображений в PDF
// Оставляем изображения как есть для лучшей совместимости
$convert_images_to_pdf = false; // Можно включить при необходимости
if (in_array($mime_type, $images) && $convert_images_to_pdf) {
// Если прислали картинку, то преобразуем ее в PDF
$PDFPath = jpg2pdf($upload_file_path . $current_id . "_" . $filename);
// Грохнем ненужную картинку
unlink($upload_file_path . $current_id . "_" . $filename);
// Из полного пути к PDF вытащим чисто имя файла
$file_parts = pathinfo($filename);
$base_name = $file_parts['filename'];
$filename = $base_name . '.pdf';
$mime_type= 'application/pdf';
$logstring = date('Y-m-d H:i:s').' Изображение конвертировано в PDF: '.$filename.PHP_EOL;
file_put_contents('wa_inbound.log', $logstring, FILE_APPEND);
}
$sql1 = "insert into vtiger_crmentity (crmid,smcreatorid,smownerid,setype,createdtime,modifiedtime) values(?,?,?,?,?,?)";
$params1 = array($current_id, $ownerid, $ownerid, 'ModComments Attachment', $adb->formatDate($date_var, true), $adb->formatDate($date_var, true));
$adb->pquery($sql1, $params1);
$logstring = date('Y-m-d H:i:s').' Добавили аттач в crmentity с параметрами: '.json_encode($params1).PHP_EOL;
file_put_contents('wa_inbound.log', $logstring, FILE_APPEND);
$sql2 = "insert into vtiger_attachments(attachmentsid, name, type, path, storedname) values(?,?,?,?,?)";
$params2 = array($current_id, $filename, $mime_type, $upload_file_path, $filename);
$adb->pquery($sql2, $params2);
$logstring = date('Y-m-d H:i:s').' Добавили аттач в attachments с параметрами: '.json_encode($params2).PHP_EOL;
file_put_contents('wa_inbound.log', $logstring, FILE_APPEND);
$sql3 = "insert into vtiger_seattachmentsrel(crmid, attachmentsid) values(?,?)";
$adb->pquery($sql3, array($commentid, $current_id));
$sql4 = 'update vtiger_modcomments set filename = ? where modcommentsid = ?';
$adb->pquery($sql4, array($current_id, $commentid));
$logstring = date('Y-m-d H:i:s').' связали аттач с комментом с параметрами: '.json_encode(array($commentid, $current_id)).PHP_EOL;
file_put_contents('wa_inbound.log', $logstring, FILE_APPEND);
}
// Собираем ссылку на сущность, куда добавлен коммент
$link = 'module='.$setype.'&view=Detail&record='.$customer.'&app=MARKETING';
// Собираем текст уведомления с именем отправителя
if ($setype == 'Leads') {
$query = 'select l.firstname, l.lastname, e.smownerid as userid
from vtiger_leaddetails l
left join vtiger_crmentity e on e.crmid = l.leadid
where l.leadid = ? and e.deleted = 0';
$result = $adb->pquery($query, array($customer));
$userid = $adb->query_result($result, 0, 'userid');
} else {
$query = 'select c.firstname, c.lastname, e.smownerid as userid
from vtiger_contactdetails c
left join vtiger_crmentity e on e.crmid = c.contactid
where c.contactid = ? and e.deleted = 0';
$result = $adb->pquery($query, array($customer));
$userid = getUserId($adb->query_result($result, 0, 'userid'), $customer); // Тут мы определим, кому показывать уведомление - ответственному по Контакту, или по Проекту, если он есть
}
$name = $adb->query_result($result, 0, 'firstname').' '.$adb->query_result($result, 0, 'lastname');
$title = $name.' - новое сообщение'; // Итоговый текст уведомления
// Ищем непрочтенную всплывашку с этим клиентом
$query = 'select id from vtiger_vdnotifierpro where userid = ? and crmid = ? and title = ? and status = 5';
$result = $adb->pquery($query, array($userid, $customer, $title));
if ($adb->num_rows($result) > 0) {
// Обновляем время в старой всплывашке, чтобы не плодить дубли
$id = $adb->query_result($result, 0, 'id');
$query = 'update vtiger_vdnotifierpro set modifiedtime = ? where id = ?';
$result = $adb->pquery($query, array(date('Y-m-d H:i:s'), $id));
// $logstring = date('Y-m-d H:i:s').' id записи '.$id.PHP_EOL;
// file_put_contents('wa_inbound.log', $logstring, FILE_APPEND);
} else {
// Добавляем новую всплывашку
$query = 'insert into vtiger_vdnotifierpro (userid, modulename, crmid, modiuserid, link, title, action, modifiedtime, status) values (?, ?, ?, 0, ?, ?, "", ?, 5)';
$result = $adb->pquery($query, array($userid, $setype, $customer, $link, $title, date('Y-m-d H:i:s')));
}
} else {
$logstring = date('Y-m-d H:i:s').' Левая шняга?'.PHP_EOL;
file_put_contents('wa_inbound.log', $logstring, FILE_APPEND);
}
function getUserId($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) {
// Единственный активный Проект - вытащим оттуда ответственного
$output = $adb->query_result($result, 0, 'userid');
} else {
// Активных Проектов нет, а может быть несколько - значит ответственным будет владелец Контакта
$output = $userid;
}
return $output;
}
function Reply($mobile, $message, $from) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://wappi.pro/api/sync/message/send?profile_id='.$from);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, '{"recipient": "'.$mobile.'", "body": "'.$message.'"}');
$headers = array();
$headers[] = 'Accept: application/json';
$headers[] = 'Authorization: 45971e2d99071461360515ced697ef08d270363d';
$headers[] = 'Content-Type: text/plain';
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
$output = curl_exec($ch);
curl_close($ch);
return $output;
}
?>