303 lines
15 KiB
Plaintext
303 lines
15 KiB
Plaintext
|
|
<?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';
|
|||
|
|
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
|
|||
|
|
from vtiger_pbxmanager_phonelookup p
|
|||
|
|
left join vtiger_ws_entity w on w.name = p.setype
|
|||
|
|
where p.fnumber = ? or p.fnumber = ? or p.fnumber = ?
|
|||
|
|
order by 1 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
|
|||
|
|
|
|||
|
|
$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'
|
|||
|
|
); //Список разрешенных к сохранению типов файлов
|
|||
|
|
|
|||
|
|
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 = 'image.jpg';
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Если появилась ошибка
|
|||
|
|
if ($msg <> '') {
|
|||
|
|
// То она и есть текст комментария
|
|||
|
|
$message = $msg;
|
|||
|
|
} 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);
|
|||
|
|
|
|||
|
|
$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'.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'.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').' связали аттач с комментом '.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;
|
|||
|
|
}
|
|||
|
|
?>
|