Browse Source

Merge branch 'twilio' into 1.0.0

1.0.0
lucas cardoso 3 years ago
parent
commit
aa46dd9c22
  1. 20
      .gitignore
  2. 4
      .htaccess
  3. 29
      Dockerfile
  4. 422
      app/Controllers/AgentController.php
  5. 107
      app/Controllers/BilheteController.php
  6. 14
      app/Controllers/ClassificacaoController.php
  7. 12
      app/Controllers/ClientController.php
  8. 28
      app/Controllers/MessageController.php
  9. 34
      app/Controllers/QueueController.php
  10. 49
      app/Controllers/SystemMessageController.php
  11. 417
      app/Core/Commands.php
  12. 22
      app/Core/Connect.php
  13. 41
      app/Core/Controller.php
  14. 417
      app/Core/CoreMedia.php
  15. 174
      app/Core/Media.php
  16. 15
      app/Core/Model.php
  17. 9
      app/Interfaces/IApi.php
  18. 24
      app/Interfaces/IApiMedia.php
  19. 742
      app/Middleware/ApiAgente.php
  20. 38
      app/Middleware/ApiInfo.php
  21. 64
      app/Middleware/ApiSupervisor.php
  22. 60
      app/Middleware/Middleware.php
  23. 36
      app/Models/Agent.php
  24. 182
      app/Models/Atendimento.php
  25. 4
      app/Models/EventQueue.php
  26. 50
      app/Models/Evento.php
  27. 59
      app/Models/Message.php
  28. 31
      app/Models/Parametros.php
  29. 37
      app/Models/Pause.php
  30. 6
      app/Models/Queue.php
  31. 408
      app/Models/SupervisorModel.php
  32. 34
      app/Models/SystemMessage.php
  33. 6
      app/Providers/ApiTelegram.php
  34. 227
      app/Providers/ApiTwilio.php
  35. 325
      app/Providers/Positus.php
  36. 2
      app/Providers/RequestURL.php
  37. 139
      app/Providers/Requests.php
  38. 9
      app/Providers/WebHeader.php
  39. 9
      app/Providers/whatsapp.php
  40. 4
      bd
  41. 27
      composer.json
  42. 18
      composer.lock
  43. 1
      config/agent.php
  44. 25
      config/app.php
  45. 2
      config/display_errors.php
  46. 15
      config/event.php
  47. 57
      config/helpers.php
  48. 18
      config/moments.php
  49. 4
      config/whatsapp.php
  50. 530
      database/att-v3.sql
  51. 27
      database/database.sql
  52. 21
      database/modelo_mensagem.json
  53. 17
      database/msg-text-positus.json
  54. 10
      docker-compose.yml
  55. 11
      includes/config.php
  56. 3
      index.php
  57. 15
      ports.conf
  58. 1115
      public/css/styles.css
  59. BIN
      public/images/alerta.png
  60. 1
      public/images/audio-icon.svg
  61. 1
      public/images/camera-icon.svg
  62. 1
      public/images/clip.svg
  63. 363
      public/images/community_message.svg
  64. 2
      public/images/cross-circle.svg
  65. 1
      public/images/double-check-seen.svg
  66. 1
      public/images/double-check-unseen.svg
  67. 1
      public/images/double-check.svg
  68. 1
      public/images/down-arrow.svg
  69. 2
      public/images/enter.svg
  70. BIN
      public/images/favicon.ico
  71. 2
      public/images/file.svg
  72. 1
      public/images/gt-arrow.svg
  73. 1
      public/images/icons.svg
  74. BIN
      public/images/icons/csv-file.png
  75. BIN
      public/images/icons/doc-file.png
  76. BIN
      public/images/icons/notfound-file.png
  77. BIN
      public/images/icons/pdf-file.png
  78. BIN
      public/images/icons/ppt-file.png
  79. BIN
      public/images/icons/txt-file.png
  80. BIN
      public/images/icons/xls-file.png
  81. BIN
      public/images/icons/zip-file.png
  82. BIN
      public/images/loading.gif
  83. 1
      public/images/manage_chats.svg
  84. 1
      public/images/menu-icon.svg
  85. 1
      public/images/message-icon.svg
  86. 1
      public/images/message-tail-receiver.svg
  87. 1
      public/images/message-tail-sender.svg
  88. BIN
      public/images/messenger.png
  89. 1
      public/images/microphone-seen.svg
  90. 2
      public/images/microphone.svg
  91. 1
      public/images/notifications.svg
  92. 2
      public/images/paper-plane.svg
  93. 1
      public/images/pause.svg
  94. 2
      public/images/picture.svg
  95. 1
      public/images/placeholder-image.svg
  96. 1
      public/images/play-audio-icon.svg
  97. 2
      public/images/play.svg
  98. 1
      public/images/power.svg
  99. 2
      public/images/redo.svg
  100. 1
      public/images/search-icon.svg
  101. Some files were not shown because too many files have changed in this diff Show More

20
.gitignore vendored

@ -0,0 +1,20 @@
/node_modules
/public/hot
/public/storage
/storage/*.key
/vendor
.env
.env.backup
.phpunit.result.cache
docker-compose.override.yml
Homestead.json
Homestead.yaml
npm-debug.log
yarn-error.log
/.idea
/stubs
/public/node_modules
/public/vendor
/banco
/websocket/vendor
composer.lock

4
.htaccess

@ -1,4 +0,0 @@
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php/$1 [L]

29
Dockerfile

@ -0,0 +1,29 @@
FROM php:8.0-apache
RUN apt-get update && apt-get install -y \
vim
ADD https://raw.githubusercontent.com/mlocati/docker-php-extension-installer/master/install-php-extensions /usr/local/bin/
RUN chmod uga+x /usr/local/bin/install-php-extensions && sync && \
install-php-extensions pdo_pgsql
RUN echo "ServerName 192.168.115.65" >> /etc/apache2/apache2.conf &&\
a2enmod rewrite &&\
a2dissite 000-default
COPY apache2.conf /etc/apache2/
COPY ports.conf /etc/apache2/
COPY . .
RUN a2enmod headers
RUN service apache2 restart
WORKDIR /var/www/html
COPY index.php index.php
RUN chmod -R 777 /var/www/html/
RUN chmod -R 777 /tmp
RUN chown -R www-data:www-data /var/www/html
EXPOSE 8081
EXPOSE 8090
EXPOSE 5432
ENTRYPOINT ["./inicia.sh"]

422
app/Controllers/AgentController.php

@ -3,14 +3,17 @@
namespace app\Controllers;
use app\Core\Controller;
use app\Models\Agent;
use app\Models\Queue;
use app\Models\Ramal;
use app\Models\Pause;
use app\Models\Bilhete;
use app\Models\EventQueue;
use app\Controllers\ClassificacaoController;
use app\Models\Atendimento;
use app\Models\Evento;
use app\Models\Message;
use app\Models\SupervisorModel;
use Exception;
use websocket\WsInterface;
/**
* Description of AgentController
@ -19,194 +22,149 @@ use Exception;
*/
class AgentController extends Controller
{
/** @var SupervisorModel $agent model de supervisor */
protected $agent;
private $agent;
private $queue;
private $ramal;
private $pause;
private $bilhete;
private $eventqueue;
/** @var Queue $queue model de queue */
protected $queue;
/** @var Ramal $ramal model de Ramal */
protected $ramal;
/** @var Pause $pause model de Pause */
protected $pause;
/** @var Bilhete $bilhete model de Bilhete */
protected $bilhete;
/** @var EventQueue $eventqueue model de EventQueue */
protected $eventqueue;
/** @var Atendimento $atendModel model de Atendimento */
protected $atendModel;
public function __construct()
{
$this->agent = new Agent();
$this->agent = new SupervisorModel();
$this->queue = new Queue();
$this->ramal = new Ramal();
$this->pause = new Pause();
$this->bilhete = new Bilhete();
$this->eventqueue = new EventQueue();
$this->atendModel = new Atendimento();
}
public function openService($ramal, $origemDest)
public function login($fila, $matricula)
{
try {
$this->agent->begin();
$agent = $this->agent->findAgentByRamal($ramal);
$uniqueid = uniqid('', true);
$this->agent->updateAgent($agent->matricula, $ramal, CONF_AGENT_STATUS_OCUPADO, $origemDest, null, 1, $uniqueid);
$this->agent->commit();
return $uniqueid;
} catch (Exception $ex) {
$this->agent->rollback();
logger()->error($ex->getMessage());
}
return false;
}
$queue = $this->queue->findQueueByName(strtoupper($fila));
$agent = $this->agent->findByAgent(strtolower($matricula));
public function closeService($telefone, $motivo, $media)
{
try {
$this->agent->begin();
$atendimento = $this->agent->findByNumber($telefone);
$agent = $this->agent->findAgentByRamal($atendimento->ramal);
if (!$atendimento || !$agent->origem_destino) {
throw new Exception('Você precisa estar em um atendimento para finalizar!');
if ($queue->midiafila == 'N') {
return 'Fila não relacionada como WhatsApp!';
}
$scr = $agent->origem_destino;
$dst = $agent->ramal;
$unique = ($agent->uniqueid) ? $agent->uniqueid : null;
$billsec = strtotime($agent->logado) - strtotime($agent->duracao);
$duration = $billsec;
if (!$queue) {
return 'Fila não encontrada ou não relacionada como WhatsApp!';
}
$disposition = 'ANSWERED';
$calldate = date('Y-m-d H:i:s');
if (!$agent) {
return 'Usuário não encontrado!';
}
$uniqueid = $this->bilhete->addBilhete($calldate, $scr, $dst, $duration, $billsec, $disposition, $unique);
if (!$uniqueid) {
throw new Exception('Não foi possével criar o uniqueid para o bilhete!');
}
if (!$motivo) {
$motivo = $atendimento->phone == 'CLIENT' ? CONF_EVENT_WHATSAPP_TIMERMINO_CLIENTE : CONF_EVENT_WHATSAPP_TIMERMINO_AGENTE;
if ($this->agent->findByMatricula($matricula)) {
return 'Agente já autenticado!';
}
$this->eventqueue->addEventQueue($uniqueid, $agent->dac, $agent->matricula, $motivo, $media);
/*
/**
* VERIFICA CLASSIFICACAO ATENDIMENTO
*/
$classificacao = new ClassificacaoController();
$chamadaSemClassificacao = $classificacao->agentClassificacaoPending($agent->matricula, $agent->dac);
// $classificacao = new ClassificacaoController();
// $chamadaSemClassificacao = $classificacao->agentClassificacaoPending($agent->matricula, $fila);
if (!$this->agent->addAgent($agent->matricula, $fila, $agent->nome)) {
return 'Não foi possével inserir o agente!';
}
$this->agent->updateAgent($agent->matricula, $agent->ramal, CONF_AGENT_STATUS_LIVRE, null, null, 1, null, ($chamadaSemClassificacao) ? 0 : 1);
if (!$this->agent->addEventoLoginAgent($agent->matricula, $queue->id, 1, '0')) {
return 'Não foi possível inserir as informações de autenticação do login!';
}
$this->agent->commit();
return [$dst, $scr];
return true;
} catch (Exception $ex) {
$this->agent->rollback();
$this->message($ex->getMessage());
logger()->error($ex->getMessage());
return $ex->getMessage();
}
return false;
}
public function inService($telefone)
{
try {
$atendimento = $this->agent->findByNumber($telefone);
if (!$atendimento && $this->ramal->findRamal($telefone)) {
return false;
} else {
return ($atendimento ? $atendimento : false);
}
} catch (Exception $ex) {
logger()->error($ex->getMessage());
}
return false;
}
public function login($ramal, $login, $fila, $media)
public function logoff($matricula, $valida = true)
{
try {
$this->agent->begin();
$queue = $this->queue->findQueueByName(strtoupper($fila));
$agent = $this->agent->findByAgent(strtolower($login));
if ($queue->midiafila == 'N') {
throw new Exception('Fila não relacionada como WhatsApp!');
}
if (!$queue) {
throw new Exception('Fila não encontrada ou não relacionada como WhatsApp!');
}
$agent = $this->agent->findAgentByMatricula($matricula);
if (!$agent) {
throw new Exception('Usuário não encontrado!');
throw new Exception('Agente não encontrado!');
}
if (!$this->ramal->findRamal($ramal)) {
throw new Exception('Telefone não encontrado!');
$queue = $this->queue->findQueueByName($agent->fila);
if (!$queue) {
throw new Exception('Agente não conectado!');
}
if ($this->agent->findByNumber($ramal)) {
throw new Exception('Agente já autenticado!');
if ($agent->status != CONF_AGENT_STATUS_LIVRE && $valida) {
throw new Exception('Saia da pausa para fazer logoff');
}
/**
* VERIFICA CLASSIFICACAO ATENDIMENTO
*/
$classificacao = new ClassificacaoController();
$chamadaSemClassificacao = $classificacao->agentClassificacaoPending($agent->matricula, $fila);
if (!$this->agent->addAgent($agent->matricula, $ramal, $login, $queue->nome, $media, ($chamadaSemClassificacao ? 0 : 1))) {
throw new Exception('Não foi possével inserir o agente!');
}
if (!$this->agent->addEventoLoginAgent($agent->matricula, $ramal, $queue->id)) {
throw new Exception('Não foi possível inserir as informações de autenticação do login!');
}
$this->agent->updateEventoLogoffAgent($agent->matricula, '0', $queue->id);
$this->agent->deleteAgent($agent->matricula);
$this->agent->commit();
return true;
} catch (Exception $ex) {
$this->agent->rollback();
$this->message($ex->getMessage());
logger()->error($ex->getMessage());
return $ex->getMessage();
}
return false;
}
public function logoff($ramal)
public function indisponivelAtendimento($matricula, $pausa)
{
try {
$this->agent->begin();
$agent = $this->agent->findAgentByRamal($ramal);
if (!$agent) {
throw new Exception('Telefone não encontrado!');
}
$queue = $this->queue->findQueueByName($agent->dac);
if (!$queue) {
throw new Exception('Agente não conectado!');
}
$this->agent->begin();
$agent = $this->agent->findAgentByMatricula($matricula);
$queue = $this->queue->findQueueByName($agent->fila);
$pause = $this->pause->findPauseByName($pausa);
if ($agent->status == CONF_AGENT_STATUS_PAUSA) {
$this->message('Agente removido de pausa!');
$this->pause->updateEventoOutPause($agent->matricula, $queue->id);
if (!$this->agent->updateAgent($agent->matricula, CONF_AGENT_STATUS_INDISPONIVEL, $pause->motivo)) {
throw new Exception('Não foi possível atualizar o status do agente!');
}
if ($agent->status != CONF_AGENT_STATUS_LIVRE) {
throw new Exception('');
if (!$this->pause->addEventoIndisponivelAgent($agent->matricula, '0', $pause->id, $queue->id)) {
throw new Exception('Não foi possível atualizar informações complementares do agente!');
}
$this->agent->updateEventoLogoffAgent($agent->matricula, $ramal, $queue->id);
$this->agent->deleteAgent($agent->matricula, $ramal);
$this->agent->commit();
return true;
} catch (Exception $ex) {
$this->agent->rollback();
$this->message($ex->getMessage());
logger()->error($ex->getMessage());
return $ex->getMessage();
}
return false;
}
public function enterPause($ramal, $pausa)
public function enterPause($matricula, $pausa)
{
try {
$this->agent->begin();
$agent = $this->agent->findAgentByRamal($ramal);
$queue = $this->queue->findQueueByName($agent->dac);
$agent = $this->agent->findAgentByMatricula($matricula);
$queue = $this->queue->findQueueByName($agent->fila);
$pause = $this->pause->findPauseByName($pausa);
if (!$agent) {
@ -221,15 +179,15 @@ class AgentController extends Controller
throw new Exception('Usuário não conectado em uma fila!');
}
if ($agent->status != 'LIVRE') {
if ($agent->status == 'PAUSA') {
throw new Exception('Agente precisa estar livre para entrar em pausa!');
}
if (!$this->agent->updateAgent($agent->matricula, $ramal, CONF_AGENT_STATUS_PAUSA, null, $pause->motivo, 1)) {
if (!$this->agent->updateAgent($agent->matricula, CONF_AGENT_STATUS_PAUSA, $pause->motivo)) {
throw new Exception('Não foi possível atualizar o status do agente!');
}
if (!$this->pause->addEventoPauseAgent($agent->matricula, $ramal, $pause->id, $queue->id, $pause->produtiva)) {
if (!$this->pause->addEventoPauseAgent($agent->matricula, '0', $pause->id, $queue->id, $pause->produtiva)) {
throw new Exception('Não foi possível atualizar informações complementares do agente!');
}
@ -239,16 +197,17 @@ class AgentController extends Controller
$this->agent->rollback();
$this->message($ex->getMessage());
logger()->error($ex->getMessage());
return $ex->getMessage();
}
return false;
}
public function exitPause($ramal)
public function exitPause($matricula)
{
try {
$this->agent->begin();
$agent = $this->agent->findAgentByRamal($ramal);
$queue = $this->queue->findQueueByName($agent->dac);
$agent = $this->agent->findAgentByMatricula($matricula);
$queue = $this->queue->findQueueByName($agent->fila);
if (!$agent) {
throw new Exception('Telefone não identificado!');
}
@ -257,11 +216,16 @@ class AgentController extends Controller
throw new Exception('Agente não está conectado!');
}
if ($agent->status != CONF_AGENT_STATUS_PAUSA) {
if ($agent->status != CONF_AGENT_STATUS_PAUSA && $agent->status != CONF_AGENT_STATUS_INDISPONIVEL) {
throw new Exception('Agente não está em pausa!');
}
$this->agent->updateAgent($agent->matricula, $ramal, CONF_AGENT_STATUS_LIVRE, null, null, 1);
$atendimentos = $this->atendModel->getAtendimentoAbertoByAgente($matricula);
$param = $this->atendModel->getQuantiAtendimentSimultaneos();
if (count($atendimentos) < $param->prm_media_simultaneo) {
$this->agent->updateAgent($agent->matricula, CONF_AGENT_STATUS_LIVRE);
} else {
$this->agent->updateAgent($agent->matricula, CONF_AGENT_STATUS_OCUPADO);
}
$this->pause->updateEventoOutPause($agent->matricula, $queue->id);
$this->agent->commit();
return true;
@ -269,145 +233,100 @@ class AgentController extends Controller
$this->agent->rollback();
$this->message($ex->getMessage());
logger()->error($ex->getMessage());
return $ex->getMessage();
}
return false;
}
public function transfer($ramal, $ramalTransfer)
public function transfer($matOrigem, $matDestino, $uniqueid)
{
try {
$this->agent->begin();
$agent = $this->agent->findAgentByRamal($ramal);
$atendimento = new Atendimento();
$eventModel = new Evento();
$agent = $this->agent->findAgentByMatricula($matOrigem);
if (!$agent) {
throw new Exception('Agente não conectado!');
}
$agentTransf = $this->agent->findAgentByRamal($ramalTransfer);
$agentTransf = $this->agent->findAgentByMatricula($matDestino);
if (!$agentTransf || $agentTransf->status != CONF_AGENT_STATUS_LIVRE) {
throw new Exception('Agente indisponível para atendimento!');
}
/** Agente Liberado ap�s a transferencia */
$this->agent->updateAgent($agent->matricula, $ramal, CONF_AGENT_STATUS_LIVRE, null, null, 1);
/** Agente Ocupado ap�s a transferencia */
$this->agent->updateAgent($agentTransf->matricula, $ramalTransfer, CONF_AGENT_STATUS_OCUPADO, $agent->origem_destino, null, 1);
$this->agent->commit();
return [$ramal, $ramalTransfer];
} catch (Exception $ex) {
$this->agent->rollback();
$this->message($ex->getMessage());
logger()->error($ex->getMessage());
}
return false;
}
public function infoAgentes($media, $queue = null)
{
try {
$agent = $this->agent->findAllAgentes($media, $queue);
return $agent;
} catch (Exception $ex) {
$this->agent->rollback();
$this->message($ex->getMessage());
logger()->error($ex->getMessage());
}
return false;
}
public function status($ramal, $status, $origemDestino = null)
{
try {
$this->agent->begin();
$agent = $this->agent->findAgentByRamal($ramal);
if (!$agent) {
throw new Exception('Agente não conectado!');
}
$this->agent->updateAgent($agent->matricula, $ramal, strtoupper($status), $origemDestino, null, 1);
$atendAtual = $atendimento->findAtendId($uniqueid);
if (!$atendAtual) {
throw new Exception('Atendimento não encontrado');
}
$atendimento->updAtendimento($uniqueid, $agentTransf->matricula);
$eventModel->createEvento(
$uniqueid,
CONF_EVENT_TRANSFER,
date('Y-m-d H:i:s'),
date('Y-m-d H:i:s'),
$atendAtual->fila,
$agent->matricula
);
$eventModel->createEvento(
$uniqueid,
CONF_EVENT_START,
date('Y-m-d H:i:s'),
date('Y-m-d H:i:s'),
$atendAtual->fila,
$agentTransf->matricula
);
//$ws->enviaMsg($ws->enviaActions('Atendimento transferido', 'transfer', $agent->matricula, $uniqueid));
$provedor = returnChannel($atendAtual->context);
$provedor->enviarMsg($atendAtual->cliente_id, CONF_NAME_REPONSE . ": Atendimento transferido");
$messegeModel = new Message();
$messegeModel->addMessage(
$uniqueid,
$agent->matricula,
$agentTransf->matricula,
'transfer',
'Atendimento transferido',
$agent->nome,
$atendAtual->context,
'read'
);
$this->agent->commit();
$this->atualizaStatusAgente($agentTransf);
$this->atualizaStatusAgente($agent);
$ws = new WsInterface();
$ws->enviaMsg($ws->enviaActions('Atendimento transferido', 'transfer', $agentTransf->matricula, $uniqueid));
return true;
} catch (Exception $ex) {
$this->agent->rollback();
$this->message($ex->getMessage());
logger()->error($ex->getMessage());
return $ex->getMessage();
}
return false;
}
public function priority($queue)
public function atualizaStatusAgente($agente)
{
try {
$agents = $this->agent->findAgentByQueue($queue, CONF_AGENT_STATUS_LIVRE, 'penalidade', 'ASC');
if (!$agents) {
$this->message('Agentes não encontrado!');
return null;
}
//se tiver somente um agente livre
if (sizeof($agents) == 1) {
return $agents[0]->ramal;
}
//validar se possui penalidades iguais
if ($agents[0]->penalidade == $agents[1]->penalidade) {
return null;
$atendimentosAbertos = $this->atendModel->getAtendimentoAbertoByAgente($agente->matricula);
$param = $this->atendModel->getQuantiAtendimentSimultaneos();
if ($agente->status == CONF_AGENT_STATUS_LIVRE || $agente->status == CONF_AGENT_STATUS_OCUPADO) {
if (count($atendimentosAbertos) < $param->prm_media_simultaneo) {
$this->agent->updateAgent($agente->matricula, CONF_AGENT_STATUS_LIVRE);
} else {
$this->agent->updateAgent($agente->matricula, CONF_AGENT_STATUS_OCUPADO);
}
return $agents[0]->ramal;
} catch (Exception $ex) {
logger()->error($ex->getMessage());
}
return false;
}
public function isAgent($ramal)
{
try {
if (!$this->ramal->findRamal($ramal)) {
return 0;
if ($agente->status == CONF_AGENT_STATUS_INDISPONIVEL) {
if (empty($atendimentosAbertos)) {
$this->enterPause($agente->matricula, $agente->motivo_pausa);
}
return 1;
} catch (Exception $ex) {
logger()->error($ex->getMessage());
}
return false;
}
public function inactiveAgents()
{
$agents = $this->agent->findAllAgentes();
$inactiveAgents = array();
foreach ($agents as $agent) {
$freeTime = strtotime($agent->logado) - strtotime($agent->duracao);
array_push($inactiveAgents, array(
"UNIQUEID" => $agent->uniqueid,
"MATRICULA" => $agent->matricula,
"ORIGEM_DESTINO" => $agent->origem_destino,
"RAMAL" => $agent->ramal,
"STATUS" => $agent->status,
"SALA2" => $agent->sala_2,
'SALA1' => $agent->sala_1,
"FILA" => $agent->dac,
"CLASSIFICADO" => $agent->chamada_classificado,
"FREETIME" => $freeTime
));
}
return $inactiveAgents;
}
public function refreshAgent($ramal, $sala2 = null)
public function infoAgentes($media, $queue = null)
{
try {
$this->agent->begin();
$agent = $this->agent->findAgentByRamal($ramal);
if (!$agent) {
throw new Exception('Telefone não identificado!');
}
$this->agent->updateRefreshAgent($agent->matricula, $ramal);
$this->agent->updateSala2Agent($agent->matricula, $ramal, $sala2);
$this->agent->commit();
return true;
$agent = $this->agent->findAllAgentes($media, $queue);
return $agent;
} catch (Exception $ex) {
$this->agent->rollback();
$this->message($ex->getMessage());
@ -416,37 +335,15 @@ class AgentController extends Controller
return false;
}
public function agentVerify($ramal)
public function status($ramal, $status, $origemDestino = null)
{
try {
$this->agent->begin();
$agent = $this->agent->findAgentByRamal($ramal);
if (!$agent) {
throw new Exception('Telefone não identificado!');
}
$this->agent->updateRefreshAgent($agent->matricula, $ramal, true);
$this->agent->updateSala2Agent($agent->matricula, $ramal, null);
$this->agent->commit();
return true;
} catch (Exception $ex) {
$this->agent->rollback();
$this->message($ex->getMessage());
logger()->error($ex->getMessage());
}
return false;
}
public function timeFinishAgente($matricula, $ramal, $timer = null)
{
try {
$this->agent->begin();
if ($timer) {
$timerFinish = strtotime("+{$timer} minutes", time());
} else {
$timerFinish = null;
throw new Exception('Agente não conectado!');
}
$this->agent->updateSala2Agent($matricula, $ramal, $timerFinish);
$this->agent->updateAgent($agent->matricula, $ramal, strtoupper($status), $origemDestino, null, 1);
$this->agent->commit();
return true;
} catch (Exception $ex) {
@ -485,17 +382,4 @@ class AgentController extends Controller
}
return false;
}
public function listaPausas()
{
try {
$pausas = $this->pause->findAllPause(true);
return $pausas;
} catch (Exception $ex) {
$this->agent->rollback();
$this->message($ex->getMessage());
logger()->error($ex->getMessage());
return false;
}
}
}

107
app/Controllers/BilheteController.php

@ -3,113 +3,22 @@
namespace app\Controllers;
use app\Core\Controller;
use app\Models\Bilhete;
use app\Models\EventQueue;
use app\Models\SupervisorQueue;
use app\Models\Parametros;
use app\Models\Protocol;
use Exception;
class BilheteController extends Controller
{
/** @var Parametros $parametros model de Parametros */
protected $parametros;
private $supervisorqueue;
private $parametros;
private $protocol;
private $bilhete;
private $eventqueue;
/** @var Protocol $protocol model de Protocol */
protected $protocol;
public function __construct()
{
$this->supervisorqueue = new SupervisorQueue();
$this->parametros = new Parametros();
$this->protocol = new Protocol();
$this->bilhete = new Bilhete();
$this->eventqueue = new EventQueue();
}
public function bilheteInQueue($src, $duration, $billsec, $disposition, $queue, $event, $dst = 'null', $agent = null, $media)
{
try {
$this->bilhete->begin();
$uniqueid = $this->bilhete->addBilhete('now', $src, $dst, $duration, $billsec, $disposition);
$this->eventqueue->addEventQueue($uniqueid, $queue, $agent, $event, $media);
$this->bilhete->commit();
return true;
} catch (Exception $ex) {
$this->bilhete->rollback();
logger()->error($ex->getMessage());
}
return false;
}
public function bilheteForaHorario($eventos, $queue = null, $data = null, $media = null)
{
try {
$bilhetes = $this->bilhete->findBilheteByEventosDacs($queue, $data, $eventos, null, $media);
return $bilhetes;
} catch (Exception $ex) {
logger()->error($ex->getMessage());
}
return false;
}
public function atenderBilheteForaHorario($uniqueid, $ramalorigem, $queue, $agent, $newuniqueid = null)
{
try {
$this->bilhete->begin();
$this->bilhete->updateBilheteForaHorario($uniqueid, $ramalorigem, '0', 'ANSWERED');
$bilhete = $this->bilhete->findByUniqueid($uniqueid);
$tempoEspera = time() - strtotime($bilhete->calldate);
$this->eventqueue->updateEventQueue($uniqueid, $queue, $agent, CONF_EVENT_WHATSAPP_TIMERMINO_AGENTE, $tempoEspera, $newuniqueid);
$this->bilhete->commit();
} catch (Exception $ex) {
$this->bilhete->rollback();
$this->message($ex->getMessage());
logger()->error($ex->getMessage());
}
return false;
}
public function cancelarBilheteForaHorario($ramal)
{
try {
$this->bilhete->begin();
$bilhetesForaHorario = $this->bilhete->findBilheteBySrc($ramal, CONF_EVENT_WHATSAPP_ESPERA);
if (!$bilhetesForaHorario) {
throw new Exception('Não foram encontrados atendimentos em espera!');
}
$this->bilhete->updateBilheteForaHorario($bilhetesForaHorario->uniqueid, $ramal, '0', CONF_EVENT_WHATSAPP_ABANDONADA);
$this->eventqueue->updateEventQueue($bilhetesForaHorario->uniqueid, $bilhetesForaHorario->fila, $bilhetesForaHorario->agente, CONF_EVENT_WHATSAPP_ABANDONADA);
$this->bilhete->commit();
return true;
} catch (Exception $ex) {
$this->bilhete->rollback();
$this->message($ex->getMessage());
logger()->error($ex->getMessage());
}
return false;
}
public function tempoEsperaSupervisor($uniqueid)
{
try {
$this->supervisorqueue->begin();
$queue = $this->eventqueue->findEventQueueByParam2($uniqueid);
if ($queue) {
$this->supervisorqueue->updateEsperaSupervisorQueue($queue->fila, $queue->param1);
}
$this->supervisorqueue->commit();
return true;
} catch (Exception $ex) {
$this->supervisorqueue->rollback();
logger()->error($ex->getMessage());
}
$this->supervisorqueue->rollback();
return false;
}
public function generateProtocol($uniqueid)
@ -136,20 +45,18 @@ class BilheteController extends Controller
{
try {
$this->protocol->begin();
$param = $this->parametros->findProtocolByParams($uniqueid);
$useProtocolo = $param->prm_agente_proto;
$useProtoParceiro = $param->prm_use_proto_parceiro;
if (!$useProtocolo) {
$this->protocol->rollback();
return '99';
}
$numProto = $this->protocol->findProtocol($uniqueid);
if ($numProto) {
$numProto = $numProto->protocolo;
$numProtoParceiro = trim($numProto->protoparceiro);
$this->protocol->rollback();
if ($useProtoParceiro && $numProtoParceiro) {
return $numProtoParceiro;
}
@ -163,11 +70,11 @@ class BilheteController extends Controller
if ($result) {
$numProto = $result->protocolo;
$numProto = $numProto ? $numProto + 1 : 1;
$proto = $year . str_pad($numProto, 6, '0', STR_PAD_LEFT);
$protoAgt = $year . '-' . str_pad($numProto, 6, '0', STR_PAD_LEFT);
$protoParceiro = $protoParceiro && $useProtoParceiro ? $protoParceiro : 'null';
$this->protocol->insert($uniqueid, $year, $numProto, $proto, 'null');
$this->protocol->commit();
return $protoParceiro !== 'null' ? $protoParceiro : $protoAgt;
}

14
app/Controllers/ClassificacaoController.php

@ -5,17 +5,19 @@ namespace app\Controllers;
use app\Core\Controller;
use app\Models\EventQueue;
use app\Models\Agent;
use app\Models\Bilhete;
use app\Models\Classificacao;
use Exception;
class ClassificacaoController extends Controller
{
/** @var Agent $agent model de Agent */
protected $agent;
/** @var EventQueue $eventqueue model de EventQueue */
protected $eventqueue;
private $agent;
private $eventqueue;
private $classificacao;
/** @var Classificacao $classificacao model de Classificacao */
protected $classificacao;
public function __construct()
{
@ -146,4 +148,4 @@ class ClassificacaoController extends Controller
}
return $pendenteClassificacao;
}
}
}

12
app/Controllers/ClientController.php

@ -3,7 +3,7 @@
namespace app\Controllers;
use app\Core\Controller;
use app\Controllers\BilheteController;
use app\Models\Atendimento;
/**
* Description of ClientController
@ -13,16 +13,16 @@ use app\Controllers\BilheteController;
class ClientController extends Controller
{
public function getClientQueueData($number)
public function getClientQueueData($number, $fila)
{
$bilheteController = new BilheteController();
$atendimentoFila = $bilheteController->bilheteForaHorario(CONF_EVENT_WHATSAPP_ESPERA);
$atendimento = new Atendimento();
$atendimentoFila = $atendimento->getAtendimentoByEvento($fila);
$clientInQueue = false;
$queuePosition = 0;
$clientQueue = '';
foreach ($atendimentoFila as $clientes) {
if ($clientes->src == $number) {
if ($clientes->cliente_id == $number) {
$clientInQueue = true;
$clientQueue = $clientes->fila;
break;
@ -35,7 +35,7 @@ class ClientController extends Controller
foreach ($queueClientData as $clientes) {
$queuePosition += 1;
if ($clientes->src == $number) {
if ($clientes->cliente_id == $number) {
break;
}
}

28
app/Controllers/MessageController.php

@ -14,7 +14,8 @@ use Exception;
*/
class MessageController extends Controller
{
private $message;
/** @var Message $message model de Message */
protected $message;
public function __construct()
{
@ -27,23 +28,22 @@ class MessageController extends Controller
* @param type $timer
* @return boolean
*/
public function timeoutTalk($uniqueid, $ramal)
public function timeoutTalk($uniqueid, $cliente)
{
try {
$message = $this->message->findLastMessage($uniqueid);
if ($message->src != $ramal) {
return null;
if ($cliente == $message->dst) {
if (strtotime($message->msg_date . '+' . CONF_WHATSAPP_TIMEOUT_CLIENT_RESPOSTA . ' seconds') < time()) {
print('FINISH');
return "FINISH";
}
$timealert = strtotime($message->msg_date . '+' . (CONF_WHATSAPP_TIMEOUT_CLIENT_RESPOSTA - 60) . ' seconds');
if ($timealert < strtotime(date('Y-m-d H:i:s'))) {
print('alerta');
return "ALERT";
}
}
if (strtotime($message->msg_date . '+' . CONF_WHATSAPP_TIMEOUT_CLIENT_RESPOSTA . ' seconds') < time()) {
return "FINISH";
}
$timealert = strtotime($message->msg_date . '+' . (CONF_WHATSAPP_TIMEOUT_CLIENT_RESPOSTA - 60) . ' seconds');
if ($timealert < strtotime(date('Y-m-d H:i:s'))) {
return "ALERT";
}
return false;
} catch (Exception $ex) {
$this->message->rollback();

34
app/Controllers/QueueController.php

@ -7,7 +7,6 @@ use app\Models\Queue;
use app\Models\Agent;
use app\Models\Bilhete;
use app\Controllers\ClientController;
use app\Core\Commands;
use Exception;
@ -26,11 +25,14 @@ use Exception;
*/
class QueueController extends Controller
{
/** @var Queue $queue model de Queue */
protected $queue;
private $queue;
private $agent;
private $bilhete;
private $commands;
/** @var Agent $agent model de Agent */
protected $agent;
/** @var Bilhete $bilhete model de Bilhete */
protected $bilhete;
public function __construct()
{
@ -76,11 +78,11 @@ class QueueController extends Controller
return $this->queue->findAllQueue();
}
public function clientQueueVerify($number)
public function clientQueueVerify($number, $fila)
{
$client = new ClientController();
$clienteFila = $client->getClientQueueData($number);
$clienteFila = $client->getClientQueueData($number, $fila);
$clienteEmFila = $clienteFila['IN_QUEUE'];
$posicaoFila = $clienteFila['QUEUE_POSITION'];
@ -92,24 +94,6 @@ class QueueController extends Controller
}
return false;
}
/**
* Busca todos os agentes conectados em uma determinada fila.
* @param type $queue
* @return type
*/
private function ringall($queue)
{
$agents = $this->agent->findAgentByQueue($queue, CONF_AGENT_STATUS_LIVRE);
if (!$agents) {
return null;
}
$data = array();
foreach ($agents as $agent) {
$data[] = $agent->ramal;
}
return $data;
}
/**
* Busca o agente com o menor tempo de atendimento.

49
app/Controllers/SystemMessageController.php

@ -0,0 +1,49 @@
<?php
namespace app\Controllers;
use app\Core\Controller;
use app\Interfaces\IApiMedia;
use app\Models\SystemMessage;
use Exception;
/**
* Description of SupervisorController
*
* @author root
*/
class SystemMessageController extends Controller
{
/** @var SystemMessage $sysMessage model de mensagens do sistema */
protected $sysMessage;
public function __construct()
{
$this->sysMessage = new SystemMessage();
}
public function sendMessageSystem($momento, $variavels, IApiMedia $api, $numero, $fila = null)
{ //$variavels = [["nome" => '@cliente', "valor" => 'afonso']]
try {
$msgs = $this->sysMessage->findMessage($momento, $fila);
if (empty($msgs)) {
$msgs = $this->sysMessage->findMessage($momento);
}
foreach ($msgs as $key => $msg) {
$msg->texto = str_replace('\n', "\n", $msg->texto);
if ($variavels) {
foreach ($variavels as $key => $variavel) {
$vari = $variavel['nome'];
$pattern = "/$vari/i";
$msg->texto = preg_replace($pattern, utf8_decode($variavel['valor']), $msg->texto);
}
}
$api->enviarMsg($numero, $msg->texto);
}
return $msgs;
} catch (Exception $ex) {
logger()->error($ex->getMessage());
return false;
}
}
}

417
app/Core/Commands.php

@ -4,6 +4,14 @@ namespace app\Core;
use app\Controllers\AgentController;
use app\Controllers\BilheteController;
use app\Controllers\QueueController;
use app\Controllers\SystemMessageController;
use app\Interfaces\IApiMedia;
use app\Models\Atendimento;
use app\Models\Evento;
use app\Models\Message;
use app\Models\SupervisorModel;
use websocket\WsInterface;
/**
*
@ -11,340 +19,151 @@ use app\Controllers\BilheteController;
*/
class Commands
{
/*
* Permissoes
* 0 - CLIENTE & AGENTE
* 1 - AGENTE
*/
/** @var IApiMedia $api provider de mensagens */
protected $api;
const PERMISSION = "PERMISSION";
const DESCRIPTION = "DESCRIPTION";
/** @var SystemMessageController $systemController controller de mensagens de sistemas */
protected $systemController;
private $personType;
private $callback;
private $bilheteController;
private $agente;
static $instance;
public $commandsConfig = array();
/** @var BilheteController $bilheteController controller de billhete */
protected $bilheteController;
/** @var Atendimento $atendimentoModel model de atendimento*/
protected $atendimentoModel;
/** @var Evento $eventosModel model de eventos */
protected $eventosModel;
/** @var QueueController $queue controller da queue*/
protected $queue;
public function __construct()
{
$this->queue = new QueueController();
$this->eventosModel = new Evento();
$this->atendimentoModel = new Atendimento();
$this->agente = new AgentController();
$this->bilheteController = new BilheteController();
$this->commandsConfig = [
//"LOGIN" => [self::PERMISSION => 1, self::DESCRIPTION => "Realizar login no sistema, informando a fila e o usu�rio."],
"ENTRAR" => [
self::PERMISSION => ["MIN" => 1, "MAX" => 1],
self::DESCRIPTION => "Realizar login no sistema, informando a fila e o usuário."
],
//"LOGOFF" => [self::PERMISSION => 1, self::DESCRIPTION => "Desconcetar do sistema, digitar o comando logoff. Ex. /logoff."],
"SAIR" => [
self::PERMISSION => ["MIN" => 1, "MAX" => 1],
self::DESCRIPTION => "Desconectar do sistema."
],
"PAUSA" => [
self::PERMISSION => ["MIN" => 1, "MAX" => 1],
self::DESCRIPTION => "Entrar em pausa, passar o nome da pausa."
],
"SAIRPAUSA" => [
self::PERMISSION => ["MIN" => 1, "MAX" => 1],
self::DESCRIPTION => "Finaliza o tempo em pausa."
],
"TRANSFERIR" => [
self::PERMISSION => ["MIN" => 1, "MAX" => 1],
self::DESCRIPTION => "Transferir o atendimento para outro atendente passando o ramal."
],
//"ENCERRAR" => [self::PERMISSION => 0, self::DESCRIPTION => "Encerrar o atendimento, digitar o comando encerrar. Ex. /encerrar"],
"STATUS" => [
self::PERMISSION => ["MIN" => 1, "MAX" => 1],
self::DESCRIPTION => "Verificar o status dos agente, digitar o comando status."
],
"PRESENTE" => [
self::PERMISSION => ["MIN" => 1, "MAX" => 1],
self::DESCRIPTION => "Atuliza informações do atendente."
],
"COMANDOS" => [
self::PERMISSION => ["MIN" => 0, "MAX" => 1],
self::DESCRIPTION => "Listar comandos disponíveis."
],
"CANCELAR" => [
self::PERMISSION => ["MIN" => 0, "MAX" => 0],
self::DESCRIPTION => "Cancelar posição na fila de atendimento."
],
"FINALIZAR" => [
self::PERMISSION => ["MIN" => 0, "MAX" => 1],
self::DESCRIPTION => "Encerrar o atendimento."
]
];
$this->systemController = new SystemMessageController();
}
private function permission($method)
public function setApi(IApiMedia $api)
{
return $this->commandsConfig[strtoupper($method)][self::PERMISSION] ? $this->commandsConfig[strtoupper($method)][self::PERMISSION] : ["MIX" => 0, "MAX" => 1];
$this->api = $api;
}
/*
* Verificar se é um comando valido
* @param string $phone
* @param string $message
*
* return string
*/
public function isCommand($phone, $message, $type, $media)
public function isCommand(IApiMedia $api)
{
$this->personType = $this->agente->isAgent($phone);
$messageParams = explode(" ", $message);
$this->api = $api;
$messageParams = explode(" ", $api->getMessage());
$command = $messageParams[0];
switch (strtoupper($command)) {
case '/ENTRAR':
case '/E':
return $this->entrar($phone, trim($messageParams[1]), trim($messageParams[2]), $media);
case '/SAIR':
return $this->sair($phone);
case '/PAUSA':
case '/P':
return $this->pausa($phone, trim($messageParams[1]));
case '/SAIRPAUSA':
return $this->sairPausa($phone);
case '/FINALIZAR':
return $this->finalizar($phone, $media);
case '/TRANSFERIR':
case '/T':
return $this->transferir($phone, trim($messageParams[1]));
case '/PRESENTE':
return $this->presente($phone);
case '/STATUS':
return $this->status($media);
case '/COMANDOS':
return $this->comandos();
return $this->finalizar($api->getPhone());
case '/CANCELAR':
return $this->cancelar($phone);
return $this->cancelar($api->getPhone());
default:
$this->callback = CONF_NAME_REPONSE . " : Comando não existente!";
return "NONE";
}
}
private function pausa($ramal, $pause)
{
if ($this->isPermitted($this->personType, $this->permission(__FUNCTION__))) {
$response = $this->agente->enterPause($ramal, $pause);
if ($response) {
$this->callback = CONF_NAME_REPONSE . " : Agente foi adicionado em pausa!";
return true;
} else {
if ($this->agente->message() == 'Pausa não encontrada!') {
return 'PAUSA';
}
$this->callback = CONF_NAME_REPONSE . " : Não foi possível adicionar a pausa! " . $this->agente->message() . "\n";
return false;
}
} else {
$this->callback = CONF_NAME_REPONSE . " : Comando não permitido!";
return false;
}
}
private function entrar($ramal, $queue, $login, $media)
{
if ($this->isPermitted($this->personType, $this->permission(__FUNCTION__))) {
$response = $this->agente->login($ramal, $login, $queue, $media);
if ($response) {
$this->callback = CONF_NAME_REPONSE . " : Login realizado com sucesso!\n";
$this->callback .= "Você se conectou na fila *{$queue}*.";
return true;
} else {
if ($this->agente->message() == 'Fila não encontrada ou não relacionada como WhatsApp!') {
return 'ENTRAR';
}
$this->callback = CONF_NAME_REPONSE . " : Não foi possível realizado o login! " . $this->agente->message();
return false;
}
} else {
$this->callback = CONF_NAME_REPONSE . " : Comando não permitido!";
return false;
}
}
private function sair($ramal)
{
if ($this->isPermitted($this->personType, $this->permission(__FUNCTION__))) {
$response = $this->agente->logoff($ramal);
if ($response) {
$this->callback = CONF_NAME_REPONSE . " : Logoff realizado com sucesso!";
return true;
} else {
$this->callback = CONF_NAME_REPONSE . " : Não foi possível realizado o logoff! " . $this->agente->message();
return false;
}
} else {
$this->callback = CONF_NAME_REPONSE . " : Comando não permitido!";
return false;
}
}
private function sairPausa($ramal)
public function finalizar($numero)
{
if ($this->isPermitted($this->personType, $this->permission(__FUNCTION__))) {
$response = $this->agente->exitPause($ramal);
if ($response) {
$this->callback = CONF_NAME_REPONSE . " : Agente foi removido em pausa!";
try {
$this->atendimentoModel->begin();
$supervisorModel = new SupervisorModel();
$atendimento = $this->atendimentoModel->getAtendimentoByCliente($numero, CONF_EVENT_START);
//verifica se existe atendimento
if (empty($atendimento)) {
$this->atendimentoModel->rollback();
$this->api->enviarMsg($numero, CONF_NAME_REPONSE . " : Não foi encontrado atendimento em aberto");
return true;
} else {
$this->callback = CONF_NAME_REPONSE . " : Não foi possível remover de pausa! " . $this->agente->message();
return false;
}
} else {
$this->callback = CONF_NAME_REPONSE . " : Comando não permitido!";
return false;
}
}
private function transferir($ramal, $traferir)
{
if ($this->isPermitted($this->personType, $this->permission(__FUNCTION__))) {
$response = $this->agente->transfer($ramal, $traferir);
if ($response) {
$this->callback = CONF_NAME_REPONSE . " : Atendimento foi transferido!";
return $response;
} else {
if ('Agente indisponível para atendimento!' === $this->agente->message()) {
return 'TRANSFERIR';
//cria o evento pra finalizar
$ret = $this->eventosModel->createEvento(
$atendimento->uniqueid,
CONF_EVENT_TIMERMINO_CLIENTE,
date('Y-m-d H:i:s'),
date('Y-m-d H:i:s'),
$atendimento->fila
);
if ($ret) {
$ws = new WsInterface();
$agente = $supervisorModel->findAgentByMatricula($atendimento->matricula);
$messegeModel = new Message();
$messegeModel->addMessage(
$atendimento->uniqueid,
$numero,
$agente->matricula,
'finish',
'Atendimento finalizado',
$atendimento->nome,
$atendimento->context,
'read'
);
//verifica se está como indisponivel e caso não tenha atendimento ele entra em pausa
if ($agente->status == CONF_AGENT_STATUS_INDISPONIVEL) {
$atends = $this->atendimentoModel->getAtendimentoAbertoByAgente($atendimento->matricula);
if (empty($atends)) {
$agente->enterPause($atendimento->matricula, $agente->motivo_pausa);
}
}
$this->callback = CONF_NAME_REPONSE . " : Não foi possível transferir seu atendimento! " . $this->agente->message();
return false;
}
} else {
$this->callback = CONF_NAME_REPONSE . " : Comando não permitido!";
return false;
}
}
private function status($sala)
{
if ($this->isPermitted($this->personType, $this->permission(__FUNCTION__))) {
$response = $this->agente->infoAgentes($sala);
if ($response) {
$agentesStatus = '';
foreach ($response as $agente) {
$agentesStatus .= "Nome: " . $agente->nome . " Ramal: " . $agente->ramal . " Status: " . $agente->status . " Fila: " . $agente->dac . "\n\n";
//coloca o agente como livre caso esteja ocupado e com menos atendimento que o maximo de atendimento
if ($agente->status == CONF_AGENT_STATUS_OCUPADO) {
$param = $this->atendimentoModel->getQuantiAtendimentSimultaneos();
$atendimentosAbertos = $this->atendimentoModel->getAtendimentoAbertoByAgente($agente->matricula);
if (count($atendimentosAbertos) < $param->prm_media_simultaneo) {
$supervisorModel->updateAgent($agente->matricula, CONF_AGENT_STATUS_LIVRE);
}
}
$this->callback = $agentesStatus;
//envia as mensagens do sistema do momento atual para o cliente
$this->systemController->sendMessageSystem(
CONF_MOMENT_FINALIZAR_ATENDIMENTO,
[],
$this->api,
$numero
);
$this->atendimentoModel->commit();
//notifica o agente da mudança
$ws->enviaMsg($ws->enviaActions('Atendimento finalizado', 'finish', $agente->matricula, $atendimento->uniqueid));
return true;
} else {
$this->callback = CONF_NAME_REPONSE . " : Não foi possível listar os status! " . $this->agente->message();
return false;
}
} else {
$this->callback = CONF_NAME_REPONSE . " : Comando não permitido!";
return false;
}
}
private function finalizar($numero, $media)
{
if ($this->isPermitted($this->personType, $this->permission(__FUNCTION__))) {
$service = $this->agente->closeService($numero, null, $media);
if ($service) {
$this->callback = CONF_NAME_REPONSE . " : Atendimento finalizado!";
return $service;
} else {
$this->callback = CONF_NAME_REPONSE . " : Não foi possível finalizar o atendimento!";
return false;
}
} else {
$this->callback = CONF_NAME_REPONSE . " : Comando não permitido!";
return false;
}
}
private function presente($ramal)
{
if ($this->isPermitted($this->personType, $this->permission(__FUNCTION__))) {
$refresh = $this->agente->agentVerify($ramal);
if ($refresh) {
$this->callback = CONF_NAME_REPONSE . " : Agente presente!";
$this->atendimentoModel->rollback();
$this->api->enviarMsg($numero, CONF_NAME_REPONSE . " : Erro ao finalizar!");
return true;
} else {
$this->callback = CONF_NAME_REPONSE . " : Não foi possível confirmar presença!";
return false;
}
} else {
$this->callback = CONF_NAME_REPONSE . " : Comando não permitido!";
return false;
} catch (\Exception $th) {
$this->atendimentoModel->rollback();
$this->api->enviarMsg($numero, CONF_NAME_REPONSE . " : " . $th->getMessage());
return true;
}
}
private function comandos()
{
if ($this->isPermitted($this->personType, $this->permission(__FUNCTION__))) {
$commands = $this->listCommands();
if ($commands) {
return 'COMANDOS';
} else {
$this->callback = CONF_NAME_REPONSE . " : Não foi possível listar os comandos!";
return false;
private function cancelar($numero)
{
$atendimento = $this->atendimentoModel->getAtendimentoByCliente($numero, CONF_EVENT_ESPERA);
$ret = $this->eventosModel->createEvento(
$atendimento->uniqueid,
CONF_EVENT_ABANDONADA,
date('Y-m-d H:i:s'),
date('Y-m-d H:i:s'),
$atendimento->fila
);
if ($ret) {
//envia as mensagens do sistema do momento atual para o cliente
$this->systemController->sendMessageSystem(
CONF_MOMENT_CANCELAR_FILA,
[],
$this->api,
$numero
);
$filas = $this->queue->listAllQueueWhatsApp($this->api->getMessage());
if ($filas['LIST']) {
$this->api->enviarMsg($this->api->getPhone(), $filas['LIST']);
}
return true;
} else {
$this->callback = CONF_NAME_REPONSE . " : Comando não permitido!";
return false;
}
}
private function cancelar($ramal)
{
if ($this->isPermitted($this->personType, $this->permission(__FUNCTION__))) {
$bilheteCancelado = $this->bilheteController->cancelarBilheteForaHorario($ramal);
if ($bilheteCancelado) {
$this->callback = CONF_NAME_REPONSE . " : Cancelado o atendimento!";
return true;
} else {
$this->callback = CONF_NAME_REPONSE . " : Não foi possível cancelar o atendimento! " . $this->bilheteController->message();
return false;
}
} else {
$this->callback = CONF_NAME_REPONSE . " : Comando não permitido!";
return false;
}
}
function getCallback()
{
return $this->callback;
}
public function listCommands($validPersonType = false)
{
$commands = array();
$commandsConfig = $this->commandsConfig;
if ($validPersonType) {
$commandsConfig = array_filter($commandsConfig, function ($permisson) {
return $this->isPermitted($this->personType, $permisson[self::PERMISSION]);
});
}
foreach ($commandsConfig as $command => $config) {
$commandFormated = "/" . strtolower($command);
array_push($commands, ['title' => substr($commandFormated, 0, 23), 'sub' => $config[self::DESCRIPTION]]);
}
return $commands;
}
public function isPermitted($personType, $permisson)
{
return (($personType >= $permisson["MIN"]) && ($personType <= $permisson["MAX"]));
}
public function listaPausas()
{
$pausas = $this->agente->listaPausas();
$listaPausas = [];
foreach ($pausas as $key => $value) {
$value->motivo = strtolower($value->motivo);
array_push($listaPausas, ['title' => ucfirst(substr($value->motivo, 0, 23)), 'sub' => "/P {$value->motivo}"]);
$this->api->enviarMsg($numero, CONF_NAME_REPONSE . " : Não foi possível cancelar o atendimento! " . $this->bilheteController->message());
return true;
}
return $listaPausas;
}
}

22
app/Core/Connect.php

@ -27,25 +27,13 @@ class Connect
}
public static function getInstance()
{
$credentials = [];
$cd = [];
if (empty(self::$instance)) {
if (CONF_DB_DRIVER && CONF_DB_HOST && CONF_DB_BASE && CONF_DB_PORT && CONF_DB_USER && CONF_DB_PASSWD) {
$credentials['host_db'] = CONF_DB_HOST;
$credentials['porta_db'] = CONF_DB_PORT;
$credentials['usuario'] = CONF_DB_USER;
$credentials['senha'] = CONF_DB_PASSWD;
$credentials['base_db'] = CONF_DB_BASE;
} else {
$credentials = self::filedb();
}
$cd = self::filedb();
self::$instance = new PDO(
CONF_DB_DRIVER .
":host=" . $credentials['host_db'] .
";port=" . $credentials['porta_db'] .
";dbname=" . $credentials['base_db'],
$credentials['usuario'],
$credentials['senha'],
"pgsql:host={$cd['host_db']};port={$cd['porta_db']};dbname={$cd['base_db']}",
$cd['usuario'],
$cd['senha'],
[
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_OBJ,

41
app/Core/Controller.php

@ -1,23 +1,24 @@
<?php
namespace app\Core;
use app\Providers\Logger;
/**
* Description of Controller
*
* @author Lucas Awade
*/
abstract class Controller {
const LOGGER_ACTIVE = true;
private $message;
public function message($message = null){
if(!$message){
return $this->message;
}
$this->message = $message;
namespace app\Core;
/**
* Description of Controller
*
* @author Lucas Awade
*/
abstract class Controller
{
const LOGGER_ACTIVE = true;
private $message;
public function message($message = null)
{
if (!$message) {
return $this->message;
}
}
$this->message = $message;
}
}

417
app/Core/CoreMedia.php

@ -3,13 +3,18 @@
namespace app\Core;
use app\Core\Commands;
use app\Core\Media;
use app\Models\Message;
use app\Providers\Crypt;
use app\Controllers\QueueController;
use app\Controllers\AgentController;
use app\Controllers\BilheteController;
use app\Controllers\SystemMessageController;
use app\Interfaces\IApiMedia;
use app\Models\Atendimento;
use app\Models\Evento;
use app\Models\ListaNegraPalavras;
use app\Models\Ramal;
use app\Models\SupervisorModel;
use websocket\WsInterface;
/**
* Description of WhatsApp
@ -18,114 +23,117 @@ use app\Models\Ramal;
*/
class CoreMedia
{
private $api;
private $crypt;
private $file;
private $host;
private $request;
private $commands;
private $media;
private $queue;
private $agente;
private $palavroes;
/** @var IApiMedia $api api do provedor de mensagens */
protected $api;
/** @var Commands $commands classe de comandos do sistema*/
protected $commands;
/** @var QueueController $queue controller da queue*/
protected $queue;
/** @var AgentController $agente controller do agente*/
protected $agente;
/** @var ListaNegraPalavras $palavroes model de palavras banida*/
protected $palavroes;
/** @var WsInterface $ws classe de comandos do sistema*/
protected $ws;
/** @var Atendimento $atendimento model de atendimentos*/
protected $atendimento;
/** @var SupervisorModel $supervisor model de supervisor*/
protected $supervisor;
/** @var Evento $eventos model de eventos*/
protected $eventos;
/** @var BilheteController $eventos controller dos bilhetess*/
protected $bilheteController;
/** @var Message $message model de mensagens*/
protected $message;
/** @var SystemMessageController $systemController controller de mensagens de sistema*/
protected $systemController;
/** @var Crypt $crypt core de criptografia*/
protected $crypt;
public function __construct()
{
$this->media = new Media();
$this->ws = new WsInterface();
$this->queue = new QueueController();
$this->commands = new Commands();
$this->agente = new AgentController();
$this->crypt = new Crypt();
$this->atendimento = new Atendimento();
$this->message = new Message();
$this->palavroes = new ListaNegraPalavras();
$this->supervisor = new SupervisorModel();
$this->eventos = new Evento();
$this->bilheteController = new BilheteController;
$this->systemController = new SystemMessageController();
}
public function setApi(IApiMedia $api)
{
$this->api = $api;
}
/**
* Send the file to the active contact
*
* @return null
*/
public function inicia($data, $api)
public function inicia($data, IApiMedia $providere)
{
/** Validate $data */
if (!$this->build($data)) {
return false;
}
$this->media->media = $api->channel;
$this->api = $api;
$this->api->setHook($this->request);
if ($this->file) {
$file = $this->api->baixarMidia($this->file);
return $file;
}
$this->api = $providere;
$this->build($data);
/**
* VERIFICA SE N?O MENSAGEM DO POSITUS DE CONFIRMA??O
*/
if (!$this->api->getIsValidMessage()) {
return null;
}
/**
* VERIFICA SE ? UM COMANDO
*/
$command = $this->commands->isCommand($this->api->getPhone(), $this->api->getMessage(), $this->api->getType(), $this->api->channel);
if ((str_split($this->api->getMessage())[0] == '/') && ($command == "NONE")) {
$this->api->enviarMsg($this->api->getPhone(), utf8_encode($this->commands->getCallback()));
if (!$this->api->baixarMidia()) {
$this->api->enviarMsg($this->api->getPhone(), "N<EFBFBD>o foi possivel entregar o aquivo para o agente. Envie novamente!");
}
// VERIFICA SE ? UM COMANDO
if ($this->commands->isCommand($this->api)) {
return null;
}
switch ($command) {
case 'COMANDOS':
$this->enviaLista($this->commands->listCommands(true), $this->api, $this->api->getPhone(), 'Comandos', 'C');
break;
case 'PAUSA':
$this->enviaLista($this->commands->listaPausas(), $this->api, $this->api->getPhone(), 'Pausas', 'P');
break;
case 'ENTRAR':
$this->enviaLista($this->listaFilas(), $this->api, $this->api->getPhone(), "Filas", 'P');
break;
case 'TRANSFERIR':
$agentes = $this->listaAgentesDisponivel();
if ($agentes) {
$this->enviaLista($agentes, $this->api, $this->api->getPhone(), "Transferência", 'P');
} else {
$this->api->enviarMsg($this->api->getPhone(), utf8_encode("Nenhum agente disponível para transferência."));
}
break;
default:
break;
//verifica se tem atendimento em aberto, se tiver ja manda msg para o agente via ws
$atendiment = $this->atendimento->findAtenEmAberto($this->api->getPhone());
if ($atendiment) {
$this->enviaMensagemAgente($atendiment);
return null;
}
if ($command !== "NONE") {
if (is_array($command)) {
foreach ($command as $val) {
$this->api->enviarMsg($val, utf8_encode($this->commands->getCallback()));
}
} else {
$this->api->enviarMsg($this->api->getPhone(), utf8_encode($this->commands->getCallback()));
}
//verifica se tem atendimento em espera, se tiver ja mostra a sua posi<EFBFBD><EFBFBD>o na fila
$atende = $this->atendimento->getAtendimentoByCliente($this->api->getPhone());
if (empty($atende)) {
$atende = $this->atendimento->getAtendimentoByCliente($this->api->getPhone(), CONF_EVENT_ERRO_ATEND);
}
if ($atende) {
$this->mostraPosiscaoNaFila($this->api->getPhone(), $atende->fila);
return null;
}
$isTalk = $this->agente->inService($this->api->getPhone());
if (!$isTalk && !$this->agente->isAgent($this->api->getPhone())) {
$clientfila = $this->queue->clientQueueVerify($this->api->getPhone());
if ($clientfila['MESSAGE']) {
$this->api->enviarMsg($this->api->getPhone(), utf8_encode($clientfila['MESSAGE']));
return null;
}
$filas = $this->queue->listAllQueueWhatsApp($this->api->getMessage());
if ($filas['LIST']) {
$this->api->enviarMsg($this->api->getPhone(), utf8_encode($filas['LIST']));
return null;
}
//verifica se o cliente escolheu uma fila caso n<EFBFBD>o escolheu manda as filas
$filas = $this->queue->listAllQueueWhatsApp($this->api->getMessage());
if ($filas['LIST']) {
//manda as mensagens personalizada do moment CONF_MOMENT_SAUDACAO
$this->systemController->sendMessageSystem(
CONF_MOMENT_SAUDACAO,
[["nome" => "@cliente_name", "valor" => $this->api->getProfile()]],
$this->api,
$this->api->getPhone()
);
$this->api->enviarMsg($this->api->getPhone(), $filas['LIST']);
return null;
}
/**
* VALIDA O ATENDIMENTO
*/
$this->validaAtendimento($this->api, $filas['QUEUE'], $data, $this->api->getPhone(), $this->api->channel);
// cria a atendimento
$this->criaAtendimento($filas['QUEUE'], $this->api->getPhone());
}
/**
* Set variables class
* @param array $data
@ -133,135 +141,188 @@ class CoreMedia
*/
public function build($data)
{
$this->file = $data['DOWNLOAD']["FILE"];
if ($data['REQUEST']['chat']['type'] == 'group' || $data['REQUEST']['from']['is_bot'] != '') {
return false;
}
if ($data['HOST'] && $data['REQUEST']) {
$this->host = $data['HOST'];
$this->request = $data['REQUEST'];
$this->validaPalavroes();
return true;
} else if ($this->file) {
return true;
}
return false;
$this->api->setHook($data);
$this->validaPalavroes();
}
public function validaPalavroes()
{
try {
if ($this->request['text']) {
$msg = $this->request['text'];
} else {
$msg = $this->request['messages'][0]['text']['body'];
}
$msg = $this->api->getMessage();
$palavras = $this->palavroes->getAll();
foreach ($palavras as $key => $value) {
$pattern = "/\b($value->palavra)\b/i";
$msg = preg_replace($pattern, '*' . str_repeat('*', strlen($value->palavra)) . '*', $msg);
}
if ($this->request['text']) {
$this->request['text'] = $msg;
} else {
$this->request['messages'][0]['text']['body'] = $msg;
}
} catch (\Throwable $th) {
logger('telegram')->info(print_r($th, true), true);
$this->api->setMessage($msg);
} catch (\Exception $th) {
}
return;
}
public function validaAtendimento($api, $fila, $data, $numero, $media)
public function criaAtendimento($fila, $numero, $uniqueid = null)
{
$response = $this->media->selecionaAtendimento($numero, $fila, $api->getMessage(), $media);
if (!$response['STATUS']) {
$api->enviarMsg($numero, utf8_encode($response['MESSAGE']));
return null;
try {
if (empty($uniqueid)) {
$uniqueid = $this->atendimento->createAtendimento(null, $numero, 'E', $this->api->getchannel(), $this->api->getProfile());
if ($uniqueid) {
$this->eventos->createEvento($uniqueid, 'EMESPERA', date('Y-m-d H:i:s'), date('Y-m-d H:i:s'), $fila);
}
}
$agentes = $this->agente->infoAgentes($fila);
if (empty($agentes)) {
$this->systemController->sendMessageSystem(
CONF_MOMENT_ENTRAR_FILA_SEM,
[],
$this->api,
$numero,
$fila
);
return null;
}
$agent = $this->supervisor->findAgentByQueue($fila, 'LIVRE');
if (empty($agent)) {
$this->systemController->sendMessageSystem(
CONF_MOMENT_ENTRAR_FILA_COM,
[],
$this->api,
$numero,
$fila
);
$this->mostraPosiscaoNaFila($numero, $fila);
return null;
}
$retServe = $this->atendimento->updAtendimento($uniqueid, $agent[0]->matricula);
if (empty($retServe)) {
$this->api->enviarMsg(
$numero,
'Erro ao gerar atendimento!'
);
return null;
}
$protocol = $this->bilheteController->generateProtocol($uniqueid);
//cria o evento de inicio de atendimento e criar o protocolo
if ($protocol) {
$event = $this->eventos->getStatusAtendimento($uniqueid);
$retCria = $this->eventos->createEvento(
$uniqueid,
CONF_EVENT_START,
date('Y-m-d H:i:s'),
date('Y-m-d H:i:s'),
$fila,
$agent[0]->matricula
);
if (!empty($retCria) && $event->evento != CONF_EVENT_START) {
$atendimentosAbertos = $this->atendimento->getAtendimentoAbertoByAgente($agent[0]->matricula);
$sup = new SupervisorModel();
$param = $this->atendimento->getQuantiAtendimentSimultaneos();
$ws = new WsInterface();
if ((count($atendimentosAbertos)) >= $param->prm_media_simultaneo) {
$sup->updateAgent($agent[0]->matricula, CONF_AGENT_STATUS_OCUPADO);
}
$this->systemController->sendMessageSystem(
CONF_MOMENT_INICIAR_ATENDIMENTO,
[["nome" => "@agente_name", "valor" => utf8_encode($agent[0]->nome)]],
$this->api,
$numero,
$fila
);
$this->api->enviarMsg(
$numero,
"N<EFBFBD>mero do protocolo do atendimento <EFBFBD> $protocol\n"
);
try {
if ($event->evento == CONF_EVENT_ERRO_ATEND) {
$ws->enviaMsg($ws->enviaActions(
'Este atendimento foi realocado para sua responsabilidade.',
're_start',
$agent[0]->matricula,
$uniqueid
));
$this->message->addMessage(
$uniqueid,
$agent[0]->matricula,
$agent[0]->matricula,
're_start',
'Este atendimento foi realocado para sua responsabilidade.',
$agent[0]->nome,
$this->api->getchannel(),
'read'
);
} else {
$ws->enviaMsg($ws->enviaActions('Atendimento iniciado!', 'start', $agent[0]->matricula, $uniqueid));
}
} catch (\Exception $th) {
return $ws->enviaActions('Atendimento iniciado!', 'start', $agent[0]->matricula, $uniqueid);
}
}
}
return;
} catch (\Exception $th) {
logger('criaAtendimento')->info($th->getMessage());
return;
}
}
if ($response['MESSAGE']) {
$api->enviarMsg($numero, utf8_encode($response['MESSAGE']));
$this->enviaLista($this->commands->listCommands(true), $api, $numero, 'Comandos', 'C');
$api->enviarMsg($numero, utf8_encode("O tempo de ociosidade é de " . ($api->timeout_client_resposta / 60) . " minutos, após esse tempo o atendimento será encerrado!"));
$api->enviarMsg($numero, utf8_encode("{$response['DATA']['apelido']}: Olá tudo bem? Como posso lhe ajudar?"));
$api->enviarMsg($response['DATA']['ramal'], utf8_encode($response['MESSAGE']));
$api->enviarMsg($response['DATA']['ramal'], utf8_encode("Novo atendimento, pelo número *{$numero}*!"));
return;
public function mostraPosiscaoNaFila($numero, $fila)
{
$clientfila = $this->queue->clientQueueVerify($numero, $fila);
if ($clientfila['MESSAGE']) {
$this->api->enviarMsg($numero, $clientfila['MESSAGE']);
return null;
}
}
switch ($api->getType()) {
public function retornaConteudo()
{
switch ($this->api->getType()) {
case 'text':
$api->enviarMsg($response['DATA']['ramal'], "{$api->getProfile()}: " . $api->getMessage());
$this->message->addMessage($response['DATA']['uniqueid'], $numero, $response['DATA']['ramal'], $api->getType(), $this->crypt->encrypt($api->getMessage()), $api->getProfile());
return null;
return $this->api->getMessage();
case 'image':
$api->enviaImagem($response['DATA']['ramal'], $api->getLinkDownload($this->host));
$this->message->addMessage($response['DATA']['uniqueid'], $numero, $response['DATA']['ramal'], $api->getType(), $this->crypt->encrypt($api->getLinkDownload($this->host)), $api->getProfile());
return null;
case 'sticker':
$api->enviaSticker($response['DATA']['ramal'], $api->getLinkDownload($this->host));
$this->message->addMessage($response['DATA']['uniqueid'], $numero, $response['DATA']['ramal'], $api->getType(), $this->crypt->encrypt($api->getLinkDownload($this->host)), $api->getProfile());
return null;
case 'video':
$api->enviaVideo($response['DATA']['ramal'], $api->getLinkDownload($this->host));
$this->message->addMessage($response['DATA']['uniqueid'], $numero, $response['DATA']['ramal'], $api->getType(), $this->crypt->encrypt($api->getLinkDownload($this->host)), $api->getProfile());
return null;
case 'voice':
case 'audio':
$api->enviaAudio($response['DATA']['ramal'], $api->getLinkDownload($this->host));
$this->message->addMessage($response['DATA']['uniqueid'], $numero, $response['DATA']['ramal'], $api->getType(), $this->crypt->encrypt($api->getLinkDownload($this->host)), $api->getProfile());
return null;
case 'document':
$api->enviaDocumento($response['DATA']['ramal'], $api->getLinkDownload($this->host), $api->retornaTituloDocument($data));
$this->message->addMessage($response['DATA']['uniqueid'], $numero, $response['DATA']['ramal'], $api->getType(), $this->crypt->encrypt($api->getLinkDownload($this->host)), $api->getProfile());
return null;
return $this->api->getId();
case 'contacts':
$api->enviarContato($response['DATA']['ramal'], $api->getContactFormatted(), $api->getContactPhone());
$this->message->addMessage($response['DATA']['uniqueid'], $numero, $response['DATA']['ramal'], $api->getType(), $this->crypt->encrypt(json_encode(['contact_formatted' => "{$api->getContactFormatted()}", 'contact_phone' => "{$api->getContactPhone()}"])), $api->getProfile());
return null;
case 'location':
$api->enviarLocalizacao($response['DATA']['ramal'], $api->getGeolocation('longitude'), $api->getGeolocation('latitude'));
$this->message->addMessage($response['DATA']['uniqueid'], $numero, $response['DATA']['ramal'], $api->getType(), $this->crypt->encrypt(json_encode(['longitude' => "{$api->getGeolocation('longitude')}", 'latitude' => "{$api->getGeolocation('latitude')}"])), $api->getProfile());
return null;
}
}
private function enviaLista($listas, $api, $numero, $nome, $pre = '')
function enviaMensagemAgente($atendiment)
{
$api->enviarMsgIterativaLista(
$numero,
"$nome disponíveis",
"$nome",
$listas,
$pre
);
}
public function listaFilas()
{
$ramal = new Ramal();
$user = $ramal->findRamal($this->api->getPhone());
$search = $this->queue->listaAllFilas();
$listaPausas = [];
foreach ($search as $key => $value) {
$value->nome = strtolower($value->nome);
array_push($listaPausas, ['title' => substr($value->nome, 0, 23), 'sub' => "/E {$value->nome} {$user->callerid}"]);
}
return $listaPausas;
}
private function listaAgentesDisponivel()
{
$user = $this->agente->infoAgentes($this->api->channel);
$listaPausas = [];
foreach ($user as $key => $value) {
if ($value->status === 'LIVRE' && $value->ramal !== $this->api->getPhone() && $value->chamada_classificado == 1) {
$value->nome = strtolower($value->nome);
array_push($listaPausas, ['title' => substr($value->nome, 0, 23), 'sub' => "/T {$value->ramal}"]);
}
}
return $listaPausas;
$this->message->addMessage(
$atendiment->uniqueid,
$this->api->getPhone(),
$atendiment->matricula,
$this->api->getType(),
$this->retornaConteudo(),
empty($this->api->getProfile()) ? '' : $this->api->getProfile(),
$this->api->getchannel(),
"sended",
$this->api->getMimetype(),
$this->api->retornaTituloDocument(),
$this->api->getId()
);
logger('vvvv')->info('passou1');
$this->ws->enviaMsg(
$this->ws->convertToWebsocket(
$this->retornaConteudo(),
$atendiment->matricula,
$atendiment->uniqueid,
$this->api->getType(),
$this->api->getProfile(),
$this->api->getPhone(),
time(),
$this->api->getId(),
$this->api->getMimetype(),
$this->api->retornaTituloDocument(),
$this->api->getchannel()
)
);
logger('vvvv')->info('passou2');
}
}

174
app/Core/Media.php

@ -1,174 +0,0 @@
<?php
namespace app\Core;
use app\Core\Commands;
use app\Controllers\AgentController;
use app\Controllers\QueueController;
use app\Controllers\BilheteController;
use app\Controllers\ClassificacaoController;
/**
* Description of Media
*
* @author Lucas Awade
*/
class Media
{
private $agente;
private $queue;
private $bilheteController;
private $commands;
private $classificacao;
public $media;
public function __construct()
{
$this->bilheteController = new BilheteController();
$this->agente = new AgentController();
$this->queue = new QueueController();
$this->classificacao = new ClassificacaoController();
$this->commands = new Commands();
}
public function selecionaAtendimento($number, $queue = null, $option = null, $media)
{
$atendimento = $this->agente->inService($number);
/**
* SE POSSUI ATENDIMENTO RETORNA OS DADOS DA CHAMADA
*/
if ($atendimento->origem_destino) {
$ramal = (($atendimento->phone == 'CLIENT') ? $atendimento->ramal : $atendimento->origem_destino);
return $this->response(true, ["ramal" => $ramal, "uniqueid" => $atendimento->uniqueid], null);
}
/**
* VERIFICA SE E UM AGENTE
*/
if ($this->agente->isAgent($number)) {
return $this->validacaoDoAgente($number, $option);
}
/**
* VALIDA A ESTRATEGIA USADA NA FILA
*/
$ramal = $this->agente->priority($queue);
if (!$ramal) {
$ramal = $this->queue->strategy($queue);
}
/**
* VALIDA A O ATENDIMENTO DO CLIENTE
*/
return $this->validacaoDoCliente($number, $queue, $ramal, $media);
}
private function response($status, $data, $message)
{
return array('STATUS' => $status, 'MESSAGE' => $message, 'DATA' => $data);
}
public function getClientQueueData($number, $atendimentoFila)
{
$clientInQueue = false;
$queuePosition = 0;
$clientQueue = '';
foreach ($atendimentoFila as $clientes) {
if ($clientes->src == $number) {
$clientInQueue = true;
$clientQueue = $clientes->fila;
break;
}
}
foreach ($atendimentoFila as $clientes) {
$queuePosition = $queuePosition + 1;
if ($clientes->src == $number) {
break;
}
}
return ["IN_QUEUE" => $clientInQueue, "QUEUE_POSITION" => $queuePosition, "CLIENT_QUEUE" => $clientQueue];
}
function validacaoDoAgente($number, $option)
{
$agent = $this->agente->getAgente($number);
if (!$agent) {
$msg = "Faça o login para iniciar o atendimento!\n";
return $this->response(false, '', $msg);
}
/**
* VERIFICA CLASSIFICACAO DE ATENDIMENTO
*/
$chamadaSemClassificacao = $this->classificacao->agentClassificacaoPending($agent->matricula, $agent->dac);
if ($chamadaSemClassificacao) {
$classificacaoRegisterReturn = $this->classificacao->agentClassificacaoRegister(
$agent->matricula,
$agent->dac,
$chamadaSemClassificacao,
$option
);
if ($classificacaoRegisterReturn) {
return $this->response(false, '', CONF_NAME_REPONSE . " : " . $this->classificacao->message());
}
$classificacaoList = $this->classificacao->agentClassificacaoList($agent->dac);
return $this->response(false, '', CONF_NAME_REPONSE . " : " . $classificacaoList);
}
$msg = "Você não pode abrir um atendimento conectado!\n";
return $this->response(false, '', $msg);
}
function validacaoDoCliente($number, $queue, $ramal, $media)
{
$atendimentoFila = $this->bilheteController->bilheteForaHorario(CONF_EVENT_WHATSAPP_ESPERA, null, null, $media);
$clienteFila = $this->getClientQueueData($number, $atendimentoFila);
$clienteEmFila = $clienteFila['IN_QUEUE'];
$posicaoFila = $clienteFila['QUEUE_POSITION'];
logger('deburguer')->info(print_r(["media" => $media, "cliente" => $clienteFila, "queue" => $queue], true));
if ((!$this->agente->infoAgentes($media, $queue)) && (!$clienteEmFila)) {
$this->bilheteController->bilheteInQueue($number, '0', '0', 'NO ANSWER', $queue, CONF_EVENT_WHATSAPP_ESPERA, 'null', null, $media);
$msg = 'Não temos nenhum atendente disponível no momento, iremos lhe atender assim que um atendente estiver disponível!';
return $this->response(false, '', $msg);
}
if (($clienteEmFila) && ($clienteFila['CLIENT_QUEUE'] != $queue)) {
$msg = 'Você está na posição ' . $posicaoFila . ' da fila ' . $clienteFila['CLIENT_QUEUE'] . '. Aguarde ser atendido!';
return $this->response(false, '', $msg);
}
if ((!$this->agente->infoAgentes($media, $queue)) && ($clienteEmFila)) { //CLIENTE EM FILA MAS NENHUM ATENDENTE LOGADO
$msg = 'Não temos nenhum atendente disponível no momento, iremos lhe atender assim que um atendente estiver disponível!';
return $this->response(false, '', $msg);
}
$agent = $this->agente->getAgente($ramal);
$chamadaSemClassificacao = $this->classificacao->agentClassificacaoPending($agent->matricula, $agent->dac);
if ((!$ramal) || ($chamadaSemClassificacao)) {
if (!$clienteEmFila) {
$this->bilheteController->bilheteInQueue($number, '0', '0', 'NO ANSWER', $queue, CONF_EVENT_WHATSAPP_ESPERA, 'null', null, $media);
}
$atendimentoFila = $this->bilheteController->bilheteForaHorario(CONF_EVENT_WHATSAPP_ESPERA, $queue, null, null, $media);
$posicaoFila = count($atendimentoFila);
$msg = "Nossos atendentes estão ocupados, por favor aguarde que iremos lhe atender! Você está na posição *$posicaoFila*.";
return $this->response(false, '', $msg);
}
$uniqueid = $this->agente->openService($ramal, $number);
$protocol = $this->bilheteController->generateProtocol($uniqueid);
$this->bilheteController->tempoEsperaSupervisor($uniqueid);
$msg = "Atendimento iniciado!\n";
$msg .= "Número do protocolo do atendimento é $protocol\n";
return $this->response(true, ["ramal" => $ramal, "uniqueid" => $uniqueid, "protocol" => $protocol, "apelido" => $agent->nome], $msg);
}
}

15
app/Core/Model.php

@ -32,6 +32,7 @@ abstract class Model
Connect::getInstance()->beginTransaction();
return 1;
} catch (PDOException $ex) {
Connect::setInstance(null);
logger('PDOExcep')->error('PDOException: ' . $ex->getMessage(), debug_backtrace());
return null;
}
@ -43,6 +44,7 @@ abstract class Model
Connect::getInstance()->commit();
return 1;
} catch (PDOException $ex) {
Connect::setInstance(null);
logger('PDOExcep')->error('PDOException: ' . $ex->getMessage(), debug_backtrace());
return null;
}
@ -54,15 +56,12 @@ abstract class Model
Connect::getInstance()->rollBack();
return 1;
} catch (PDOException $ex) {
Connect::setInstance(null);
logger('PDOExcep')->error('PDOException: ' . $ex->getMessage(), debug_backtrace());
return null;
}
}
################################################################################################
################################################################################################
################################################################################################
protected function create($query, $data)
{
try {
@ -71,6 +70,7 @@ abstract class Model
$stmt->execute();
return Connect::getInstance()->lastInsertId();
} catch (PDOException $ex) {
Connect::setInstance(null);
logger('PDOExcep')->error('PDOException: ' . $ex->getMessage(), debug_backtrace());
return null;
}
@ -80,9 +80,7 @@ abstract class Model
{
try {
$stmt = Connect::getInstance()->prepare($query);
if ($data) {
$this->strquery($stmt, $data);
}
$this->strquery($stmt, $data);
$stmt->execute();
return $stmt;
} catch (PDOException $ex) {
@ -100,6 +98,7 @@ abstract class Model
$stmt->execute();
return ($stmt->rowCount() ? 1 : 0);
} catch (PDOException $ex) {
Connect::setInstance(null);
logger('PDOExcep')->error('PDOException: ' . $ex->getMessage(), debug_backtrace());
return null;
}
@ -108,12 +107,12 @@ abstract class Model
protected function update($query, $data)
{
try {
logger('Query')->error('Query: ' . $query, debug_backtrace());
$stmt = Connect::getInstance()->prepare($query);
$this->strquery($stmt, $data);
$stmt->execute();
return ($stmt->rowCount() ? 1 : 0);
} catch (PDOException $ex) {
Connect::setInstance(null);
logger('PDOExcep')->error('PDOException: ' . $ex->getMessage(), debug_backtrace());
return null;
}

9
app/Interfaces/IApi.php

@ -0,0 +1,9 @@
<?php
namespace app\Interfaces;
interface IApi
{
function router($rota, $request);
function retorno($mensagem, $status = null, $dados = null);
}

24
app/Interfaces/IApiMedia.php

@ -4,18 +4,15 @@ namespace app\Interfaces;
interface IApiMedia
{
function enviarMsg($whatsapp, $mensagem);
function getchannel();
function getContentType();
function enviarMedia($whatsapp, $link, $type, $titulo = null);
function enviarMsg($whatsapp, $mensagem, $encode = true);
function enviarMsgIterativaLista($whatsapp, $mensagem, $nomeButton, $sections);
function enviarMsgIterativaBotao($whatsapp, $mensagem, $buttons);
function enviarContato($whatsapp, $nome, $contato);
function enviarLocalizacao($whatsapp, $longitude, $latitude, $nome = null, $endereco = null);
function enviaDocumento($whatsapp, $link, $titulo = null);
function enviaImagem($whatsapp, $link, $titulo = null);
function enviaSticker($whatsapp, $link);
function enviaVideo($whatsapp, $link, $titulo = null);
function enviaAudio($whatsapp, $link);
function baixarMidia($name);
function setStorage($storage);
function baixarMidia();
function getProfile();
function getPhone();
function getType();
@ -23,17 +20,10 @@ interface IApiMedia
function getId();
function getIsValidMessage();
function getMessage();
function setMessage($msg);
function getContactFormatted();
function getContactPhone();
function getGeolocation($type);
function setHook($hook);
function setMetodo($metodo);
function setQuery($query);
function getQuery();
function getArgs($args);
function requestType($req = null);
function exec();
function response($result);
public function getLinkDownload($host);
public function retornaTituloDocument($msg);
function retornaTituloDocument();
}

742
app/Middleware/ApiAgente.php

@ -0,0 +1,742 @@
<?php
namespace app\Middleware;
use app\Controllers\AgentController;
use app\Controllers\ClassificacaoController;
use app\Controllers\QueueController;
use app\Controllers\SystemMessageController;
use app\Interfaces\IApi;
use app\Models\Atendimento;
use app\Models\Evento;
use app\Models\ListaNegraPalavras;
use app\Models\Message;
use app\Models\Parametros;
use app\Models\Pause;
use app\Models\Queue;
use app\Models\SupervisorModel;
use websocket\WsInterface;
class ApiAgente implements IApi
{
/** @var AgentController $agentController Controller do agente */
protected $agentController;
/** @var ClassificacaoController $classificacao Controller do classificacao */
protected $classificacao;
/** @var SupervisorModel $supervisor model do supervisor */
protected $supervisor;
/** @var Atendimento $atendimento model do atendimento */
protected $atendimento;
/** @var Evento $eventos model do Evento */
protected $eventos;
/** @var Parametros $parametros model do Parametros */
protected $parametros;
/** @var Pause $pausasModel model do Pause */
protected $pausasModel;
public function __construct()
{
$this->classificacao = new ClassificacaoController();
$this->agentController = new AgentController();
$this->supervisor = new SupervisorModel();
$this->atendimento = new Atendimento();
$this->eventos = new Evento();
$this->pausasModel = new Pause();
$this->parametros = new Parametros();
}
function router($rota, $request)
{
$request = json_decode($request, true);
switch ($rota) {
case 'entrar':
$this->login($request);
break;
case 'listarFilas':
$this->listaFilas($request);
break;
case 'classificarAtendimento':
$this->classificarAtendimento($request);
break;
case 'enviarMensagem':
$this->enviaMsg($request);
break;
case 'sair':
$this->logoff($request);
break;
case 'listarAgentesDisponivel':
$this->listaAgentesDisponivel();
break;
case 'finalizarAtendimento':
$this->finalizaAtendimento($request);
break;
case 'listarAtendimentoAgente':
$this->listaAtendimentoAgent($request);
break;
case 'listarMensagem':
$this->listaMensagem($request);
break;
case 'listarPausasAgente':
$this->listaPausasAgente($request);
break;
case 'sairPausa':
$this->saiPausa($request);
break;
case 'entrarPausa':
$this->entrarPausa($request);
break;
case 'transferirAtendimento':
$this->transfAtendimento($request);
break;
case 'marcarMensagemVista':
$this->markMessegeRead($request);
break;
case 'statusAgente':
$this->statusAgente($request);
break;
case 'listarAtendimentosFilas':
$this->listarAtendimentosFilas($request);
break;
case 'listarAtendimentosAbandonado':
$this->listarAtendimentosAbandonado($request);
break;
default:
echo json_encode(['status' => '404']);
break;
}
}
function login($request)
{
try {
$agente = $this->supervisor->findByAgent($request['matricula']);
//verifica se existe agente
if (empty($agente)) {
$this->retorno("Agente n<EFBFBD>o encontrado");
return;
}
$filaModel = new Queue();
$fila = $filaModel->findQueueById($request['id_fila']);
if (empty($fila)) {
$this->retorno("Fila n<EFBFBD>o encontrada");
return;
}
$ret = $this->agentController->login(
$fila->nome,
$request['matricula']
);
if (is_string($ret)) {
$this->retorno($ret);
return;
}
$this->retorno(
$ret ? "Logado com sucesso" : "Erro",
$ret ? $ret : null
);
} catch (\Exception $th) {
$this->retorno($th->getMessage());
}
return null;
}
function logoff($request, $valida = true)
{
try {
$agente = $this->supervisor->findAgentByMatricula($request['matricula']);
//verifica se existe agente
$atends = $this->atendimento->getAtendimentoAbertoByAgente($request['matricula']);
if (!$valida) {
foreach ($atends as $key => $atendimento) {
$this->sinalisaErroAtendimento(
$request['matricula'],
$atendimento->uniqueid,
$agente->fila,
$atendimento->cliente_id
);
}
}
if (!empty($atends) && $valida) {
$this->retorno("Atendimentos em aberto, finalize os atendimentos para fazer logoff");
return;
}
if (empty($agente)) {
$this->retorno("Agente n<EFBFBD>o encontrado");
return;
}
$ret = $this->agentController->logoff(
$request['matricula'],
$valida
);
if (is_string($ret)) {
$this->retorno($ret);
return;
}
$this->retorno(
$ret ? "Deslogado com sucesso" : "Erro",
$ret ? $ret : null
);
} catch (\Exception $th) {
$this->retorno($th->getMessage());
}
return null;
}
function listaAgentesDisponivel()
{;
try {
$param = $this->atendimento->getQuantiAtendimentSimultaneos();
$ret = $this->supervisor->listaAgentesDisponivel('LIVRE');
$agentes = [];
foreach ($ret as $key => $value) {
if ($value->countAtendimentos < $param->prm_media_simultaneo) {
array_push($agentes, $value);
}
}
$this->retorno(
$agentes ? "Sucesso" : "Nenhum agente disponivel",
$agentes ? $agentes : null,
$agentes ? $agentes : null
);
} catch (\Exception $th) {
$this->retorno($th->getMessage());
}
return null;
}
function finalizaAtendimento($request)
{
try {
$agente = $this->supervisor->findAgentByMatricula($request['matricula']);
logger('debug')->info(var_export($agente, true));
//verifica se existe agente
if (empty($agente)) {
$this->retorno("Agente n<EFBFBD>o encontrado");
return;
}
$atendimento = $this->atendimento->findAtendId($request['uniqueid']);
//verifica se existe atendimento
if (empty($atendimento)) {
$this->retorno("Atendimento n<EFBFBD>o encontrado");
return;
}
$event = $this->eventos->findEventFinish($request['uniqueid']);
//verifica se o atendimento ja foi finalizado
if (!empty($event)) {
$this->retorno("Atendimento j<EFBFBD> foi finalizado");
return;
}
$ret = $this->eventos->createEvento(
$request['uniqueid'],
CONF_EVENT_TIMERMINO_AGENTE,
date('Y-m-d H:i:s'),
date('Y-m-d H:i:s'),
$atendimento->fila,
$request['matricula']
);
$ws = new WsInterface();
$ws->enviaMsg($this->enviaActions('Atendimento finalizado', 'finish', $agente->matricula, $request['uniqueid']));
$systemController = new SystemMessageController();
$provedor = returnChannel($atendimento->context);
$systemController->sendMessageSystem(
CONF_MOMENT_FINALIZAR_ATENDIMENTO,
[],
$provedor,
$atendimento->cliente_id
);
$messegeModel = new Message();
$messegeModel->addMessage(
$request['uniqueid'],
$agente->matricula,
$agente->matricula,
'finish',
'Atendimento finalizado',
$agente->nome,
$atendimento->context,
'read'
);
if ($agente->status == CONF_AGENT_STATUS_INDISPONIVEL) {
$atends = $this->atendimento->getAtendimentoAbertoByAgente($request['matricula']);
if (empty($atends)) {
$this->agentController->enterPause($request['matricula'], $agente->motivo_pausa);
$ws->enviaMsg($this->enviaActions('Agente em pausa', 'att_status', $agente->matricula, $request['uniqueid']));
}
}
if ($agente->status == CONF_AGENT_STATUS_OCUPADO) {
$atendimentosAbertos = $this->atendimento->getAtendimentoAbertoByAgente($agente->matricula);
$param = $this->atendimento->getQuantiAtendimentSimultaneos();
if (count($atendimentosAbertos) < $param->prm_media_simultaneo) {
$this->supervisor->updateAgent($agente->matricula, CONF_AGENT_STATUS_LIVRE);
}
}
$this->retorno(
$ret ? "Finalizado com sucesso" : "Erro",
$ret ? $ret : null,
$ret ? [$ret] : null
);
} catch (\Exception $th) {
$this->retorno($th->getMessage());
}
return null;
}
function sinalisaErroAtendimento($matricula, $uniqueid, $fila, $client_id)
{
try {
$ret = $this->eventos->createEvento(
$uniqueid,
CONF_EVENT_ERRO_ATEND,
date('Y-m-d H:i:s'),
date('Y-m-d H:i:s'),
$fila,
$matricula
);
$atendimento = $this->atendimento->findAtendId($uniqueid);
$systemController = new SystemMessageController();
$provedor = returnChannel($atendimento->context);
$systemController->sendMessageSystem(
CONF_MOMENT_ERRO_ATEND,
[],
$provedor,
$client_id,
$fila
);
$this->retorno(
$ret ? "Finalizado com sucesso" : "Erro",
$ret ? $ret : null,
$ret ? ["id" => $ret] : null
);
} catch (\Exception $th) {
$this->retorno($th->getMessage());
}
return null;
}
public function listaFilas()
{
$queue = new QueueController();
$search = $queue->listaAllFilas();
$this->retorno(
$search ? "Sucesso" : "Erro",
$search ? $search : null,
$search ? $search : null
);
return null;
}
public function classificarAtendimento($request)
{
$agent = $this->agentController->getAgente($request['ramal']);
if (!$agent) {
$msg = "Fa<EFBFBD>a o login para iniciar o atendimento!\n";
echo json_encode(['message' => $msg]);
return null;
}
/**
* VERIFICA CLASSIFICACAO DE ATENDIMENTO
*/
$chamadaSemClassificacao = $this->classificacao->agentClassificacaoPending($agent->matricula, $agent->dac);
if ($chamadaSemClassificacao) {
$classificacaoRegisterReturn = $this->classificacao->agentClassificacaoRegister(
$agent->matricula,
$agent->dac,
$chamadaSemClassificacao,
$request['option']
);
if ($classificacaoRegisterReturn) {
echo json_encode(['message' => CONF_NAME_REPONSE . " : " . $this->classificacao->message()]);
return null;
}
$classificacaoList = $this->classificacao->agentClassificacaoList($agent->dac);
echo json_encode(['message' => CONF_NAME_REPONSE . " : " . $classificacaoList]);
return null;
}
echo json_encode(['status' => "false"]);
return null;
}
public function enviaMsg($request)
{
try {
$mensagem = $request['event']['mensagem'];
$contact = $request['event']['contact'];
$status = $this->atendimento->getStatusAtendimento($mensagem['uniqueid']);
if ($status->evento == CONF_EVENT_START) {
$provider = returnChannel($mensagem['media']);
$modelMensagem = new Message();
$msgTexto = $contact['name'] . ': ' . $this->validaPalavroes($mensagem['content']);
if ($mensagem['type'] == 'text') {
$retuno = $provider->enviarMsg(
$mensagem['dst'],
$msgTexto,
false
);
} else {
$anmeArquivo = CONF_PATH_FILES . $mensagem['id_provedor'];
$texto = base64_decode($mensagem['content']);
file_put_contents($anmeArquivo, $texto);
$provider->convertWavToMp3($anmeArquivo);
$retuno = $provider->enviarMedia(
$mensagem['dst'],
CONF_MIDDLEWARE_LINKUPLOAD .
$mensagem['id_provedor'] . '/' .
base64_encode($mensagem['mimetype']),
$mensagem['type'],
$mensagem['file_name']
);
}
$modelMensagem->addMessage(
$mensagem['uniqueid'],
$contact['number'],
$mensagem['dst'],
$mensagem['type'],
$mensagem['type'] == 'text' ? $msgTexto : $mensagem['id_provedor'],
$contact['name'],
$mensagem['media'],
$mensagem['status'],
$mensagem['mimetype'],
$mensagem['file_name'],
$mensagem['id_provedor']
);
$this->retorno(
$retuno ? "Sucesso" : "Erro",
$retuno ? $retuno : null,
$retuno ? [["message" => $retuno]] : null
);
} else {
$this->retorno('Atendimento j<EFBFBD> foi finalizado');
}
return;
} catch (\Exception $th) {
$this->retorno($th->getMessage());
return;
}
}
function retorno($mensagem, $status = null, $dados = null)
{
//{ "status": "success", "message": "register created!", "data": [ "id": 20 ] }
$data = [];
$data['message'] = utf8_encode($mensagem);
if (!empty($status)) {
$data['status'] = "success";
} else {
$data['status'] = "error";
}
if (!empty($dados)) {
$data['data'] = $dados;
}
echo json_encode($data);
}
function listaAtendimentoAgent($request)
{
try {
if ($request['matricula']) {
$agente = $this->supervisor->findAgentByMatricula($request['matricula']);
//verifica se existe agente
if (empty($agente)) {
$this->retorno("Agente n<EFBFBD>o encontrado");
return;
}
}
$ret = $this->atendimento->findAtendAgent($request['matricula'], $request['quantidade']);
$data = [];
$data['message'] = utf8_encode("Sucesso");
$data['status'] = "success";
$data['data'] = $ret;
echo json_encode($data);
return null;
} catch (\Exception $th) {
$this->retorno($th->getMessage());
return;
}
}
function listaMensagem($request)
{
try {
$atend = $this->atendimento->findAtendId($request['uniqueid']);
//verifica se existe agente
if (empty($atend)) {
$this->retorno("N<EFBFBD>o existe atendimento para esse uniqueid");
return;
}
$messageModel = new Message();
$retunr = $messageModel->findMessageByUniqueid($request['uniqueid']);
$mensagenss = [];
foreach ($retunr as $key => $value) {
$mensagem = [];
$mensagem["event"] = [
"type" => $value->type == 'finish' ? 'actions' : "mensagem",
"contact" => [
"name" => utf8_encode($value->profile_name),
"number" => $value->src,
],
"mensagem" => [
"type" => $value->type,
"content" => utf8_encode($value->content),
"id_provedor" => $value->id_provedor,
"dst" => $value->dst,
"uniqueid" => $value->uniqueid,
"media" => $value->media,
"file_name" => utf8_encode($value->file_name),
"datetime" => $value->msg_date,
"status" => $value->status,
'mimetype' => $value->mimetype
]
];
array_push($mensagenss, $mensagem);
}
$data = [];
$data['message'] = utf8_encode("Sucesso");
$data['status'] = "success";
$data['data'] = $mensagenss;
echo json_encode($data);
return;
} catch (\Exception $th) {
$this->retorno($th->getMessage());
return;
}
}
function listaPausasAgente($request)
{
$agente = $this->supervisor->findAgentByMatricula($request['matricula']);
//verifica se existe agente
if (empty($agente)) {
$this->retorno("Agente n<EFBFBD>o encontrado");
return null;
}
$param = $this->parametros->findProtocolByParams();
if (false) {
$ret = $this->pausasModel->findPauseByGroupUser($request['matricula']);
} else {
$ret = $this->pausasModel->findAllPause();
}
foreach ($ret as $key => $value) {
$ret[$key] = removeAcentosArray((array) $value);
}
$this->retorno(
$ret ? "Sucesso" : "Erro",
$ret ? $ret : null,
$ret ? $ret : null
);
return null;
}
function entrarPausa($request)
{
$ws = new WsInterface();
$agente = $this->supervisor->findAgentByMatricula($request['matricula']);
//verifica se existe agente
if (empty($agente)) {
$this->retorno("Agente n<EFBFBD>o encontrado");
return;
}
$pausa = $this->pausasModel->findPauseById($request['id_pausa']);
if (empty($pausa)) {
$this->retorno("Pausa n<EFBFBD>o encontrado");
return;
}
if ($agente->status == CONF_AGENT_STATUS_PAUSA || $agente->status == CONF_AGENT_STATUS_INDISPONIVEL) {
$this->retorno('Agente precisa estar livre para entrar em pausa!');
return;
}
$atends = $this->atendimento->getAtendimentoAbertoByAgente($request['matricula']);
if (!empty($atends)) {
$ret = $this->agentController->indisponivelAtendimento($request['matricula'], $pausa->motivo);
if (is_string($ret)) {
$this->retorno($ret);
} else {
$ws->enviaMsg($this->enviaActions('Agente em pausa', 'att_status', $agente->matricula));
$this->retorno(
"Agente em 'indisponivel'",
$ret
);
}
return;
}
$ret = $this->agentController->enterPause($request['matricula'], $pausa->motivo);
$ws->enviaMsg($this->enviaActions('Agente em pausa', 'att_status', $agente->matricula));
$this->retorno(
$ret ? "Agente em 'pausa'" : "Erro",
$ret ? $ret : null
);
return null;
}
function saiPausa($request)
{
$ws = new WsInterface();
$agente = $this->supervisor->findAgentByMatricula($request['matricula']);
//verifica se existe agente
if (empty($agente)) {
$this->retorno("Agente n<EFBFBD>o encontrado");
return;
}
$ret = $this->agentController->exitPause($request['matricula']);
if (is_string($ret)) {
$this->retorno($ret);
} else {
$ws->enviaMsg($this->enviaActions('Agente em pausa', 'att_status', $agente->matricula));
$this->retorno(
"Agente 'livre'",
$ret
);
}
return;
}
function transfAtendimento($request)
{
$retunr = $this->agentController->transfer(
$request['matricula_origem'],
$request['matricula_destino'],
$request['uniqueid']
);
if (is_string($retunr)) {
$this->retorno($retunr);
} else {
$this->retorno(
$retunr ? "Sucesso" : "Erro",
$retunr ? $retunr : null
);
}
return null;
}
function markMessegeRead($request)
{
$atend = $this->atendimento->findAtendId($request['uniqueid']);
//verifica se existe agente
if (empty($atend)) {
$this->retorno("N<EFBFBD>o existe atendimento para esse uniqueid");
return;
}
$modelMensagem = new Message();
$ret = $modelMensagem->markMessege($request['uniqueid'], 'read');
$this->retorno(
$ret ? "Sucesso" : "Erro",
$ret ? $ret : null
);
return null;
}
function statusAgente($request)
{
try {
$agente = $this->supervisor->findAgentByMatricula($request['matricula']);
//verifica se existe agente
if (empty($agente)) {
$this->retorno("Agente n<EFBFBD>o encontrado");
return;
}
$this->supervisor->updateAgent($agente->matricula, $agente->status, $agente->motivo_pausa);
$ret = $this->supervisor->statusAgente($request['matricula']);
$ret = removeAcentosArray((array) $ret);
$this->retorno(
$ret ? "Sucesso" : "Erro",
$ret ? $ret : null,
$ret ? [$ret] : null
);
} catch (\Exception $th) {
$this->retorno($th->getMessage());
}
}
function enviaActions($msg, $tipo, $destino, $uniqueid = null)
{
try {
$mensagem = [];
$mensagem["event"] = [
"type" => 'actions',
"contact" => [
"name" => 'Sistema',
"number" => '0'
],
"mensagem" => [
"type" => $tipo,
"dst" => $destino,
"uniqueid" => $uniqueid,
"content" => utf8_encode($msg)
],
];
return json_encode($mensagem);
} catch (\Exception $th) {
logger('monitora')->info($th->getMessage());
}
}
public function validaPalavroes($msg)
{
try {
$palavroes = new ListaNegraPalavras();
$palavras = $palavroes->getAll();
foreach ($palavras as $key => $value) {
$pattern = "/\b($value->palavra)\b/i";
$msg = preg_replace($pattern, '*' . str_repeat('*', strlen($value->palavra)) . '*', $msg);
}
} catch (\Exception $th) {
logger('telegram')->info(print_r($th, true), true);
}
return $msg;
}
public function listarAtendimentosFilas($request)
{
try {
$filaModel = new Queue();
$fila = $filaModel->findQueueById($request['id_fila']);
if (empty($fila)) {
$this->retorno("Fila n<EFBFBD>o encontrada");
return;
}
$retunr = $this->atendimento->getAtendFila($fila->nome);
$data = [];
$data['message'] = utf8_encode("Sucesso");
$data['status'] = "success";
$data['data'] = $retunr;
echo json_encode($data);
return null;
} catch (\Exception $th) {
return $this->retorno($th->getMessage());
}
}
public function listarAtendimentosAbandonado($request)
{
try {
$filaModel = new Queue();
if ($request['id_fila']) {
$fila = $filaModel->findQueueById($request['id_fila']);
if (empty($fila)) {
$this->retorno("Fila n<EFBFBD>o encontrada");
return;
}
}
$retunr = $this->atendimento->getAtendAbandonado($fila->nome);
$data = [];
$data['message'] = utf8_encode("Sucesso");
$data['status'] = "success";
$data['data'] = $retunr;
echo json_encode($data);
return null;
} catch (\Exception $th) {
return $this->retorno($th->getMessage());
}
}
}

38
app/Middleware/ApiInfo.php

@ -0,0 +1,38 @@
<?php
namespace app\Middleware;
use app\Interfaces\IApi;
class ApiInfo implements IApi
{
function router($rota, $request)
{
$this->showConf();
}
function retorno($mensagem, $status = null, $dados = null)
{
//{ "status": "success", "message": "register created!", "data": [ "id": 20 ] }
$data = [];
$data['message'] = utf8_encode($mensagem);
if (!empty($status)) {
$data['status'] = "success";
} else {
$data['status'] = "error";
}
if (!empty($dados)) {
$data['data'] = $dados;
}
echo json_encode($data);
}
function showConf()
{
$dados['version_system'] = INFO_VERSION_SYSTEM;
$dados['conf_db_host'] = CONF_DB_HOST;
$dados['conf_db_port'] = CONF_DB_PORT;
$this->retorno('Sucesso', "success", [$dados]);
}
}

64
app/Middleware/ApiSupervisor.php

@ -0,0 +1,64 @@
<?php
namespace app\Middleware;
use app\Interfaces\IApi;
use app\Models\SupervisorModel;
class ApiSupervisor implements IApi
{
/** @var SupervisorModel $supervisor model de supervisor */
protected $supervisor;
public function __construct()
{
$this->supervisor = new SupervisorModel();
}
function router($rota, $request)
{
switch ($rota) {
case 'listarAgentesDisponivel':
$this->listaAgentesDisponivel();
break;
default:
echo json_encode(['status' => '404']);
break;
}
}
function listaAgentesDisponivel()
{
try {
$ret = $this->supervisor->listaAgentesDisponivel();
$agentes = [];
foreach ($ret as $key => $value) {
array_push($agentes, $value);
}
$this->retorno(
$agentes ? "Sucesso" : "Nenhum agente disponivel",
$agentes ? $agentes : null,
$agentes ? $agentes : null
);
} catch (\Exception $th) {
$this->retorno($th->getMessage());
}
return null;
}
function retorno($mensagem, $status = null, $dados = null)
{
//{ "status": "success", "message": "register created!", "data": [ "id": 20 ] }
$data = [];
$data['message'] = utf8_encode($mensagem);
if (!empty($status)) {
$data['status'] = "success";
} else {
$data['status'] = "error";
}
if (!empty($dados)) {
$data['data'] = $dados;
}
echo json_encode($data);
}
}

60
app/Middleware/Middleware.php

@ -3,12 +3,10 @@
namespace app\Middleware;
use app\Core\CoreMedia;
use app\Core\Telegram;
use app\Middleware\Http;
use app\Middleware\ApiAgente;
use app\Providers\WebHeader;
use app\Core\WhatsApp;
use app\Providers\ApiTelegram;
use app\Providers\Positus;
use app\Providers\Whatsapp;
/**
* Description of WppController
@ -18,7 +16,7 @@ use app\Providers\Positus;
class Middleware extends Http
{
/** @var return Positus request */
/** @var array request */
private $request;
/** @class WebHeader */
@ -31,6 +29,7 @@ class Middleware extends Http
{
$this->link = $config['LINK_UPLOAD'];
$this->header = new WebHeader($config);
$this->api();
$this->hook();
}
@ -38,41 +37,51 @@ class Middleware extends Http
* Start API headers
* @param array $config
*/
public function api()
public function api($contentType = "application/json; charset=UTF-8")
{
$this->header->API();
$this->header->API($contentType);
}
/**
* Redirect request to Media Social
* Redirect request to media Social
* @return null
*/
private function router()
{
$coremedia = new CoreMedia();
$apiAgente = new ApiAgente();
$apiInfo = new ApiInfo();
$apiSupervisor = new ApiSupervisor();
$this->baseUri();
// logger('telegram')->info(print_r($this->request, true), true);
$data = [
"REQUEST" => $this->request,
"HOST" => $this->getLink(),
"DOWNLOAD" => $this->download()
];
switch (strtolower($this->param()[1])) {
case 'whatsapp':
$file = $coremedia->inicia($data, new Positus());
if ($file) {
$this->download($file);
}
echo json_encode(['success' => true]);
return null;
case 'telegram':
$file = $coremedia->inicia($data, new ApiTelegram());
echo json_encode(['success' => $this->request]);
$provedor = new Whatsapp();
$coremedia->inicia($this->request, $provedor);
$this->api($provedor->getContentType());
echo '';
return null;
case 'api':
$this->api();
switch ($this->param()[2]) {
case 'agente':
$apiAgente->router($this->param()[3], $this->request);
return null;
case 'supervisor':
$apiSupervisor->router($this->param()[3], $this->request);
return null;
default:
echo 'erro';
}
case 'link':
$this->api();
$this->header->fileTransfer($this->param()[2], CONF_PATH_FILES . $this->param()[2], base64_decode($this->param()[3]));
exit(0);
case 'info':
$apiInfo->router($this->param()[3], $this->request);
exit(0);
default:
$this->header->redirect();
exit(0);
}
}
@ -82,7 +91,6 @@ class Middleware extends Http
private function hook()
{
$this->request = file_get_contents('php://input');
$this->request = json_decode($this->request, true);
$this->router();
}

36
app/Models/Agent.php

@ -2,7 +2,6 @@
namespace app\Models;
use app\Core\Connect;
use app\Core\Model;
/**
@ -28,25 +27,20 @@ class Agent extends Model
return $this->read($this->query, ['number' => $number])->fetch();
}
public function findAllAgentes($media = ['TELEGRAM', 'WHATSAPP'], $queue = null)
public function findAllAgentes($media = ['TELEGRAM', 'whatsapp'], $queue = null)
{
try {
$data = [];
$this->query = "SELECT * FROM " . self::SUPERVISOR_AGENTE . " WHERE 1=1 ";
$this->query .= sprintf(" AND sala_1 in(%s) ", whereIn($media));
if ($queue) {
$this->query .= " AND dac = :queue ";
$data['queue'] = $queue;
}
$ret = $this->read($this->query, $data);
if ($ret) {
return $ret->fetchAll();
} else {
return [];
}
} catch (\Throwable $th) {
Connect::setInstance(null);
logger('testeOff')->info(gettype(Connect::getInstance()), debug_backtrace());
$data = [];
$this->query = "SELECT * FROM " . self::SUPERVISOR_AGENTE . " WHERE 1=1 ";
$this->query .= sprintf(" AND sala_1 in(%s) ", whereIn($media));
if ($queue) {
$this->query .= " AND dac = :queue ";
$data['queue'] = $queue;
}
$ret = $this->read($this->query, $data);
if ($ret) {
return $ret->fetchAll();
} else {
return [];
}
}
@ -88,8 +82,8 @@ class Agent extends Model
$data['queue'] = $queue;
$data['status'] = $status;
$data['evento'] = implode(',', [
CONF_EVENT_WHATSAPP_TIMERMINO_CLIENTE,
CONF_EVENT_WHATSAPP_TIMERMINO_AGENTE
CONF_EVENT_TIMERMINO_CLIENTE,
CONF_EVENT_TIMERMINO_AGENTE
]);
if ($orderBy && $orderByType) {

182
app/Models/Atendimento.php

@ -0,0 +1,182 @@
<?php
namespace app\Models;
use app\Core\Connect;
use app\Core\Model;
class Atendimento extends Model
{
private $evento = 'md_evento';
private $atendimento = 'md_atendimento';
public function getAtendimentoByCliente($cliente_id, $evento = 'EMESPERA')
{
$this->query = "SELECT ma.*, me.fila as fila FROM {$this->atendimento} ma
INNER JOIN {$this->evento} me
ON ma.uniqueid = me.uniqueid
WHERE ma.cliente_id = :cliente_id
AND (SELECT m2.evento FROM md_evento m2 WHERE ma.uniqueid = m2.uniqueid ORDER BY id DESC LIMIT 1) = :evento";
return $this->read($this->query, ['cliente_id' => $cliente_id, 'evento' => $evento])->fetch();
}
public function getStatusAtendimento($uniqueid)
{
$this->query = "SELECT evento FROM md_evento WHERE uniqueid = :uniqueid ORDER BY id DESC LIMIT 1";
return $this->read($this->query, ['uniqueid' => $uniqueid])->fetch();
}
public function findAtendId($uniqueid)
{
$this->query = "SELECT ma.*, me.fila as fila FROM {$this->atendimento} ma
INNER JOIN {$this->evento} me
ON ma.uniqueid = me.uniqueid
WHERE ma.uniqueid = :uniqueid ";
return $this->read($this->query, ['uniqueid' => $uniqueid])->fetch();
}
public function getAtendimentoByEvento($fila, $evento = 'EMESPERA')
{
$this->query = "SELECT ma.* FROM {$this->atendimento} ma
INNER JOIN {$this->evento} me
ON ma.uniqueid = me.uniqueid
WHERE (SELECT m2.evento FROM md_evento m2 WHERE ma.uniqueid = m2.uniqueid ORDER BY id DESC LIMIT 1) = :evento
AND me.fila = :fila";
return $this->read($this->query, ['evento' => $evento, 'fila' => $fila])->fetchAll();
}
public function findAtendAgent($matricula, $quantidade = 10)
{
$data = [];
$this->query = "SELECT ma.*,
ma.nome AS profile_name,
ppr.protocolo as protocolo,
(SELECT m2.evento FROM md_evento m2 WHERE ma.uniqueid = m2.uniqueid ORDER BY id DESC LIMIT 1) AS evento,
CASE
WHEN (SELECT m2.evento FROM md_evento m2 WHERE ma.uniqueid = m2.uniqueid ORDER BY id DESC LIMIT 1) = 'START' THEN 1
ELSE 0
END AS status
FROM {$this->atendimento} ma
INNER JOIN pbx_protocolo_reg ppr ON ma.uniqueid = ppr.uniqueid ";
if ($matricula) {
$this->query .= " WHERE ma.matricula = :matricula ";
$data['matricula'] = $matricula;
}
if (empty($quantidade)) {
$data['quantidade'] = 10;
} else {
$data['quantidade'] = $quantidade;
}
$this->query .= " ORDER BY status DESC, data_reg DESC
LIMIT :quantidade ";
return $this->read($this->query, $data)->fetchAll();
}
public function findAtenEmAberto($cliente_id = null)
{
$this->query = "SELECT ma.* FROM {$this->atendimento} ma
WHERE (SELECT m2.evento FROM md_evento m2 WHERE ma.uniqueid = m2.uniqueid ORDER BY id DESC LIMIT 1) = 'START'
AND ma.matricula notnull ";
$data = [];
if ($cliente_id) {
$data['cliente_id'] = $cliente_id;
$this->query .= 'AND ma.cliente_id = :cliente_id';
return $this->read($this->query, $data)->fetch();
}
return $this->read($this->query, $data)->fetchAll();
}
public function getAtendimentoAbertoByAgente($matricula)
{
$this->query = "SELECT ma.* FROM {$this->atendimento} ma
WHERE (SELECT m2.evento FROM md_evento m2 WHERE ma.uniqueid = m2.uniqueid ORDER BY id DESC LIMIT 1) = 'START'
AND ma.matricula = :matricula ";
return $this->read($this->query, ['matricula' => $matricula])->fetchAll();
}
// public function getAtendFila($fila, $evento = 'LOST_CONNECTION')
// {
// $this->query = "SELECT ma.*, me.fila as fila FROM {$this->atendimento} ma
// INNER JOIN {$this->evento} me
// ON ma.uniqueid = me.uniqueid
// WHERE me.evento = :evento
// AND me.fila = :fila
// AND (SELECT m2.evento FROM md_evento m2 WHERE ma.uniqueid = m2.uniqueid ORDER BY id DESC LIMIT 1) = 'LOST_CONNECTION' ";
// return $this->read($this->query, ['evento' => $evento, 'fila' => $fila])->fetchAll();
// }
public function getAtendFila($fila)
{
$this->query = "SELECT ma.*, me.fila as fila FROM {$this->atendimento} ma
INNER JOIN {$this->evento} me
ON ma.uniqueid = me.uniqueid
AND me.evento IN('EMESPERA', 'LOST_CONNECTION')
WHERE me.fila = :fila
AND (SELECT m2.evento FROM md_evento m2 WHERE ma.uniqueid = m2.uniqueid ORDER BY id DESC LIMIT 1) IN('EMESPERA', 'LOST_CONNECTION') ";
return $this->read($this->query, ['fila' => $fila])->fetchAll();
}
public function getAtendAbandonado($fila = null)
{
$data = [];
$this->query = "SELECT ma.*, me.fila as fila FROM {$this->atendimento} ma
INNER JOIN {$this->evento} me
ON ma.uniqueid = me.uniqueid
AND me.evento = 'ABANDON'
WHERE (SELECT m2.evento FROM md_evento m2 WHERE ma.uniqueid = m2.uniqueid ORDER BY id DESC LIMIT 1) = 'ABANDON'
AND ma.data_reg >= CURRENT_DATE ";
if ($fila) {
$this->query .= ' AND me.fila = :fila';
$data['fila'] = $fila;
}
return $this->read($this->query, $data)->fetchAll();
}
public function createAtendimento($matricula, $cliente_id, $direcao, $context, $nome)
{
$unique = uniqid('', true);
$this->query = "INSERT INTO {$this->atendimento} (
matricula,
cliente_id,
direcao,
uniqueid,
context,
nome)
VALUES(
:matricula,
:cliente_id,
:direcao,
:uniqueid,
:context,
:nome);";
$data['uniqueid'] = $unique;
$data['matricula'] = $matricula;
$data['cliente_id'] = $cliente_id;
$data['direcao'] = $direcao;
$data['context'] = $context;
$data['nome'] = $nome;
$return = $this->create($this->query, $data);
if ($return) {
return $unique;
}
return $return;
}
public function updAtendimento($uniqueid, $matricula)
{
$this->query = "UPDATE {$this->atendimento} SET matricula = :matricula WHERE uniqueid = :uniqueid;";
$data['matricula'] = $matricula;
$data['uniqueid'] = $uniqueid;
return $this->update($this->query, $data);
}
public function getQuantiAtendimentSimultaneos()
{
$this->query = "SELECT prm_media_simultaneo FROM pbx_parametros LIMIT 1";
return $this->read($this->query)->fetch();
}
}

4
app/Models/EventQueue.php

@ -58,6 +58,6 @@ class EventQueue extends Model
$this->query = "SELECT * FROM " . self::TABLE . "
WHERE evento = :evento
AND fila = :fila ";
return $this->read($this->query, ['fila' => $queue, 'evento' => CONF_EVENT_WHATSAPP_ESPERA]);
return $this->read($this->query, ['fila' => $queue, 'evento' => CONF_EVENT_ESPERA]);
}
}
}

50
app/Models/Evento.php

@ -0,0 +1,50 @@
<?php
namespace app\Models;
use app\Core\Model;
class Evento extends Model
{
private $evento = 'md_evento';
public function createEvento($uniqueid, $evento, $data_evento, $data_reg, $fila = null, $matricula = null)
{
$this->query = "INSERT INTO {$this->evento} (uniqueid,
evento,
data_evento,
data_reg,
fila,
matricula )
VALUES(:uniqueid,
:evento,
:data_evento,
:data_reg,
:fila,
:matricula );";
$data['uniqueid'] = $uniqueid;
$data['evento'] = $evento;
$data['data_evento'] = $data_evento;
$data['data_reg'] = $data_reg;
$data['fila'] = $fila;
$data['matricula'] = $matricula;
$return = $this->create($this->query, $data);
return $return;
}
public function findEventFinish($uniqueid)
{
$this->query = "SELECT * FROM {$this->evento}
WHERE uniqueid = :uniqueid
AND evento in ('COMPLETE_AGENT', 'COMPLETE_AGENT');";
return $this->read($this->query, ['uniqueid' => $uniqueid])->fetch();
}
public function getStatusAtendimento($uniqueid)
{
$this->query = "SELECT evento FROM {$this->evento} WHERE uniqueid = :uniqueid ORDER BY id DESC LIMIT 1";
return $this->read($this->query, ['uniqueid' => $uniqueid])->fetch();
}
}

59
app/Models/Message.php

@ -1,6 +1,7 @@
<?php
namespace app\Models;
use app\Core\Model;
/**
@ -11,18 +12,48 @@ use app\Core\Model;
class Message extends Model
{
const MESSAGE = "pbx_message_wpp";
const MESSAGE = "md_message";
public function addMessage($uniqueid, $src, $dst, $tipo, $conteudo, $nome)
public function addMessage($uniqueid, $src, $dst, $tipo, $content, $profile_name, $media, $status, $mimetype = null, $file_name = null, $id_provedor = null)
{
$this->query = "INSERT INTO " . self::MESSAGE . " (uniqueid, src, dst, type, content, profile_name) VALUES(:uniqueid, :src, :dst, :type, :content, :profile_name);";
return $this->create($this->query, ['uniqueid' => $uniqueid, 'src' => $src, 'dst' => $dst, 'type' => $tipo, 'content' => utf8_decode($conteudo), 'profile_name' => utf8_decode($nome)]);
if ($uniqueid) {
$this->query = "INSERT INTO " . self::MESSAGE . " (uniqueid, src, dst, type, content, profile_name, media, status, mimetype, file_name, id_provedor)
VALUES(:uniqueid, :src, :dst, :type, :content, :profile_name, :media, :status, :mimetype, :file_name, :id_provedor);";
return $this->create($this->query, [
'uniqueid' => $uniqueid,
'src' => $src,
'dst' => $dst,
'type' => $tipo,
'content' => utf8_decode($content),
'profile_name' => utf8_decode($profile_name),
'media' => $media,
'status' => $status,
'id_provedor' => $id_provedor,
'file_name' => $file_name,
'mimetype' => $mimetype
]);
} else {
logger('debug')->info(print_r([
'uniqueid' => $uniqueid,
'src' => $src,
'dst' => $dst,
'type' => $tipo,
'content' => utf8_decode($content),
'profile_name' => utf8_decode($profile_name),
'media' => $media,
'status' => $status,
'id_provedor' => $id_provedor,
'file_name' => $file_name,
'mimetype' => $mimetype
], true));
return null;
}
}
public function findMessageByUniqueid($uniqueid)
{
$this->query = "SELECT * FROM " . self::MESSAGE . " WHERE uniqueid = :uniqueid ORDER BY msg_date ASC";
return $this->read($this->query, ['uniqueid' => $uniqueid])->fetch();
$this->query = "SELECT * FROM " . self::MESSAGE . " WHERE uniqueid = :uniqueid ORDER BY id";
return $this->read($this->query, ['uniqueid' => $uniqueid])->fetchAll();
}
public function findMessageByNumber($number)
@ -37,4 +68,18 @@ class Message extends Model
$this->query .= " ORDER BY msg_date DESC LIMIT 1";
return $this->read($this->query, ['uniqueid' => $uniqueid])->fetch();
}
}
public function markMessege($uniqueid, $status)
{
$this->query = "UPDATE " . self::MESSAGE . " SET status = :status WHERE uniqueid = :uniqueid;";
$data['uniqueid'] = $uniqueid;
$data['status'] = $status;
return $this->update($this->query, $data);
}
public function getNameCliente($uniqueid, $cliente_id)
{
$this->query = "SELECT profile_name FROM md_message mm WHERE uniqueid = :uniqueid AND src = :cliente_id LIMIT 1";
return $this->read($this->query, ['uniqueid' => $uniqueid, 'cliente_id' => $cliente_id])->fetch();
}
}

31
app/Models/Parametros.php

@ -1,19 +1,22 @@
<?php
namespace app\Models;
use app\Core\Model;
namespace app\Models;
/**
* Description of EventQueue
*
* @author root
*/
class Parametros extends Model {
use app\Core\Model;
const TABLE = 'pbx_parametros';
/**
* Description of EventQueue
*
* @author root
*/
class Parametros extends Model
{
public function findProtocolByParams($uniqueid){
$this->query = "SELECT prm_agente_proto, prm_use_proto_parceiro FROM " . self::TABLE . " WHERE id = :id;";
return $this->read($this->query, ['id' => 1])->fetch();
}
}
const TABLE = 'pbx_parametros';
public function findProtocolByParams()
{
$this->query = "SELECT prm_agente_proto, prm_use_proto_parceiro, prm_pausa_grupo, prm_media_simultaneo FROM " . self::TABLE . " WHERE id = :id;";
return $this->read($this->query, ['id' => 1])->fetch();
}
}

37
app/Models/Pause.php

@ -18,10 +18,16 @@ class Pause extends Model
public function findPauseByName($name)
{
$this->query = "SELECT * FROM " . self::TABLE . " WHERE upper(motivo) = :name";
$this->query = "SELECT * FROM " . self::TABLE . " WHERE upper(motivo) = :name ";
return $this->read($this->query, ['name' => strtoupper($name)])->fetch();
}
public function findPauseById($id)
{
$this->query = "SELECT * FROM " . self::TABLE . " WHERE id = :id";
return $this->read($this->query, ['id' => strtoupper($id)])->fetch();
}
public function addEventoPauseAgent($matricula, $ramal, $idMotivo, $idDac, $produtiva, $flag = 1)
{
$this->query = "INSERT INTO " . self::EVENTO_AGENTE . " (matricula, ramal, id_dac, id_motivo_pausa, flag, pausa_produtiva, entrada_pausa, saida_pausa)
@ -41,11 +47,27 @@ class Pause extends Model
);
}
public function addEventoIndisponivelAgent($matricula, $ramal, $idDac)
{
$this->query = "INSERT INTO " . self::EVENTO_AGENTE . " (matricula, ramal, id_dac, entrada_indisponivel, saida_indisponivel)
VALUES(:matricula, :ramal, :id_dac, :entrada_indisponivel, :saida_indisponivel);";
return $this->create(
$this->query,
[
'matricula' => $matricula,
'ramal' => $ramal,
'id_dac' => $idDac,
'entrada_indisponivel' => 'now()',
'saida_indisponivel' => 'now()'
]
);
}
public function updateEventoOutPause($matricula, $dac, $flag = 2)
{
$this->query = "UPDATE " . self::EVENTO_AGENTE . " SET saida_pausa = :saida_pausa, flag = :flag WHERE matricula = :matricula AND id_dac = :id_dac
AND entrada_pausa = (SELECT MAX(entrada_pausa) FROM " . self::EVENTO_AGENTE . " WHERE matricula = :matricula AND id_dac = :id_dac);";
return $this->update($this->query, ['saida_pausa' => 'now()', 'flag' => $flag, 'matricula' => $matricula, 'id_dac' => $dac, 'entrada_pausa']);
return $this->update($this->query, ['saida_pausa' => 'now()', 'flag' => $flag, 'matricula' => $matricula, 'id_dac' => $dac]);
}
public function findGroupPause()
@ -56,14 +78,15 @@ class Pause extends Model
public function findPauseByGroupUser($matricula, $active = true)
{
$this->query = "SELECT d.id, d.motivo, d.produtiva, d.flag, d.tempo_alerta
$this->query = "SELECT DISTINCT d.id, d.motivo, d.produtiva, d.flag, d.tempo_alerta
FROM pbx_usuarios a
INNER JOIN pbx_grupo_usuario b ON a.id = b.user_id
INNER JOIN pbx_pausa_grupo_usuario c ON c.gp_id = b.gp_id
INNER JOIN pbx_motivos_pausas d ON d.id = c.id
WHERE matricula = :matricula ";
WHERE matricula = :matricula
AND d.motivo NOT IN('login','ausente','RECUSADA') ";
if ($active) {
$this->query .= " AND flag = :flag";
$this->query .= " AND d.flag = :flag";
$data['flag'] = 1;
}
$data['matricula'] = $matricula;
@ -74,10 +97,10 @@ class Pause extends Model
{
$this->query = "SELECT * FROM " . self::TABLE . " WHERE 1=1 ";
if ($active) {
$this->query .= " AND flag = :flag";
$this->query .= " AND flag = :flag ";
$data['flag'] = 1;
}
$this->query .= " LIMIT 10 ";
$this->query .= " AND motivo NOT IN('login','ausente','RECUSADA') LIMIT 10 ";
return $this->read($this->query, $data)->fetchAll();
}

6
app/Models/Queue.php

@ -19,14 +19,14 @@ class Queue extends Model
public function findAllQueue($active = true)
{
$this->query = "SELECT * FROM " . self::TABLE . " WHERE 1=1 AND midiafila = :midiafila ";
$this->query = "SELECT id, nome FROM " . self::TABLE . " WHERE 1=1 AND midiafila = :midiafila ";
if ($active) {
$this->query .= " AND status = :status ";
$data['status'] = 'A';
}
$data['midiafila'] = "S";
$this->query .= " LIMIT 10 ";
return $this->read($this->query, $data)->fetchAll();;
$this->query .= " ORDER BY nome LIMIT 10 ";
return $this->read($this->query, $data)->fetchAll();
}
public function findQueueByName($nome, $active = true)

408
app/Models/SupervisorModel.php

@ -0,0 +1,408 @@
<?php
namespace app\Models;
use app\Core\Model;
class SupervisorModel extends Model
{
const USUARIOS = "pbx_usuarios";
const SUPERVISOR_AGENTE = "md_supervisor";
const EVENTO_AGENTE = 'pbx_eventos_agentes';
const BILHETE = 'pbx_bilhetes';
const EVENTOS_DACS = 'pbx_eventos_dacs';
private $supervisor = 'md_supervisor';
private $atendimento = 'md_atendimento';
public function listaAgentesDisponivel($status = null, $somenteLivre = true)
{
$data = [];
$this->query = " SELECT *,
(
SELECT
count(*)
FROM
md_atendimento ma
WHERE 'START' = (SELECT m2.evento FROM md_evento m2
WHERE ma.uniqueid = m2.uniqueid
ORDER BY m2.id DESC LIMIT 1)
AND ma.matricula = ms.matricula
) AS countAtendimentos,
(
SELECT
count(*)
FROM
md_atendimento ma
WHERE ma.data_reg >= current_date
AND ma.matricula = ms.matricula
) AS numero_atendimento_dia
FROM md_supervisor ms
WHERE 1=1
";
if ($somenteLivre) {
$this->query .= " AND (
SELECT
count(*)
FROM
md_atendimento ma
WHERE 'START' = (SELECT m2.evento FROM md_evento m2
WHERE ma.uniqueid = m2.uniqueid
ORDER BY m2.id DESC LIMIT 1)
AND ma.matricula = ms.matricula
) < (SELECT prm_media_simultaneo FROM pbx_parametros pp LIMIT 1 )";
}
if ($status) {
$this->query .= " AND ms.status = :status ";
$data['status'] = $status;
}
$this->query .= " ORDER BY countAtendimentos ";
return $this->read($this->query, $data)->fetchAll();
}
public function statusAgente($matricula)
{
$this->query = "SELECT
*,
(
SELECT
count(*)
FROM
md_atendimento ma
WHERE 'START' = (SELECT m2.evento FROM md_evento m2
WHERE ma.uniqueid = m2.uniqueid
ORDER BY m2.id DESC LIMIT 1)
AND ma.matricula = ms.matricula
) AS numero_atendimento
FROM
md_supervisor ms
WHERE ms.matricula = :matricula ";
return $this->read($this->query, ['matricula' => $matricula])->fetch();
}
public function findByMatricula($matricula)
{
//Codigo acrescentado para não permitir logar o agente no chat e pabx simultaneamente.
$this->query = "SELECT matricula from ( SELECT matricula FROM " . self::SUPERVISOR_AGENTE
. " UNION "
. " SELECT matricula FROM pbx_supervisor_agentes ) AS agt WHERE (matricula = :matricula)";
return $this->read($this->query, ['matricula' => $matricula])->fetch();
}
public function findAllAgentes($queue = null)
{
$data = [];
$this->query = "SELECT * FROM " . self::SUPERVISOR_AGENTE . " WHERE 1=1 ";
if ($queue) {
$this->query .= " AND fila = :queue ";
$data['queue'] = $queue;
}
return $this->read($this->query, $data)->fetchAll();
}
public function findAllAgentesPBX($queue = null, $media = null)
{
$data = [];
$this->query = "SELECT * FROM pbx_supervisor_agentes WHERE 1=1 ";
if ($queue) {
$this->query .= " AND fila = :queue ";
$data['queue'] = $queue;
}
if($media){
$this->query .= " AND media <> :media";
$data['media'] = $media;
}
return $this->read($this->query, $data)->fetchAll();
}
public function findByAgent($matricula)
{
$this->query = "SELECT * FROM " . self::USUARIOS . " WHERE matricula = :matricula;";
return $this->read($this->query, ['matricula' => $matricula])->fetch();
}
public function findAgentByRamal($ramal)
{
$this->query = "SELECT * FROM " . self::SUPERVISOR_AGENTE . " WHERE ramal = :ramal;";
return $this->read($this->query, ['ramal' => $ramal])->fetch();
}
public function findAgentByMatricula($matricula)
{
$this->query = "SELECT * FROM " . self::SUPERVISOR_AGENTE . " WHERE matricula = :matricula;";
return $this->read($this->query, ['matricula' => $matricula])->fetch();
}
public function findAgentByMatriculaPbx($matricula)
{
$this->query = "SELECT * FROM pbx_supervisor_agentes WHERE matricula = :matricula;";
return $this->read($this->query, ['matricula' => $matricula])->fetch();
}
public function findAgentByQueue($queue, $status)
{
$data = [];
$this->query = "SELECT *
FROM md_supervisor ms
WHERE ms.fila = :queue
AND ms.status = :status
ORDER BY (
SELECT
count(*)
FROM
md_atendimento ma
WHERE 'START' = (SELECT m2.evento FROM md_evento m2
WHERE ma.uniqueid = m2.uniqueid
ORDER BY m2.id DESC LIMIT 1)
AND ma.matricula = ms.matricula
)
";
$data['queue'] = $queue;
$data['status'] = $status;
return $this->read($this->query, $data)->fetchAll();
}
########################################
### SUPERVISOR AGENTE ###
########################################
public function addAgent($matricula, $fila, $nome, $chamada_classificado = 1)
{
$this->query = "INSERT INTO " . self::SUPERVISOR_AGENTE . "
(
matricula,
tempo_login,
fila,
status,
duracao,
chamada_classificado,
nome
)
VALUES(
:matricula,
:tempo_login,
:fila,
:status,
:duracao,
:chamada_classificado,
:nome
);";
return $this->create($this->query, [
'matricula' => $matricula,
'tempo_login' => 'now()',
'fila' => $fila,
'status' => 'LIVRE',
'duracao' => 'now()',
'nome' => $nome,
'chamada_classificado' => $chamada_classificado
]);
}
public function addAgent2(
$nome,
$matricula,
$ramal,
$dac,
$tempo_login,
$status = 'LIVRE',
$motivoPausa = null,
$origemDestino = null,
$media = 1,
$modo_atendimento = 'Automatico',
$chamada_classificado = 1
) {
$data = [];
$this->query = "INSERT INTO pbx_supervisor_agentes (
tempo_login,
nome,
ramal,
matricula,
origem_destino,
status,
motivo_pausa,
chamada_classificado,
modo_atendimento,
dac,
duracao,
logado,
media) VALUES(
:tempo_login,
:nome,
:ramal,
:matricula,
:origem_destino,
:status,
:motivo_pausa,
:chamada_classificado,
:modo_atendimento,
:dac,
:duracao,
:logado,
:media) ";
$data['tempo_login'] = $tempo_login;
$data['nome'] = $nome;
$data['matricula'] = $matricula;
$data['ramal'] = $ramal;
$data['duracao'] = 'now()';
$data['logado'] = 'now()';
$data['dac'] = $dac;
$data['origem_destino'] = $origemDestino;
$data['status'] = $status;
$data['motivo_pausa'] = $motivoPausa;
$data['media'] = $media;
$data['modo_atendimento'] = $modo_atendimento;
$data['chamada_classificado'] = ($chamada_classificado ? '1' : '0');
return $this->create($this->query, $data);
}
public function updateAgent(
$matricula,
$status = 'LIVRE',
$motivo_pausa = null
) {
$this->query = "UPDATE {$this->supervisor} SET status = :status, motivo_pausa = :motivo_pausa, duracao = :duracao";
$this->query .= " WHERE matricula = :matricula";
$data = [];
$data['status'] = $status;
$data['motivo_pausa'] = $motivo_pausa;
$data['matricula'] = $matricula;
$data['duracao'] = 'now()';
// logger('teste')->debug(print_r($data, true), true);
return $this->update($this->query, $data);
}
public function updateAgent2(
$matricula,
$ramal,
$dac,
$status = 'LIVRE',
$atualizaDuracao = false,
$motivoPausa = null,
$origemDestino = null,
$disponivel_atendimento = 1
) {
$data = [];
$this->query = "UPDATE pbx_supervisor_agentes SET
origem_destino = :origem_destino,
status = :status,
motivo_pausa = :motivo_pausa,
dac = :dac,
ramal = :ramal,
logado = :logado,
disponivel_atendimento = :disponivel_atendimento ";
if ($atualizaDuracao) {
$data['duracao'] = 'now()';
$this->query .= " , duracao = :duracao";
}
$this->query .= " WHERE matricula = :matricula";
$data['matricula'] = $matricula;
$data['ramal'] = $ramal;
$data['dac'] = $dac;
$data['origem_destino'] = $origemDestino;
$data['status'] = $status;
$data['logado'] = 'now()';
$data['motivo_pausa'] = $motivoPausa;
$data['disponivel_atendimento'] = $disponivel_atendimento;
return $this->update($this->query, $data);
}
public function updateRefreshAgent($matricula, $ramal, $duracao = false)
{
$data = [];
$this->query = "UPDATE " . self::SUPERVISOR_AGENTE . " SET logado = :logado ";
if ($duracao) {
$this->query .= ", duracao = :duracao ";
$data['duracao'] = 'now()';
}
$this->query .= " WHERE matricula = :matricula AND ramal = :ramal;";
$data['logado'] = 'now()';
$data['matricula'] = $matricula;
$data['ramal'] = $ramal;
return $this->update($this->query, $data);
}
public function updateSala2Agent($matricula, $ramal, $sala2 = null)
{
$data = [];
$this->query = "UPDATE " . self::SUPERVISOR_AGENTE . " SET sala_2 = :sala_2 WHERE matricula = :matricula AND ramal = :ramal;";
$data['sala_2'] = $sala2;
$data['matricula'] = $matricula;
$data['ramal'] = $ramal;
return $this->update($this->query, $data);
}
public function deleteAgentPbx($matricula, $media = null, $oper = '=')
{
$data = [];
$this->query = "DELETE FROM pbx_supervisor_agentes WHERE matricula = :matricula ";
if($media){
$this->query .= "AND media {$oper} :media ";
$data['media'] = $media;
}
$data['matricula'] = $matricula;
return $this->delete($this->query, $data);
}
public function deleteAgent($matricula)
{
$data = [];
$this->query = "DELETE FROM " . self::SUPERVISOR_AGENTE . " WHERE matricula = :matricula";
$data['matricula'] = $matricula;
return $this->delete($this->query, $data);
}
public function updateLogadoAll($media = null)
{
$data = [];
$this->query = "UPDATE " . self::SUPERVISOR_AGENTE . " SET logado = :logado ";
if ($media) {
$this->query .= " WHERE sala_1 = :sala_1; ";
}
$data['logado'] = 'now()';
$data['sala_1'] = $media;
return $this->update($this->query, $data);
}
########################################
### EVENTOS ###
########################################
public function addEventoLoginAgent($matricula, $idDac, $flag = 1, $ramal)
{
$data = [];
$this->query = "INSERT INTO " . self::EVENTO_AGENTE . "(matricula, login, logoff, id_dac, flag, ramal) VALUES(:matricula, :login, :logoff, :id_dac, :flag, :ramal) ";
$data['matricula'] = $matricula;
$data['login'] = 'now()';
$data['logoff'] = 'now()';
$data['id_dac'] = $idDac;
$data['flag'] = $flag;
$data['ramal'] = $ramal;
return $this->create($this->query, $data);
}
public function updateEventoLogoffAgent($matricula, $ramal, $dac, $flag = 2)
{
$data = [];
$this->query = "UPDATE " . self::EVENTO_AGENTE . " SET logoff = :logoff, flag = :flag "
. "WHERE matricula = :matricula AND id_dac = :id_dac AND ramal = :ramal "
. "AND login = (SELECT MAX(login) FROM pbx_eventos_agentes WHERE matricula = :matricula AND id_dac = :id_dac AND ramal= :ramal);";
$data['logoff'] = 'now()';
$data['flag'] = $flag;
$data['matricula'] = $matricula;
$data['id_dac'] = $dac;
$data['ramal'] = $ramal;
return $this->update($this->query, $data);
}
}

34
app/Models/SystemMessage.php

@ -0,0 +1,34 @@
<?php
namespace app\Models;
use app\Core\Model;
/**
* Description of Ramal
*
* @author Lucas Awade
*/
class SystemMessage extends Model
{
private $table = 'md_system_message';
public function findMessage($momento, $fila = null)
{
$data = [];
$data['momento'] = $momento;
$this->query = "SELECT * FROM {$this->table} m WHERE 1=1";
if (!empty($fila)) {
$data['fila'] = $fila;
$this->query .= " AND (m.fila = :fila OR m.fila IS null)";
} else {
$this->query .= " AND m.fila IS null";
}
$this->query .= " AND m.momento = :momento ORDER BY ordem";
return $this->read($this->query, $data)->fetchAll();
}
}

6
app/Providers/ApiTelegram.php

@ -31,6 +31,10 @@ class ApiTelegram implements IApiMedia
$this->request = new RequestURL();
}
function convertToWebsocket($msg)
{
}
function enviarMsgIterativaLista($telegram, $mensagem, $nomeButton, $lista, $prex = '')
{
$this->debug = debug_backtrace();
@ -520,4 +524,4 @@ class ApiTelegram implements IApiMedia
{
return $this->hook['document']['file_name'];
}
}
}

227
app/Providers/ApiTwilio.php

@ -0,0 +1,227 @@
<?php
namespace app\Providers;
use app\Interfaces\IApiMedia;
use Twilio\Rest\Client;
class ApiTwilio implements IApiMedia
{
private $sid = 'ACab626d6f133aa20b21879d37cd21b139';
private $token = '131e5ed83468349ff93250ca72417d70';
private $numeroTwilio = '553140428280';
/** @var string $hook resposta do webhook */
private $hook;
function getContentType()
{
return 'text/xml';
}
function getchannel()
{
return CONF_WHATSAPP_CHANNEL;
}
function enviarMedia($whatsapp, $link, $type, $titulo = null)
{
/*
twilio só suporta esses formatos.
Images JPG, JPEG, PNG
Audio MP3, OGG, AMR
Documents PDF
Video MP4 (with H.264 video codec and AAC audio)
Contacts vCard (.vcf)*/
$twilio = new Client($this->sid, $this->token);
$message = $twilio->messages->create(
"whatsapp:+$whatsapp", // to
[
"from" => "whatsapp:+{$this->numeroTwilio}",
"mediaUrl" => [$link]
]
);
logger('twilio')->info(var_export($message->body, true));
return $message->body;
}
function enviarMsg($whatsapp, $mensagem, $encode = true)
{
if ($encode) {
$mensagem = utf8_encode($mensagem);
}
$twilio = new Client($this->sid, $this->token);
$message = $twilio->messages->create(
"whatsapp:+$whatsapp", // to
[
"from" => "whatsapp:+{$this->numeroTwilio}",
"body" => $mensagem,
]
);
logger('twilio')->info(var_export($message->body, true));
return $message->body;
}
function enviarMsgIterativaLista($whatsapp, $mensagem, $nomeButton, $sections)
{
}
function enviarMsgIterativaBotao($whatsapp, $mensagem, $buttons)
{
}
function enviarContato($whatsapp, $nome, $contato)
{
}
function enviarLocalizacao($whatsapp, $longitude, $latitude, $nome = null, $endereco = null)
{
}
function baixarMidia()
{
$request = new Requests();
if (in_array($this->getType(), ['location', 'contacts', 'text'])) {
return true;
}
logger('baixarMidia')->info('url: ' . $this->hook['MediaUrl0']);
$request->setUrl($this->hook['MediaUrl0']);
$name = $this->getId();
$request->requestType("GET");
$request->setMetodo('');
$pathfile = $request->storage . $name;
$retorno = $request->exec();
file_put_contents($pathfile, $retorno);
if (file_exists($pathfile)) {
return true;
}
return false;
}
function getProfile()
{
return $this->hook['ProfileName'];
}
function getPhone()
{
return $this->hook['WaId'];
}
function getType()
{
$type = $this->hook['MediaContentType0'];
if ($type) {
$type = explode('/', $type);
if ($type[0] == 'application') {
return 'document';
}
return $type[0];
}
return 'text';
}
function getMimetype()
{
return $this->hook['MediaContentType0'];
}
function getId()
{
return $this->hook['MessageSid'];
}
function getIsValidMessage()
{
logger('getIsValidMessage')->info('getIsValidMessage: ' + is_array($this->hook));
return is_array($this->hook);
}
function getMessage()
{
return $this->hook['Body'];
}
function setMessage($msg)
{
$this->hook['Body'] = $msg;
}
function getContactFormatted()
{
return false;
}
function getContactPhone()
{
return false;
}
function getGeolocation($type)
{
return false;
}
function setHook($hook)
{
//$array = "SmsMessageSid=SM3f3e0cac9d0da519ec0ffa7f15eaa15b&NumMedia=0&ProfileName=Lucas&SmsSid=SM3f3e0cac9d0da519ec0ffa7f15eaa15b&WaId=556596107663&SmsStatus=received&Body=sdsdfads&To=whatsapp%3A%2B14155238886&NumSegments=1&MessageSid=SM3f3e0cac9d0da519ec0ffa7f15eaa15b&AccountSid=ACab626d6f133aa20b21879d37cd21b139&From=whatsapp%3A%2B556596107663&ApiVersion=2010-04-01";
$array = explode("&", urldecode($hook));
$map = [];
foreach ($array as $key => $value) {
$auxi = $array = explode("=", $value);
$map[$auxi[0]] = $auxi[1];
}
// mensagem normal
// [SmsMessageSid] => SM3f3e0cac9d0da519ec0ffa7f15eaa15b
// [NumMedia] => 0
// [ProfileName] => Lucas
// [SmsSid] => SM3f3e0cac9d0da519ec0ffa7f15eaa15b
// [WaId] => 556596107663
// [SmsStatus] => received
// [Body] => sdsdfads
// [To] => whatsapp%3A%2B14155238886
// [NumSegments] => 1
// [MessageSid] => SM3f3e0cac9d0da519ec0ffa7f15eaa15b
// [AccountSid] => ACab626d6f133aa20b21879d37cd21b139
// [From] => whatsapp%3A%2B556596107663
// [ApiVersion] => 2010-04-01
// imagem
// [MediaContentType0] => image/jpeg
// [SmsMessageSid] => MM0e531d626f74ec950e637d7491b0080c
// [NumMedia] => 1
// [ProfileName] => Lucas
// [SmsSid] => MM0e531d626f74ec950e637d7491b0080c
// [WaId] => 556596107663
// [SmsStatus] => received
// [Body] =>
// [To] => whatsapp:+14155238886
// [NumSegments] => 1
// [MessageSid] => MM0e531d626f74ec950e637d7491b0080c
// [AccountSid] => ACab626d6f133aa20b21879d37cd21b139
// [From] => whatsapp:+556596107663
// [MediaUrl0] => https://api.twilio.com/2010-04-01/Accounts/ACab626d6f133aa20b21879d37cd21b139/Messages/MM0e531d626f74ec950e637d7491b0080c/Media/ME4ff81dee03f2cd1b7875d87954e8abd7
// [ApiVersion] => 2010-04-01
// documento n�o valido
// [SmsMessageSid] => MM2f04ee178bf6843dd26ac19e6e193c19
// [NumMedia] => 0
// [ProfileName] => Lucas
// [SmsSid] => MM2f04ee178bf6843dd26ac19e6e193c19
// [WaId] => 556596107663
// [SmsStatus] => received
// [Body] => INSTALA��O WHATSAPP.docx
// [To] => whatsapp:+553140428280
// [NumSegments] => 1
// [MessageSid] => MM2f04ee178bf6843dd26ac19e6e193c19
// [AccountSid] => ACab626d6f133aa20b21879d37cd21b139
// [From] => whatsapp:+556596107663
// [ApiVersion] => 2010-04-01
// documento pdf
// [MediaContentType0] => application/pdf
// [SmsMessageSid] => MM1042d93ac503a7468b7d3fb3a0ca77d5
// [NumMedia] => 1
// [ProfileName] => Lucas
// [SmsSid] => MM1042d93ac503a7468b7d3fb3a0ca77d5
// [WaId] => 556596107663
// [SmsStatus] => received
// [Body] => Cap1-_atlas_.pdf
// [To] => whatsapp:+553140428280
// [NumSegments] => 1
// [MessageSid] => MM1042d93ac503a7468b7d3fb3a0ca77d5
// [AccountSid] => ACab626d6f133aa20b21879d37cd21b139
// [From] => whatsapp:+556596107663
// [MediaUrl0] => https://api.twilio.com/2010-04-01/Accounts/ACab626d6f133aa20b21879d37cd21b139/Messages/MM1042d93ac503a7468b7d3fb3a0ca77d5/Media/ME8fead4a393c68d2fcd14bdff03cc8e74
// [ApiVersion] => 2010-04-01
$this->hook = $map;
}
function retornaTituloDocument()
{
return $this->hook['Body'];
}
}

325
app/Providers/Positus.php

@ -3,45 +3,64 @@
namespace app\Providers;
use app\Interfaces\IApiMedia;
use app\Providers\RequestURL;
class Positus implements IApiMedia
class Positus extends Requests implements IApiMedia
{
/** @var string $hook resposta do webhook */
private $hook;
private $token;
private $url;
private $metodo;
function getContentType()
{
return "application/json; charset=UTF-8";
}
########################################################################
## VARIAVEIS DA CLASSE ##
########################################################################
private $query;
private $requestType;
private $request;
private $params = array();
private $hook;
private $storage = "/var/www/html/aplicativo/audio/";
public $channel = CONF_WHATSAPP_CHANNEL;
public $timeout_client_resposta = CONF_WHATSAPP_TIMEOUT_CLIENT_RESPOSTA;
public function getchannel()
{
return CONF_WHATSAPP_CHANNEL;
}
########################################################################
## RECURSOS DA API ##
########################################################################
function setHook($hook)
{
$this->hook = json_decode($hook, true);
}
function enviarMsg($whatsapp, $mensagem)
function setToken()
{
$this->token = CONF_WHATSAPP_AUTH_TOKEN;
}
function enviarMedia($whatsapp, $link, $type, $titulo = null)
{
$tipos = [];
$tipos['link'] = $link;
if (!empty($titulo)) {
$tipos['caption'] = $titulo;
}
$this->debug = debug_backtrace();
if ($this->getArgs(func_get_args())) {
$this->params = array(
"to" => "+$whatsapp",
"type" => "text",
"text" => array("body" => "$mensagem")
);
$this->requestType("POST");
$this->setMetodo('messages');
return $this->exec();
$this->params = [
"to" => "+$whatsapp",
"type" => "$type",
"$type" => $tipos
];
$this->requestType("POST");
$this->setMetodo('messages');
return $this->exec();
}
function enviarMsg($whatsapp, $mensagem, $encode = true)
{
if ($encode) {
$mensagem = utf8_encode($mensagem);
}
return false;
$this->debug = debug_backtrace();
$this->params = array(
"to" => "+$whatsapp",
"type" => "text",
"text" => array("body" => "$mensagem")
);
$this->requestType("POST");
$this->setMetodo('messages');
return $this->exec();
}
function enviarMsgIterativaLista($whatsapp, $mensagem, $nomeButton, $lista, $prex = '')
@ -62,11 +81,9 @@ class Positus implements IApiMedia
)
)
);
logger('positus')->info(print_r($this->params, true));
$this->requestType("POST");
$this->setMetodo('messages');
$ret = $this->exec();
logger('positus')->info(print_r($ret, true));
return $ret;
}
return false;
@ -111,11 +128,9 @@ class Positus implements IApiMedia
)
)
);
logger('positus')->info(print_r($this->params, true));
$this->requestType("POST");
$this->setMetodo('messages');
$ret = $this->exec();
logger('positus')->info(print_r($ret, true));
return $ret;
}
return false;
@ -125,7 +140,7 @@ class Positus implements IApiMedia
function enviarContato($whatsapp, $nome, $contato)
{
$this->debug = debug_backtrace();
if ($this->getArgs(func_get_args())) {
if ($whatsapp) {
$this->params = array(
"to" => "+$whatsapp",
"type" => "contacts",
@ -173,104 +188,35 @@ class Positus implements IApiMedia
return false;
}
function enviaDocumento($whatsapp, $link, $titulo = null)
{
$this->debug = debug_backtrace();
if ($this->getArgs(func_get_args())) {
$this->params = array(
"to" => "+$whatsapp",
"type" => "document",
"document" => array("link" => "$link", "caption" => "$titulo")
);
$this->requestType("POST");
$this->setMetodo('messages');
return $this->exec();
}
return false;
}
function enviaImagem($whatsapp, $link, $titulo = null)
{
$this->debug = debug_backtrace();
if ($this->getArgs(func_get_args())) {
$this->params = array(
"to" => "+$whatsapp",
"type" => "image",
"image" => array("link" => "$link", "caption" => "$titulo")
);
$this->requestType("POST");
$this->setMetodo('messages');
return $this->exec();
}
return false;
}
function enviaSticker($whatsapp, $link)
{
$this->debug = debug_backtrace();
if ($this->getArgs(func_get_args())) {
$this->params = array(
"to" => "+$whatsapp",
"type" => "sticker",
"sticker" => array("link" => "$link")
);
$this->requestType("POST");
$this->setMetodo('messages');
return $this->exec();
}
return false;
}
function enviaVideo($whatsapp, $link, $titulo = null)
{
$this->debug = debug_backtrace();
if ($this->getArgs(func_get_args())) {
$this->params = array(
"to" => "+$whatsapp",
"type" => "video",
"video" => array("link" => "$link", "caption" => "$titulo")
);
$this->requestType("POST");
$this->setMetodo('messages');
return $this->exec();
}
return false;
}
function enviaAudio($whatsapp, $link)
function baixarMidia()
{
$this->debug = debug_backtrace();
if ($this->getArgs(func_get_args())) {
$this->params = array(
"to" => "+$whatsapp",
"type" => "audio",
"audio" => array("link" => "$link")
);
$this->requestType("POST");
$this->setMetodo('messages');
return $this->exec();
if (in_array($this->getType(), ['location', 'contacts', 'text'])) {
return true;
}
return false;
}
function baixarMidia($name)
{
$name = $this->getId();
$this->debug = debug_backtrace();
if ($this->getArgs(func_get_args())) {
$this->requestType("GET");
$this->setMetodo('media/' . $name);
$file = $this->storage . $name;
file_put_contents($file, $this->exec());
if (file_exists($file)) {
return $file;
$pathfile = $this->storage . $name;
$retorno = $this->exec();
file_put_contents($pathfile, $retorno);
if (file_exists($pathfile)) {
return true;
}
}
return false;
}
function setStorage($storage)
function convertWavToMp3($fileOrig)
{
$this->storage = $storage;
try {
// ffmpeg -i 61f1a021ca8908.72216404_1643308267776 61f1a021ca8908.72216404_1643308267776
$cmd = "ffmpeg -y -i $fileOrig $fileOrig.mp3";
exec($cmd);
copy($fileOrig . ".mp3", $fileOrig);
} catch (\Exception $th) {
logger('ConvertWavToMp3')->error($th->getMessage());
}
}
/**
@ -310,7 +256,7 @@ class Positus implements IApiMedia
public function getMimetype()
{
if ($this->hook['messages'][0][$this->getType()]['mime_type']) {
return base64_encode($this->hook['messages'][0][$this->getType()]['mime_type']);
return $this->hook['messages'][0][$this->getType()]['mime_type'];
}
return false;
}
@ -322,6 +268,9 @@ class Positus implements IApiMedia
public function getId()
{
if ($this->hook && $this->getType()) {
if ($this->getType() == 'text') {
return $this->hook['messages'][0]['id'];
}
return $this->hook['messages'][0][$this->getType()]['id'];
}
return false;
@ -333,8 +282,7 @@ class Positus implements IApiMedia
*/
public function getIsValidMessage()
{
$message = $this->hook['messages'];
return ($message ? $message : false);
return $this->hook['contacts'];
}
/**
@ -343,7 +291,6 @@ class Positus implements IApiMedia
*/
public function getMessage()
{
logger('positus')->info(print_r($this->hook, true));
if ($this->hook['messages'][0]['interactive']) {
if ($this->hook['messages'][0]['interactive']['list_reply']) {
$id = $this->hook['messages'][0]['interactive']['list_reply']['id'];
@ -358,7 +305,10 @@ class Positus implements IApiMedia
$message = $this->hook['messages'][0]['text']['body'];
return ($message ? $message : false);
}
public function setMessage($msg)
{
$this->hook['messages'][0]['text']['body'] = $msg;
}
/**
* Returns the name of the contact
* @return string|boolean
@ -398,125 +348,12 @@ class Positus implements IApiMedia
return false;
}
function setHook($hook)
{
$this->hook = $hook;
}
########################################################################
## FUNCOES DO SISTEMA ##
########################################################################
function __construct()
public function retornaTituloDocument()
{
$this->token = CONF_WHATSAPP_AUTH_TOKEN;
$this->url = CONF_WHATSAPP_AUTH_URL;
$this->request = new RequestURL();
}
function setMetodo($metodo)
{
$this->metodo = $metodo;
}
/**
* Escreve a query para ser passada para o curl
*
* @param string $query
*/
function setQuery($query)
{
return $this->query .= $query;
}
/**
* retorna a string pronta da query do curl e limpa a variavel.
*
* @return string $query
*/
function getQuery()
{
$query = $this->query;
unset($this->query);
return $query;
}
/**
* Verifica se todos os parametros passados foram completados.
*
* @param array $args
* @return true|false
*/
function getArgs($args)
{
foreach ($args as $value) {
if (!$value) {
return false;
}
if ($this->hook['messages'][0]['document']) {
return $this->hook['messages'][0]['document']['filename'];
}
return true;
}
/**
* Recebe o tipo de Requisi<EFBFBD><EFBFBD>o GET/POST
*
* @return boolean
*/
function requestType($req = null)
{
if (!$req) {
return $this->requestType;
}
if (strtoupper($req) == "GET") {
return $this->requestType = "GET";
} else if (strtoupper($req) == "POST") {
return $this->requestType = "POST";
}
}
function exec()
{
$this->setQuery(json_encode($this->params)); //SET QUERY
$this->request->setUrl($this->url . $this->metodo);
$header = array();
$header[] = "Authorization: Bearer {$this->token}";
if ($this->requestType == 'POST') {
$header[] = 'Content-Type: application/json';
$this->request->post_field($this->getQuery(), true);
}
$this->request->header($header);
$this->request->method_request($this->requestType);
$response = $this->request->exec_request();
return $this->response($response);
}
function response($result)
{
if ($result) {
if (json_decode($result, true) !== null) {
return json_decode($result, true);
}
return $result;
} else {
return false;
}
}
/**
* Create file and download in browser
*/
public function getLinkDownload($host)
{
return $host . "whatsapp/download/{$this->getId()}/{$this->getMimetype()}";
}
public function retornaTituloDocument($msg)
{
$titulo = $msg['REQUEST']['messages'][0]['document']['filename'];
$titulo = substr($titulo, 0, strrpos($titulo, "."));
return $titulo;
return null;
}
}

2
app/Providers/RequestURL.php

@ -70,6 +70,7 @@ class RequestURL
private function response($result)
{
// logger('deburguer')->info(print_r($result, true));
if ($result) {
if (json_decode($result, true) !== null) {
return json_decode($result, true);
@ -85,6 +86,7 @@ class RequestURL
private function conf_request()
{
curl_setopt($this->curl, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($this->curl, CURLOPT_HTTPHEADER, $this->header);
curl_setopt($this->curl, CURLOPT_CUSTOMREQUEST, $this->method);
curl_setopt($this->curl, CURLOPT_RETURNTRANSFER, true);

139
app/Providers/Requests.php

@ -0,0 +1,139 @@
<?php
namespace app\Providers;
class Requests
{
/** @var string $token token de autenticação das requesições */
protected $token;
/** @var string $url url base das requsições */
protected $url;
/** @var string $metodo endpoint da requsição */
protected $metodo;
/** @var string $query query da requsição */
protected $query;
/** @var string $query tipo da requsição */
protected $requestType;
/** @var array $params parametros da request */
protected $params = array();
/** @var RequestURL $request description */
protected $request;
/** @var string $contentType tipo da requsição */
protected $contentType;
/** @var string $storage caminho dos arquivos */
public $storage = CONF_PATH_FILES;
function __construct()
{
$this->request = new RequestURL();
$this->setToken();
$this->setUrl(CONF_WHATSAPP_AUTH_URL);
}
function response($result)
{
if ($result) {
if (json_decode($result, true) !== null) {
return json_decode($result, true);
}
return $result;
} else {
return false;
}
}
function exec()
{
$this->setQuery(json_encode($this->params)); //SET QUERY
$this->request->setUrl($this->url . $this->metodo, false);
$header = array();
$header[] = "Authorization: Bearer {$this->token}";
if ($this->requestType == 'POST') {
$header[] = 'Content-Type: application/json';
$this->request->post_field($this->getQuery(), true);
}
$this->request->header($header);
$this->request->method_request($this->requestType);
return $this->request->exec_request();
}
/**
* Recebe o tipo de Requisi��o GET/POST
*
* @return boolean
*/
function requestType($req = null)
{
if (!$req) {
return $this->requestType;
}
if (strtoupper($req) == "GET") {
return $this->requestType = "GET";
} else if (strtoupper($req) == "POST") {
return $this->requestType = "POST";
}
}
/**
* Verifica se todos os parametros passados foram completados.
*
* @param array $args
* @return true|false
*/
function getArgs($args)
{
foreach ($args as $value) {
if (!$value) {
return false;
}
}
return true;
}
function setMetodo($metodo)
{
$this->metodo = $metodo;
}
/**
* Escreve a query para ser passada para o curl
*
* @param string $query
*/
function setQuery($query)
{
return $this->query .= $query;
}
/**
* retorna a string pronta da query do curl e limpa a variavel.
*
* @return string $query
*/
function getQuery()
{
$query = $this->query;
unset($this->query);
return $query;
}
function setToken()
{
$this->token = CONF_WHATSAPP_AUTH_TOKEN;
}
public function setUrl($url)
{
$this->url = $url;
}
}

9
app/Providers/WebHeader.php

@ -130,11 +130,11 @@ class WebHeader
return $this->config;
}
public function API()
public function API($contentType)
{
$this->methods([
"Access-Control-Allow-Origin" => ['*'],
"Content-Type" => "application/json; charset=UTF-8",
"Content-Type" => $contentType,
"Access-Control-Allow-Methods" => ['GET', 'POST', 'PUT', 'DELETE'],
"Access-Control-Max-Age" => 0,
"Access-Control-Allow-Headers" => ['Origin', 'X-Requested-With', 'Content-Type', 'Accept', 'Authorization']
@ -164,6 +164,7 @@ class WebHeader
public function fileTransfer($name, $file, $mimetype)
{
//logger('logggeeeee')->info(basename($name . "." . explode('/', $mimetype)[1]));
$this->methods([
"Content-Description" => 'File Transfer',
"Content-Transfer-Encoding" => "binary",
@ -172,13 +173,13 @@ class WebHeader
"Content-Length" => filesize($file),
"Content-Disposition" => "attachment; filename=" . basename($name . "." . explode('/', $mimetype)[1]),
"Expires" => 0,
"Connection" => 'close',
"Pragma" => 'public'
]);
$this->bootstrap();
ob_end_clean();
ob_start();
readfile($file);
unlink($file);
ob_flush();
}
@ -255,4 +256,4 @@ class WebHeader
{
return $this->methods;
}
}
}

9
app/Providers/whatsapp.php

@ -0,0 +1,9 @@
<?php
namespace app\Providers;
use app\Providers\ApiTwilio;
class Whatsapp extends ApiTwilio
{
}

4
bd

@ -1,4 +1,4 @@
HOST_DB="127.0.0.1"
HOST_DB="192.168.115.65"
BASE_DB="pbx"
USUARIO="contacte"
SENHA="ctepgSQL"
@ -6,4 +6,4 @@ PORTA_DB="5432"
HOST_SCK="127.0.0.1"
PORTA_SCK="5038"
USUARIO_SCK="manager"
SENHA_SCK="manager007"
SENHA_SCK="manager007"

27
composer.json

@ -1,17 +1,22 @@
{
"name": "simplesip/whatsapp",
"description": "Projeto WhatsApp",
"authors": [
{
"name": "Simples IP Desenvolvimento",
"email": "lucasawade46@gmail.com"
}
],
"authors": [{
"name": "Simples IP Desenvolvimento",
"email": "lucasawade46@gmail.com"
}],
"autoload": {
"psr-4": {
"app\\": "app/",
"scripts\\": "scripts/"
"app\\": "app/",
"scripts\\": "scripts/",
"websocket\\": "websocket/",
"service\\": "service/",
"tests\\": "tests/"
}
},
"require": {}
}
},
"require": {
"cboden/ratchet": "^0.4.4",
"textalk/websocket": "^1.3.1",
"twilio/sdk": "^5.42.2"
}
}

18
composer.lock generated

@ -1,18 +0,0 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "824e80b41f5059974728f7d2650347b2",
"packages": [],
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": [],
"platform-dev": [],
"plugin-api-version": "2.0.0"
}

1
config/agent.php

@ -11,4 +11,5 @@
define("CONF_AGENT_STATUS_LIVRE", 'LIVRE');
define("CONF_AGENT_STATUS_OCUPADO", 'OCUPADO');
define("CONF_AGENT_STATUS_PAUSA", 'PAUSA');
define("CONF_AGENT_STATUS_INDISPONIVEL", 'INDISPONIVEL');
define("CONF_AGENT_STATUS_CHAMANDO", 'CHAMANDO');

25
config/app.php

@ -30,7 +30,7 @@ define("CONF_LOG_PATH", '/var/www/html/aplicativo/integracao/media/storage/log/'
|
*/
define('CONF_MIDDLEWARE_REDIRECT', '192.168.115.65');
define('CONF_MIDDLEWARE_LINKUPLOAD', 'http://devwpp.simplesip.com.br:8090/integracao/media/');
define('CONF_MIDDLEWARE_LINKUPLOAD', 'http://voip.simplesip.com.br/integracao/media/link/');
/*
@ -42,12 +42,22 @@ define('CONF_MIDDLEWARE_LINKUPLOAD', 'http://devwpp.simplesip.com.br:8090/integr
|
*/
define('CONF_DB_DRIVER', 'pgsql');
define('CONF_DB_HOST', '127.0.0.1');
define('CONF_DB_HOST', '192.168.115.65');
define('CONF_DB_PORT', '5432');
define('CONF_DB_BASE', 'pbx');
define('CONF_DB_USER', '');
define('CONF_DB_PASSWD', '');
/*
|--------------------------------------------------------------------------
| Configuracao de MEDIA SIMPLES IP
|--------------------------------------------------------------------------
|
| Configuração para a media do PBX
|
*/
define('CONF_MEDIA_PBX', 99);
/*
|--------------------------------------------------------------------------
| Configuracao de Protocolo
@ -56,4 +66,13 @@ define('CONF_DB_PASSWD', '');
| Configuração geral de protocolo
|
*/
define('CONF_PROTOCOL_TENTATIVAS_GERAR', 5);
define('CONF_PROTOCOL_TENTATIVAS_GERAR', 5);
define('CONF_PATH_FILES', '/var/www/html/aplicativo/integracao/media/storage/files/');
/*
|--------------------------------------------------------------------------
| Informações da aplicação
|--------------------------------------------------------------------------
*/
define('INFO_VERSION_SYSTEM', '1.0.0');

2
config/display_errors.php

@ -1,6 +1,6 @@
<?php
error_reporting(E_ERROR | E_WARNING);
error_reporting(E_ERROR);
ini_set('display_errors', CONF_LOG_DISPLAY_ACTIVE);
ini_set("memory_limit", "512M");

15
config/event.php

@ -10,11 +10,14 @@
*/
// define("CONF_EVENT_WHATSAPP_ATENDIDA", 'MD_ATENDIDA');
define("CONF_EVENT_WHATSAPP_ESPERA", 'MD_EMESPERA');
define("CONF_EVENT_ESPERA", 'EMESPERA');
// define("CONF_EVENT_WHATSAPP_CANCELADA", 'MD_CANCELADO');
define("CONF_EVENT_WHATSAPP_TIMEOUT_CLIENTE", 'MD_TIMEOUT_CALLER');
define("CONF_EVENT_WHATSAPP_TIMEOUT_AGENTE", 'MD_TIMEOUT_AGENT');
define("CONF_EVENT_WHATSAPP_TIMERMINO_CLIENTE", 'MD_COMPLETE_CALLER');
define("CONF_EVENT_WHATSAPP_TIMERMINO_AGENTE", 'MD_COMPLETE_AGENT');
define("CONF_EVENT_WHATSAPP_ABANDONADA", 'MD_ABANDON');
define("CONF_EVENT_TIMEOUT_CLIENTE", 'TIMEOUT_CALLER');
define("CONF_EVENT_TIMEOUT_AGENTE", 'TIMEOUT_AGENT');
define("CONF_EVENT_TIMERMINO_CLIENTE", 'COMPLETE_CALLER');
define("CONF_EVENT_TIMERMINO_AGENTE", 'COMPLETE_AGENT');
define("CONF_EVENT_ABANDONADA", 'ABANDON');
define("CONF_EVENT_TRANSFER", 'TRANSFER');
define("CONF_EVENT_START", 'START');
define("CONF_EVENT_ERRO_ATEND", 'LOST_CONNECTION');
// define("CONF_EVENT_WHATSAPP_OCUPADO", 'MD_OCUPADO');

57
config/helpers.php

@ -1,6 +1,9 @@
<?php
use app\Interfaces\IApiMedia;
use app\Providers\ApiTelegram;
use app\Providers\Logger;
use app\Providers\Whatsapp;
function logger($name = null, $active = true)
{
@ -13,4 +16,58 @@ function whereIn($array)
return "'" . implode("','", $array) . "'";
}
return "'$array'";
}
/**
* undocumented function summary
*
* Undocumented function long description
*
* @param Type $var Description
* @return IApiMedia
* @throws conditon
**/
function returnChannel($channel)
{
switch ($channel) {
case CONF_WHATSAPP_CHANNEL:
return new Whatsapp();
case CONF_TELEGRAM_CHANNEL:
return new ApiTelegram();
}
}
function ConvertWavToMp3($fileOrig)
{
try {
// ffmpeg -i 61f1a021ca8908.72216404_1643308267776 61f1a021ca8908.72216404_1643308267776
$cmd = "ffmpeg -y -i $fileOrig $fileOrig.mp3";
exec($cmd);
copy($fileOrig . ".mp3", $fileOrig);
} catch (\Exception $th) {
throw $th;
}
//retorna uma string sem acentos
function removeAcentos($str, $upper = False)
{
$text = array('À', 'Á', 'Â', 'Ã', 'Ä', 'Å', 'Æ', 'Ç', 'È', 'É', 'Ê', 'Ë', 'Ì', 'Í', 'Î', 'Ï', 'Ð', 'Ñ', 'Ò', 'Ó', 'Ô', 'Õ', 'Ö', 'Ø', 'Ù', 'Ú', 'Û', 'Ü', 'Ý', 'Þ', 'ß', 'à', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 'è', 'é', 'ê', 'ë', 'ì', 'í', 'î', 'ï', 'ð', 'ñ', 'ò', 'ó', 'ô', 'õ', 'ö', 'ø', 'ù', 'ú', 'û', 'ü', 'ý');
$subs = array('A', 'A', 'A', 'A', 'A', 'A', 'A', 'C', 'E', 'E', 'E', 'E', 'I', 'I', 'I', 'I', 'D', 'N', 'O', 'O', 'O', 'O', 'O', 'O', 'U', 'U', 'U', 'U', 'U', 'P', 'B', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'c', 'e', 'e', 'e', 'e', 'i', 'i', 'i', 'i', 'o', 'n', 'o', 'o', 'o', 'o', 'o', 'o', 'u', 'u', 'u', 'u', 'y');
for ($i = 0; $i < strlen($str); $i++) {
$j = array_search($str[$i], $text);
if ($j)
$str[$i] = $subs[$j];
}
if ($upper)
$str = strtoupper($str);
return ($str);
}
//retorna uma string sem acentos
function removeAcentosArray(array $str)
{
foreach ($str as $key => $value) {
$str[$key] = RemoveAcentos($value);
}
return ($str);
}

18
config/moments.php

@ -0,0 +1,18 @@
<?php
/*
|--------------------------------------------------------------------------
| Momonts
|--------------------------------------------------------------------------
|
| Eventos utilizados pelo gerenciamento dos atendimentos.
|
*/
define("CONF_MOMENT_SAUDACAO", 'SAUDACAO');
define("CONF_MOMENT_INICIAR_ATENDIMENTO", 'INICIAR_ATENDIMENTO');
define("CONF_MOMENT_FINALIZAR_ATENDIMENTO", 'FINALIZAR_ATENDIMENTO');
define("CONF_MOMENT_CANCELAR_FILA", 'CANCELAR_FILA'); //entrou na fila mas n tem agente logado
define("CONF_MOMENT_ENTRAR_FILA_SEM", 'ENTRAR_FILA_SEM'); //entrou na fila mas n tem agente livre
define("CONF_MOMENT_ENTRAR_FILA_COM", 'ENTRAR_FILA_COM');
define("CONF_MOMENT_ERRO_ATEND", 'LOST_CONNECTION');// quando estiver em atendimento e perder a conexão com websocket

4
config/whatsapp.php

@ -9,7 +9,7 @@
|
*/
define("CONF_WHATSAPP_AUTH_TOKEN", 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJhdWQiOiIxIiwianRpIjoiNGViZDYxOTJmMThhNTU2OGVlMDQ5ZDM3NDYzYTZmNDg5YjcxYjQyMGNhYzZmYWM0OWI5MjBkMmIxM2MzMzFmZDExMjAzMzhiZDhiODE5NWUiLCJpYXQiOjE2MjA4MzI1MTYsIm5iZiI6MTYyMDgzMjUxNiwiZXhwIjoxNjUyMzY4NTE2LCJzdWIiOiIyOTQ1Iiwic2NvcGVzIjpbXX0.mPLDDdSZuL6XlZbJ_xRNxebZgKKnYPS7Ecuit_8nPKRwLkY4MCuUA7UVWgt7Z7IG6PfNaxWzZtld7qQ_grV469Va_UbGbD342FJnL6iMVrXRv8_ybArS5J65zRR50zclexaKklplRxfebMIo6eovTJrc9kOeUPbfD-vztVB-MEXdW8sH4BTl23p8XVSxRhAegCcd9eA_IDymUpRJ8XPjJ_ZD2KkOXgucBwotl4srE3CweTdbKtCLqZfQcePE0rxxcDN6d4R0vj5TU2ze0iL-jwNUV1Gk4CF6lHqTr-8xqEzNsPHlJSsLFwmrlAqokgVFLA4ktfywBYZmzI6VJRODyqNuA-sf1KKDcWmVy6y1wdbX0pKiE96yHp86IO0Dqb7nZTH035rC7Ml0UTAo0-AmO-1WRS2BCtirBTpP9EGzWtBElwKLpPzokwbiae-GZgykDO9HUbGSm5GV8zvVGRhRNys_kbXjJaL_0DcHevHlcnEQDHqIX0-3fP5uvC5NhFSXBOtzJ796DLYpUXCunAsh5KzDFGdK_VTP6Xhsf2YGofn1okyHgU0J1B-a9F3AZpMYdnv7_FQC_SuZJ8mXFcoFSGI8F6RfjsO9dVY-N6MHsNQ8vWCkf4c4bv0XhBgPz5Uo3kOETpvzQjgmeptC7Wl4G_jG4RoVSjnH-QnLpdrO9zg');
define("CONF_WHATSAPP_AUTH_URL", 'https://api.positus.global/v2/whatsapp/numbers/ea4c9088-e34e-49ce-ba02-8ea55e6fdaa4/');
define("CONF_WHATSAPP_AUTH_URL", 'https://api.positus.global/v2/whatsapp/numbers/251f6a59-b32f-4d28-abad-c7869493d5a9/');
/*
|--------------------------------------------------------------------------
@ -20,7 +20,7 @@ define("CONF_WHATSAPP_AUTH_URL", 'https://api.positus.global/v2/whatsapp/numbers
| -> Facilita da identificacao durante um atendimento.
|
*/
define("CONF_WHATSAPP_CHANNEL", 'WHATSAPP');
define("CONF_WHATSAPP_CHANNEL", 'whatsapp');
/*
|--------------------------------------------------------------------------

530
database/att-v3.sql

@ -0,0 +1,530 @@
ALTER TABLE
pbx_supervisor_agentes
ADD
media int NOT NULL DEFAULT 0;
-- pbx_eventos_agentes
ALTER TABLE
pbx_eventos_agentes
ADD
entrada_indisponivel timestamp(0) NULL;
ALTER TABLE
pbx_eventos_agentes
ADD
saida_indisponivel timestamp(0) NULL;
select
id,
prm_sk_host_chat,
prm_chat_api,
prm_media_simultaneo,
prm_chat_api_supervisor into pbx_parametros_bk
from
pbx_parametros
where
id = 1;
alter table
pbx_parametros drop column prm_chat_api_supervisor;
alter table
pbx_parametros
add
prm_chat_api_supervisor varchar(255);
alter table
pbx_parametros drop column prm_sk_host_chat;
alter table
pbx_parametros
add
prm_sk_host_chat varchar(255);
alter table
pbx_parametros drop column prm_chat_api;
alter table
pbx_parametros
add
prm_chat_api varchar(255);
update
pbx_parametros
set
prm_chat_api_supervisor = (
select
prm_chat_api_supervisor
from
pbx_parametros_bk
where
id = 1
);
update
pbx_parametros
set
prm_chat_api = (
select
prm_chat_api
from
pbx_parametros_bk
where
id = 1
);
update
pbx_parametros
set
prm_sk_host_chat = (
select
prm_sk_host_chat
from
pbx_parametros_bk
where
id = 1
);
drop table pbx_parametros_bk;
select
id,
prm_sk_host_chat,
prm_chat_api,
prm_media_simultaneo,
prm_chat_api_supervisor
from
pbx_parametros
where
id = 1;
ALTER TABLE
pbx_parametros
ADD
prm_media_simultaneo int not NULL DEFAULT 3;
CREATE TABLE md_supervisor (
id SERIAL NOT NULL PRIMARY KEY,
ramal varchar NULL,
matricula varchar NULL,
nome varchar NULL,
tempo_login timestamp NULL,
fila varchar NULL,
status varchar NULL,
duracao timestamp NULL,
uniqueid varchar NULL DEFAULT '',
status_agente int NOT NULL DEFAULT 0,
motivo_pausa varchar NULL,
chamada_classificado int NOT NULL DEFAULT 1,
cliente_id varchar NULL
);
CREATE TABLE md_message (
id SERIAL NOT NULL PRIMARY KEY,
uniqueid varchar NOT NULL,
src varchar NOT NULL,
dst varchar NOT NULL,
type varchar NOT NULL,
content varchar NOT NULL,
profile_name varchar NOT NULL,
msg_date timestamptz NULL DEFAULT now(),
media varchar NULL,
status varchar NULL,
file_name varchar NULL,
id_provedor varchar NULL,
mimetype varchar NULL
);
CREATE TABLE md_evento (
id SERIAL NOT NULL PRIMARY KEY,
uniqueid varchar NOT NULL,
evento varchar NOT NULL,
data_evento timestamp NULL,
data_reg timestamp NULL DEFAULT now(),
fila varchar NOT NULL,
matricula varchar NULL
);
CREATE TABLE md_atendimento (
id SERIAL NOT NULL PRIMARY KEY,
matricula varchar NULL,
cliente_id varchar NOT NULL,
direcao varchar(1) NOT NULL,
uniqueid varchar NULL,
context varchar NULL,
data_reg timestamp NULL DEFAULT now(),
nome varchar NULL
);
CREATE TABLE md_system_message (
id SERIAL NOT NULL PRIMARY KEY,
data_reg timestamp NULL DEFAULT now(),
texto varchar NOT NULL,
ordem int NOT NULL,
momento varchar NULL
);
INSERT INTO
md_system_message (data_reg, texto, ordem, momento)
VALUES
(
now(),
'Olá @cliente_name tudo certo?',
0,
'SAUDACAO'
),
(
now(),
'@cliente_name escolha uma das opções abaixo para iniciar o atendimento',
0,
'SAUDACAO'
),
(
now(),
'Cancelado o atendimento!',
0,
'CANCELAR_FILA'
),
(
now(),
'Atendimento iniciado com @agente_name!',
0,
'INICIAR_ATENDIMENTO'
),
(
now(),
'Atendimento finalizado!',
0,
'FINALIZAR_ATENDIMENTO'
),
(
now(),
'Não temos nenhum atendente disponível no momento, iremos lhe atender assim que um atendente estiver disponível!',
0,
'ENTRAR_FILA_SEM'
),
(
now(),
'Nossos atendentes estão ocupados, por favor aguarde que iremos lhe atender!',
0,
'ENTRAR_FILA_COM'
),
(
now(),
'Para finalar o atendimento digite \n*"/finalizar"*.',
0,
'INICIAR_ATENDIMENTO'
),
(
now(),
'Para sair da fila digite \n*"/cancelar"*.',
0,
'ENTRAR_FILA_SEM'
),
(
now(),
'Para sair da fila digite \n*"/cancelar"*.',
0,
'ENTRAR_FILA_COM'
);
CREATE TABLE md_supervisor (
id SERIAL NOT NULL PRIMARY KEY,
ramal varchar NULL,
matricula varchar NULL,
nome varchar NULL,
tempo_login timestamp NULL,
fila varchar NULL,
status varchar NULL,
duracao timestamp NULL,
uniqueid varchar varying NULL DEFAULT '',
status_agente int NOT NULL DEFAULT 0,
motivo_pausa varchar NULL,
chamada_classificado int NOT NULL DEFAULT 1,
cliente_id varchar NULL
);
CREATE TABLE pbx_notifica_media (
id SERIAL NOT NULL PRIMARY KEY,
uniqueid character varying NOT NULL,
src character varying NOT NULL,
msg character varying NOT NULL,
notif_date timestamp WITHOUT TIME ZONW DEFAULT(NOW())
);
CREATE TABLE pbx_lista_negra_palavras (
id SERIAL NOT NULL PRIMARY KEY,
palavra character varying NOT NULL,
date_create timestamp WITHOUT TIME ZONW DEFAULT(NOW())
);
INSERT INTO
pbx_lista_negra_palavras (palavra)
VALUES
('Anus'),
('Baba-ovo'),
('Babaovo'),
('Babaca'),
('Bacura'),
('Bagos'),
('Baitola'),
('Bebum'),
('Besta'),
('Bicha'),
('Bisca'),
('Bixa'),
('Boazuda'),
('Boceta'),
('Boco'),
('Boiola'),
('Bolagato'),
('Boquete'),
('Bolcat'),
('Bosseta'),
('Bosta'),
('Bostana'),
('Brecha'),
('Brexa'),
('Brioco'),
('Bronha'),
('Buca'),
('Buceta'),
('Bunda'),
('Bunduda'),
('Burra'),
('Burro'),
('Busseta'),
('Cachorra'),
('Cachorro'),
('Cadela'),
('Caga'),
('Cagado'),
('Cagao'),
('Cagona'),
('Canalha'),
('Caralho'),
('Casseta'),
('Cassete'),
('Checheca'),
('Chereca'),
('Chibumba'),
('Chibumbo'),
('Chifruda'),
('Chifrudo'),
('Chota'),
('Chochota'),
('Chupada'),
('Chupado'),
('Clitoris'),
('Cocaina'),
('Coco'),
('Corna'),
('Corno'),
('Cornuda'),
('Cornudo'),
('Corrupta'),
('Corrupto'),
('Cretina'),
('Cretino'),
('Cruz-credo'),
('Cu'),
('Culhao'),
('Curalho'),
('Cuzao'),
('Cuzuda'),
('Cuzudo'),
('Debil'),
('Debiloide'),
('Defunto'),
('Demonio'),
('Difunto'),
('Doida'),
('Doido'),
('Egua'),
('Escrota'),
('Escroto'),
('Esporrada'),
('Esporrado'),
('Esporro'),
('Estupida'),
('Estupidez'),
('Estupido'),
('Fedida'),
('Fedido'),
('Fedor'),
('Fedorenta'),
('Feia'),
('Feio'),
('Feiosa'),
('Feioso'),
('Feioza'),
('Feiozo'),
('Felacao'),
('Fenda'),
('Foda'),
('Fodao'),
('Fode'),
('FodidaFodido'),
('Fornica'),
('Fudendo'),
('Fudecao'),
('Fudida'),
('Fudido'),
('Furada'),
('Furado'),
('Furão'),
('Furnica'),
('Furnicar'),
('Furo'),
('Furona'),
('Gaiata'),
('Gaiato'),
('Gay'),
('Gonorrea'),
('Gonorreia'),
('Gosma'),
('Gosmenta'),
('Gosmento'),
('Grelinho'),
('Grelo'),
('Homo-sexual'),
('Homossexual'),
('Homossexual'),
('Idiota'),
('Idiotice'),
('Imbecil'),
('Iscrota'),
('Iscroto'),
('Japa'),
('Ladra'),
('Ladrao'),
('Ladroeira'),
('Ladrona'),
('Lalau'),
('Leprosa'),
('Leproso'),
('Lésbica'),
('Macaca'),
('Macaco'),
('Machona'),
('Machorra'),
('Manguaca'),
('Mangua¦a'),
('Masturba'),
('Meleca'),
('Merda'),
('Mija'),
('Mijada'),
('Mijado'),
('Mijo'),
('Mocrea'),
('Mocreia'),
('Moleca'),
('Moleque'),
('Mondronga'),
('Mondrongo'),
('Naba'),
('Nadega'),
('Nojeira'),
('Nojenta'),
('Nojento'),
('Nojo'),
('Olhota'),
('Otaria'),
('Ot-ria'),
('Otario'),
('Ot-rio'),
('Paca'),
('Paspalha'),
('Paspalhao'),
('Paspalho'),
('Pau'),
('Peia'),
('Peido'),
('Pemba'),
('Pênis'),
('Pentelha'),
('Pentelho'),
('Perereca'),
('Peru'),
('Pica'),
('Picao'),
('Pilantra'),
('Piranha'),
('Piroca'),
('Piroco'),
('Piru'),
('Porra'),
('Prega'),
('Prostibulo'),
('Prost-bulo'),
('Prostituta'),
('Prostituto'),
('Punheta'),
('Punhetao'),
('Pus'),
('Pustula'),
('Puta'),
('Puto'),
('Puxa-saco'),
('Puxasaco'),
('Rabao'),
('Rabo'),
('Rabuda'),
('Rabudao'),
('Rabudo'),
('Rabudona'),
('Racha'),
('Rachada'),
('Rachadao'),
('Rachadinha'),
('Rachadinho'),
('Rachado'),
('Ramela'),
('Remela'),
('Retardada'),
('Retardado'),
('Ridícula'),
('Rola'),
('Rolinha'),
('Rosca'),
('Sacana'),
('Safada'),
('Safado'),
('Sapatao'),
('Sifilis'),
('Siririca'),
('Tarada'),
('Tarado'),
('Testuda'),
('Tezao'),
('Tezuda'),
('Tezudo'),
('Trocha'),
('Trolha'),
('Troucha'),
('Trouxa'),
('Troxa'),
('Vaca'),
('Vagabunda'),
('Vagabundo'),
('Vagina'),
('Veada'),
('Veadao'),
('Veado'),
('Viada'),
('Víado'),
('Viado'),
('Viadao'),
('Xavasca'),
('Xerereca'),
('Xexeca'),
('Xibiu'),
('Xibumba'),
('Xota'),
('Xochota'),
('Xoxota'),
('Xana'),
('ladrão'),
('viado'),
('Xaninha');

27
database/database.sql

@ -17,4 +17,29 @@ ADD
ALTER TABLE
pbx_sip_ramais
ADD
COLUMN midiaramal VARCHAR(1);
COLUMN midiaramal VARCHAR(1);
CREATE TABLE md_supervisor (
id SERIAL NOT NULL PRIMARY KEY,
ramal character varying NOT NULL,
matricula character varying NULL,
nome character varying NULL,
tempo_login timestamp NULL,
cliente_id character varying NULL,
fila character varying NULL,
status character varying NULL,
duracao timestamp NULL,
uniqueid character varying NULL DEFAULT '',
status_agente int NOT NULL DEFAULT 0,
motivo_pausa character varying NULL,
chamada_classificado int NOT NULL DEFAULT 1
);
ALTER TABLE
md_message
add
COLUMN mimetype character varying NOT null,
add
COLUMN id_provedor character varying NOT null,
add
COLUMN file_name character varying NOT NULL;

21
database/modelo_mensagem.json

@ -0,0 +1,21 @@
{
"event": {
"type": "mensagem",
"contact": {
"name": "Lucas",
"number": "556596107663",
"matricula": ""
},
"mensagem": {
"type": "text",
"content": "ola",
"id_provedor": null,
"dst": "1040",
"uniqueid": 1638191429,
"media": "whatsapp",
"datetime": 1638191429,
"status": "sent",
"mimetype": false
}
}
}

17
database/msg-text-positus.json

@ -0,0 +1,17 @@
{
"contacts": [{
"profile": {
"name": "Lucas"
},
"wa_id": "556596107663"
}],
"messages": [{
"from": "556596107663",
"id": "ABEGVWWWEHZjAhBQHfcZqjRX4rWUHoW5CZul",
"text": {
"body": "Test"
},
"timestamp": "1637696166",
"type": "text"
}]
}

10
docker-compose.yml

@ -0,0 +1,10 @@
version: '3'
services:
mid:
container_name: mid
build: .
volumes:
- $PWD/data:/var/www/html/aplicativo/integracao/media/storage/files
ports:
- 8090:8090
- 8081:8081

11
includes/config.php

@ -114,4 +114,15 @@ if (file_exists(__DIR__ . '/../config/display_errors.php')) {
*/
if (file_exists(__DIR__ . '/../config/telegram.php')) {
require __DIR__ . '/../config/telegram.php';
}
/*
|--------------------------------------------------------------------------
| Configuracao de momentis
|--------------------------------------------------------------------------
|
| Variaveis do moments das mensagens padrão.
|
*/
if (file_exists(__DIR__ . '/../config/moments.php')) {
require __DIR__ . '/../config/moments.php';
}

3
index.php

@ -7,5 +7,4 @@ include __DIR__ . '/includes/config.php';
use app\Middleware\Middleware;
$middle = new Middleware($config);
$middle->api();
$middle = new Middleware($config);

15
ports.conf

@ -0,0 +1,15 @@
# If you just change the port or add more ports here, you will likely also
# have to change the VirtualHost statement in
# /etc/apache2/sites-enabled/000-default.conf
Listen 8081
<IfModule ssl_module>
Listen 443
</IfModule>
<IfModule mod_gnutls.c>
Listen 443
</IfModule>
# vim: syntax=apache ts=4 sw=4 sts=4 sr noet

1115
public/css/styles.css

File diff suppressed because it is too large Load Diff

BIN
public/images/alerta.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

1
public/images/audio-icon.svg

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="#919191" d="M11.999 14.942c2.001 0 3.531-1.53 3.531-3.531V4.35c0-2.001-1.53-3.531-3.531-3.531S8.469 2.35 8.469 4.35v7.061c0 2.001 1.53 3.531 3.53 3.531zm6.238-3.53c0 3.531-2.942 6.002-6.237 6.002s-6.237-2.471-6.237-6.002H3.761c0 4.001 3.178 7.297 7.061 7.885v3.884h2.354v-3.884c3.884-.588 7.061-3.884 7.061-7.885h-2z"></path></svg>

After

Width:  |  Height:  |  Size: 426 B

1
public/images/camera-icon.svg

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 20" width="16" height="20"><path fill="#919191" d="M13.822 4.668H7.14l-1.068-1.09a1.068 1.068 0 0 0-.663-.278H3.531c-.214 0-.51.128-.656.285L1.276 5.296c-.146.157-.266.46-.266.675v1.06l-.001.003v6.983c0 .646.524 1.17 1.17 1.17h11.643a1.17 1.17 0 0 0 1.17-1.17v-8.18a1.17 1.17 0 0 0-1.17-1.169zm-5.982 8.63a3.395 3.395 0 1 1 0-6.79 3.395 3.395 0 0 1 0 6.79zm0-5.787a2.392 2.392 0 1 0 0 4.784 2.392 2.392 0 0 0 0-4.784z"></path></svg>

After

Width:  |  Height:  |  Size: 488 B

1
public/images/clip.svg

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" id="Outline" viewBox="0 0 24 24" width="24" height="24"><path fill="#919191" d="M22.95,9.6a1,1,0,0,0-1.414,0L10.644,20.539a5,5,0,1,1-7.072-7.071L14.121,2.876a3,3,0,0,1,4.243,4.242L7.815,17.71a1.022,1.022,0,0,1-1.414,0,1,1,0,0,1,0-1.414l9.392-9.435a1,1,0,0,0-1.414-1.414L4.987,14.882a3,3,0,0,0,0,4.243,3.073,3.073,0,0,0,4.243,0L19.778,8.532a5,5,0,0,0-7.071-7.07L2.158,12.054a7,7,0,0,0,9.9,9.9L22.95,11.018A1,1,0,0,0,22.95,9.6Z"/></svg>

After

Width:  |  Height:  |  Size: 474 B

363
public/images/community_message.svg

@ -0,0 +1,363 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
data-name="Layer 1"
width="809.34183"
height="629.87561"
viewBox="0 0 809.34183 629.87561"
version="1.1"
id="svg1106"
sodipodi:docname="community_message.svg"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)">
<metadata
id="metadata1112">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs1110" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1017"
id="namedview1108"
showgrid="false"
inkscape:zoom="1.3462976"
inkscape:cx="404.67093"
inkscape:cy="344.64892"
inkscape:window-x="1912"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="svg1106" />
<path
d="M363.34812,549.37208C326.75641,513.35019,311.30181,475.77031,319.83,443.556c10.94436-41.34026,59.56474-69.92355,133.39432-78.42081l.27268,2.37135c-72.8018,8.37841-120.68047,36.32021-131.35973,76.66007-8.29975,31.35084,6.93053,68.10978,42.88542,103.50533Z"
transform="translate(-195.32909 -135.0622)"
fill="#f0f0f0"
id="path970" />
<path
d="M716.39646,677.23887c-45.66871,0-95.2959-6.78662-144.58981-19.83666-60.95842-16.138-116.85076-40.49659-161.63444-70.44326l1.3261-1.98332c44.56809,29.8016,100.21281,54.04886,160.919,70.11932,54.85932,14.52412,110.13,21.25364,159.81659,19.47775l.08623,2.38417Q724.44916,677.24,716.39646,677.23887Z"
transform="translate(-195.32909 -135.0622)"
fill="#f0f0f0"
id="path972" />
<path
d="M821.16612,662.23116l-.76443-2.26066c40.84443-13.8098,66.391-36.05568,73.87795-64.33483,9.51688-35.94965-11.15878-77.66687-58.21884-117.4672C788.76069,438.166,720.553,404.974,643.99959,384.7079l.61061-2.30727c76.88788,20.35521,145.42765,53.71909,192.99111,93.94591,47.80351,40.42842,68.75185,83.00971,58.98559,119.89973C888.88553,625.33647,862.80527,648.15334,821.16612,662.23116Z"
transform="translate(-195.32909 -135.0622)"
fill="#f0f0f0"
id="path974" />
<path
d="M446.11894,678.23635C356.38263,638.43719,287.234,586.8028,251.412,532.84367l1.988-1.32027C288.97435,585.10906,357.76,636.43756,447.08613,676.05494Z"
transform="translate(-195.32909 -135.0622)"
fill="#f0f0f0"
id="path976" />
<path
d="M761.62469,747.18514q-13.8838,0-28.24187-.61993l.10255-2.38418c71.43317,3.09267,134.46-5.45587,182.27864-24.72619l.8926,2.214C874.94289,738.477,821.77556,747.18514,761.62469,747.18514Z"
transform="translate(-195.32909 -135.0622)"
fill="#f0f0f0"
id="path978" />
<path
d="M709.55856,367.27523c-14.83176-4.829-30.08768-9.3252-45.34359-13.36408-87.792-23.24162-174.09251-31.49359-249.575-23.85923l-.24-2.37485c75.764-7.65942,162.35983.61236,250.42571,23.92681,15.299,4.05053,30.59806,8.55961,45.47177,13.40253Z"
transform="translate(-195.32909 -135.0622)"
fill="#f0f0f0"
id="path980" />
<path
d="M307.26526,552.95184c-36.59172-36.0219-52.04632-73.60177-43.51818-105.8161,10.94437-41.34025,59.56474-69.92354,133.39433-78.4208l.27267,2.37135c-72.8018,8.37841-120.68047,36.32021-131.35973,76.66006-8.29975,31.35085,6.93054,68.10978,42.88542,103.50533Z"
transform="translate(-195.32909 -135.0622)"
fill="#f0f0f0"
id="path982" />
<path
d="M660.31359,680.81863c-45.66871,0-95.29589-6.78663-144.58981-19.83666-60.95842-16.138-116.85076-40.49659-161.63443-70.44326l1.32609-1.98332c44.5681,29.8016,100.21282,54.04886,160.919,70.11931,54.85932,14.52412,110.13,21.25365,159.8166,19.47775l.08623,2.38418Q668.3663,680.81979,660.31359,680.81863Z"
transform="translate(-195.32909 -135.0622)"
fill="#f0f0f0"
id="path984" />
<path
d="M765.08325,665.81091l-.76442-2.26065c40.84442-13.8098,66.391-36.05569,73.87794-64.33484,9.51689-35.94964-11.15878-77.66687-58.21884-117.4672-47.3001-40.0025-115.50777-73.1945-192.06121-93.46056l.61061-2.30727C665.41521,406.3356,733.955,439.69947,781.51844,479.9263,829.322,520.35472,850.27029,562.936,840.504,599.826,832.80266,628.91622,806.7224,651.7331,765.08325,665.81091Z"
transform="translate(-195.32909 -135.0622)"
fill="#f0f0f0"
id="path986" />
<path
d="M390.03608,681.81611c-89.73632-39.79916-158.885-91.43355-194.707-145.39269l1.988-1.32026c35.57443,53.58566,104.36007,104.91416,193.68621,144.53154Z"
transform="translate(-195.32909 -135.0622)"
fill="#f0f0f0"
id="path988" />
<path
d="M705.54182,750.7649q-13.88379,0-28.24186-.61994l.10254-2.38417c71.43318,3.09267,134.46-5.45587,182.27864-24.7262l.89261,2.21405C818.86,742.05672,765.6927,750.7649,705.54182,750.7649Z"
transform="translate(-195.32909 -135.0622)"
fill="#f0f0f0"
id="path990" />
<path
d="M653.47569,370.855c-14.83175-4.82894-30.08767-9.32519-45.34359-13.36407-87.792-23.24163-174.09251-31.4936-249.57505-23.85923l-.24-2.37485c75.764-7.65942,162.35983.61236,250.42571,23.92681,15.299,4.05053,30.59807,8.55961,45.47177,13.40253Z"
transform="translate(-195.32909 -135.0622)"
fill="#f0f0f0"
id="path992" />
<path
d="M236.00582,489.03092c17.14976-19.22826,44.50649-30.9473,69.29785-23.93208a118.70968,118.70968,0,0,0-46.32362,67.05768c-2.67852,10.46848-4.56766,22.39086-13.2738,28.79149-5.41709,3.98274-12.59491,4.93415-19.25684,4.02714-6.66224-.90723-12.96156-3.498-19.17587-6.06531l-1.6722.14488C210.40907,533.74217,218.85606,508.25918,236.00582,489.03092Z"
transform="translate(-195.32909 -135.0622)"
fill="#f2f2f2"
id="path994" />
<path
d="M305.22794,465.63414a101.46482,101.46482,0,0,0-53.2125,32.64337,43.694,43.694,0,0,0-7.36793,11.53847,25.06052,25.06052,0,0,0-1.47925,13.10607c.55678,4.13632,1.60531,8.30284,1.14692,12.50247a15.34469,15.34469,0,0,1-6.50386,10.73231c-4.35348,3.179-9.58959,4.60415-14.7585,5.81987-5.7391,1.34983-11.7216,2.66095-16.39988,6.475-.56684.46212-1.25076-.44444-.68477-.90587,8.1394-6.63578,19.45136-5.7838,28.45684-10.60368,4.20212-2.24905,7.75192-5.77808,8.615-10.6206.75471-4.23457-.32287-8.53205-.92282-12.71529a26.74161,26.74161,0,0,1,.96892-12.86881,40.3945,40.3945,0,0,1,6.89713-11.77917,98.43912,98.43912,0,0,1,22.91947-20.52511,103.26408,103.26408,0,0,1,32.19117-13.92683c.70851-.17132.838.95761.13408,1.12781Z"
transform="translate(-195.32909 -135.0622)"
fill="#fff"
id="path996" />
<path
d="M256.1732,492.79743a15.223,15.223,0,0,1-.469-19.70657c.46161-.56633,1.36335.12438.90112.69147a14.09612,14.09612,0,0,0,.47371,18.33032c.48879.54315-.41987,1.22481-.90586.68478Z"
transform="translate(-195.32909 -135.0622)"
fill="#fff"
id="path998" />
<path
d="M242.60972,520.862a29.341,29.341,0,0,0,20.38536-6.45483c.56846-.4601,1.25255.44632.68478.90586a30.51908,30.51908,0,0,1-21.22647,6.67372c-.73038-.05063-.57008-1.17511.15633-1.12475Z"
transform="translate(-195.32909 -135.0622)"
fill="#fff"
id="path1000" />
<path
d="M283.01569,473.47307a8.61692,8.61692,0,0,0,6.954,4.30074c.73066.04134.56957,1.16582-.15633,1.12475a9.656,9.656,0,0,1-7.70353-4.74072.58687.58687,0,0,1,.11055-.79532.57066.57066,0,0,1,.79532.11055Z"
transform="translate(-195.32909 -135.0622)"
fill="#fff"
id="path1002" />
<path
d="M340.49545,544.9208c-.457-.01284-.91394-.02568-1.37675-.03089a113.48,113.48,0,0,0-18.37688,1.07221c-.47163.05859-.9487.12507-1.41766.19708a119.63375,119.63375,0,0,0-41.593,14.49229,116.1798,116.1798,0,0,0-14.59508,9.929c-6.361,5.07568-12.8542,11.08514-20.30131,13.68643a19.92976,19.92976,0,0,1-2.367.69439l-34.56-23.87781c-.05131-.09368-.10842-.17978-.16012-.27373l-1.42346-.89027c.22432-.20613.46141-.4151.68573-.62122.12963-.12012.267-.23491.39658-.355.08886-.07839.17814-.15656.25384-.23238.02948-.0262.05934-.05214.0834-.07049.07571-.07582.15407-.13821.22432-.20613q1.98306-1.7576,3.99773-3.49341c.00545-.00789.00545-.00789.01861-.01047,10.25634-8.7913,21.33673-16.80359,33.281-22.87937.35942-.18268.72391-.37352,1.09912-.54529a110.86072,110.86072,0,0,1,16.77837-6.7664,98.008,98.008,0,0,1,9.61356-2.4324,81.49862,81.49862,0,0,1,25.46291-.97585c16.91705,2.01955,32.99991,9.427,43.48488,22.57717C339.97222,544.24483,340.23292,544.57632,340.49545,544.9208Z"
transform="translate(-195.32909 -135.0622)"
fill="#f2f2f2"
id="path1004" />
<path
d="M340.1147,545.30576a101.4649,101.4649,0,0,0-62.14077-5.9737,43.69454,43.69454,0,0,0-12.82983,4.77682,25.06061,25.06061,0,0,0-9.07186,9.57386c-2.04579,3.63784-3.71713,7.59586-6.6116,10.673a15.34465,15.34465,0,0,1-11.65456,4.65337c-5.39-.08284-10.42876-2.09744-15.2878-4.2388-5.395-2.37757-10.96111-4.93259-16.99277-4.70394-.73082.02771-.73108-1.1079-.00136-1.13556,10.49405-.39782,19.01307,7.093,29.10534,8.66653,4.70924.73423,9.66828.05372,13.27292-3.29314,3.15209-2.92668,4.87909-7.00675,6.91866-10.708a26.74157,26.74157,0,0,1,8.52154-9.69167,40.39458,40.39458,0,0,1,12.59885-5.25246,98.439,98.439,0,0,1,30.65744-2.589,103.264,103.264,0,0,1,34.08777,8.26152c.66884.28978.09251,1.2691-.572.98121Z"
transform="translate(-195.32909 -135.0622)"
fill="#fff"
id="path1006" />
<path
d="M284.59306,537.45978a15.223,15.223,0,0,1,11.49027-16.01694c.70954-.17426,1.01367.92015.30319,1.09464a14.09613,14.09613,0,0,0-10.6579,14.92094c.06327.728-1.07266.72516-1.13556.00136Z"
transform="translate(-195.32909 -135.0622)"
fill="#fff"
id="path1008" />
<path
d="M256.8666,551.70159a29.341,29.341,0,0,0,20.16282,7.11958c.73089-.02512.73137,1.11048.00136,1.13556a30.519,30.519,0,0,1-20.96618-7.45121c-.55269-.48016.25232-1.28148.802-.80393Z"
transform="translate(-195.32909 -135.0622)"
fill="#fff"
id="path1010" />
<path
d="M317.65988,538.1914a8.61693,8.61693,0,0,0,2.963,7.62068c.5585.47292-.24714,1.27377-.802.80393a9.656,9.656,0,0,1-3.2966-8.42325.58686.58686,0,0,1,.5671-.56846.57065.57065,0,0,1,.56846.5671Z"
transform="translate(-195.32909 -135.0622)"
fill="#fff"
id="path1012" />
<path
d="M907.994,646.65488c-9.68947-23.87372-31.516-44.10561-57.223-45.83088a118.70969,118.70969,0,0,1,21.09071,78.72606c-.99576,10.75975-3.22363,22.62349,2.82479,31.57791,3.76336,5.57173,10.20385,8.88026,16.78308,10.26509,6.57959,1.38473,13.38321,1.06186,20.09888.73254l1.52623.69848C917.07422,697.36818,917.68346,670.52859,907.994,646.65488Z"
transform="translate(-195.32909 -135.0622)"
fill="#f2f2f2"
id="path1014" />
<path
d="M850.66244,601.35361a101.46487,101.46487,0,0,1,39.14549,48.62911,43.6942,43.6942,0,0,1,3.06122,13.3436,25.06057,25.06057,0,0,1-3.01175,12.84082c-1.91461,3.70856-4.30251,7.28029-5.28229,11.38968a15.34469,15.34469,0,0,0,2.51838,12.29392c3.03176,4.45727,7.48427,7.55936,11.94389,10.44163,4.95155,3.20022,10.14536,6.44579,13.26959,11.61032.37854.62576,1.32737.0018.9494-.623-5.43562-8.98541-16.37587-11.985-23.2375-19.55118-3.20176-3.53055-5.35895-8.04737-4.54424-12.89826.71244-4.24188,3.17171-7.92719,5.14275-11.66543A26.74158,26.74158,0,0,0,894.03,664.719a40.39429,40.39429,0,0,0-2.53691-13.41205,98.439,98.439,0,0,0-14.68766-27.03433,103.2639,103.2639,0,0,0-25.63769-23.93611c-.60971-.39948-1.11106.62026-.50533,1.01713Z"
transform="translate(-195.32909 -135.0622)"
fill="#fff"
id="path1016" />
<path
d="M887.7339,643.424a15.223,15.223,0,0,0,7.06506-18.40254c-.24441-.68854-1.32584-.34108-1.0811.34837a14.09613,14.09613,0,0,1-6.607,17.10477c-.64291.34726-.01621,1.29467.623.9494Z"
transform="translate(-195.32909 -135.0622)"
fill="#fff"
id="path1018" />
<path
d="M891.07584,674.41461a29.341,29.341,0,0,1-17.03-12.93084c-.38075-.6244-1.32969-.00063-.9494.623a30.51906,30.51906,0,0,0,17.74861,13.4197c.70491.1978.93187-.91514.23079-1.11187Z"
transform="translate(-195.32909 -135.0622)"
fill="#fff"
id="path1020" />
<path
d="M868.94785,616.20207a8.61693,8.61693,0,0,1-7.99493,1.7133c-.702-.20663-.92827.90658-.23079,1.11187a9.656,9.656,0,0,0,8.84874-1.87578.58685.58685,0,0,0,.16319-.7862.57065.57065,0,0,0-.78621-.16319Z"
transform="translate(-195.32909 -135.0622)"
fill="#fff"
id="path1022" />
<path
d="M790.7983,664.17446c.43471.14149.86941.283,1.30705.43363a113.47979,113.47979,0,0,1,16.94746,7.18631c.4245.21369.85148.43665,1.269.66209a119.63349,119.63349,0,0,1,34.30249,27.62862,116.1803,116.1803,0,0,1,10.40889,14.25679c4.28507,6.91836,8.38071,14.76057,14.5203,19.71352a19.933,19.933,0,0,0,1.99594,1.44955l40.57483-10.87314c.07981-.071.16254-.13288.2428-.204l1.63988-.36005c-.142-.26954-.29506-.546-.437-.81556-.08172-.15669-.17247-.311-.25419-.46766-.05734-.10369-.11515-.20732-.161-.30417-.019-.03459-.03836-.06905-.05485-.09443-.04582-.09685-.09866-.182-.142-.26953q-1.277-2.32185-2.591-4.63382c-.00249-.00927-.00249-.00927-.014-.01611-6.70494-11.727-14.44781-22.99734-23.65514-32.73414-.27711-.29285-.55625-.59509-.8519-.883a110.86056,110.86056,0,0,0-13.52812-12.012,98.00839,98.00839,0,0,0-8.23678-5.522,81.499,81.499,0,0,0-23.65365-9.47717c-16.6117-3.78376-34.24859-2.21269-48.54339,6.6485C791.5183,663.71366,791.16135,663.93825,790.7983,664.17446Z"
transform="translate(-195.32909 -135.0622)"
fill="#f2f2f2"
id="path1024" />
<path
d="M791.02753,664.665a101.46484,101.46484,0,0,1,60.53356,15.25934,43.69414,43.69414,0,0,1,10.478,8.811,25.06059,25.06059,0,0,1,5.32635,12.066c.7041,4.1138.94792,8.4033,2.63976,12.27431a15.34468,15.34468,0,0,0,9.41257,8.29977c5.10427,1.73356,10.527,1.52968,15.82311,1.146,5.88029-.426,11.98132-.96161,17.58524,1.281.679.27172,1.06091-.79774.38294-1.069-9.74986-3.90172-20.291.2901-30.325-1.61994-4.68206-.89126-9.12389-3.19892-11.394-7.56259-1.98507-3.81585-2.24029-8.239-2.9172-12.41048a26.74153,26.74153,0,0,0-4.76845-11.992,40.394,40.394,0,0,0-10.10057-9.18138,98.43872,98.43872,0,0,0-28.0038-12.74241,103.264,103.264,0,0,0-34.88145-3.676c-.72734.04813-.51368,1.16418.20891,1.11637Z"
transform="translate(-195.32909 -135.0622)"
fill="#fff"
id="path1026" />
<path
d="M845.9563,675.93628a15.223,15.223,0,0,0-5.43854-18.94706c-.6097-.4026-1.264.52592-.65346.92906a14.09612,14.09612,0,0,1,5.023,17.635c-.30425.66435.76654,1.04349,1.06905.383Z"
transform="translate(-195.32909 -135.0622)"
fill="#fff"
id="path1028" />
<path
d="M867.28313,698.66845a29.341,29.341,0,0,1-21.38276-.07132c-.67993-.26931-1.06206.80006-.38295,1.06905a30.51914,30.51914,0,0,0,22.25086.029c.68192-.26648.19306-1.29174-.48515-1.02672Z"
transform="translate(-195.32909 -135.0622)"
fill="#fff"
id="path1030" />
<path
d="M814.5672,665.51157a8.617,8.617,0,0,1-5.352,6.18148c-.685.2577-.19535,1.28273.48515,1.02671a9.656,9.656,0,0,0,5.93587-6.82525.58684.58684,0,0,0-.34305-.726.57064.57064,0,0,0-.726.34305Z"
transform="translate(-195.32909 -135.0622)"
fill="#fff"
id="path1032" />
<circle
cx="524.6825"
cy="583.33993"
r="9.54602"
fill="#fd6584"
id="circle1034" />
<circle
cx="558.49624"
cy="420.74992"
r="9.54602"
fill="#e6e6e6"
id="circle1036" />
<circle
cx="380.86745"
cy="155.96167"
r="9.54602"
fill="#e6e6e6"
id="circle1038" />
<path
d="M376.15456,657.20782c0,2.43.08,4.86.25,7.27.18,2.88.49,5.74.91,8.58a107.2491,107.2491,0,0,0,8.02,27.73q.29993.66.6,1.32l.3.66c.1.22.21.44.31.65.14.29.27.57.41.85.17.35.34.7.52,1.05.02.04.04.09.07.14.15.3.29.59.45.89a1.7462,1.7462,0,0,0,.1.19c.17.34.35.69.53,1.03.2.36005.39.73.59,1.09.48.89.98,1.77,1.49,2.65.15.26.3.52.46.78.14.24.28.48.43.72.1.16.2.33.3.49.16.27.32.53.49.8.37.59.75,1.18,1.12,1.77h.01c.17.27.35.54.53.8v.01c.53.8,1.08,1.6,1.63,2.38995a.0098.0098,0,0,1,.01.01c.44.64.9,1.26,1.35,1.88v.01c.3.39.59.79.89,1.18.24.32.49.65.74.97.05.06994.11.12994.16.2v.01c.07.09.14.17.21.26q1.59,2.04,3.28,3.99l.18.21c.06.07.13.14.19.22.1.11005.19.22.29.33.09.11.19.22.29.33a108.617,108.617,0,0,0,19.83,17.49c.97.67,1.96,1.32,2.95,1.95.25.16.5.32.75.47.07.05.15.1.22.15.14.08.27.16.41.25.13.08.26.16.4.24.53.33,1.07.65,1.61.97.13.08.27.16.41.24006.13.06994.27.15.41.23,0,.01,0,.01.01.01h.01v.01h.01q1.00506.58492,2.04,1.13995c.37.2.75.41,1.13.6.86.47,1.73.92,2.6,1.35.31.15.63.3.94.46.26.12.52.25.78.37.08.04.15.07.22.11.22.1.43.2.65.3.24.11005.48.22.71.33.27.12.53.24.8.35,1,.45,2.01.88,3.02,1.29a105.87419,105.87419,0,0,0,11.08,3.84c.17.05.35.1.53.15q2.41508.675,4.86005,1.23,4.275.99006,8.63,1.62h.02c.35.05.69.1,1.04.15.03,0,.06.01.1.01,0,.01,0,.01.01,0,.38.05.77.1,1.15.15a.24843.24843,0,0,0,.08.01c.36.05.73.09,1.09.13q.58494.06,1.17.12.45.045.9.09a.19454.19454,0,0,0,.08.01c.27.02.54.05.81.07h.04c.23.02.47.04.7.06006,2.79.22,5.59.31994,8.43.31994,3.07,0,6.12-.12,9.13-.38h.03c.65-.06,1.29-.12,1.93-.18.17-.02.33-.03.5-.05.03,0,.07-.01.1-.01.05-.01.1-.01.16-.02a.24843.24843,0,0,0,.08-.01c.22-.02.44-.05.67-.07l.75-.09c.25-.03.5-.07.75-.1.06-.01.11-.01.17-.02.18-.02.36-.05.54-.07995.09-.01.18-.02.26-.03.18-.03.35-.05.53-.08h.01a105.706,105.706,0,0,0,16.2-3.65c.28-.09.56-.17005.84-.26.01,0,.02-.01.03-.01.08-.03.17-.05.25-.08.75-.24,1.49-.49,2.23-.75,1.02-.34,2.03-.71,3.04-1.09.27-.11.54-.21.81-.32.55-.21,1.09-.43,1.63-.65.92-.37,1.84-.76,2.75-1.16.85-.38,1.69-.76,2.53-1.16.02-.00994.05-.00994.07-.03.19-.09.38-.18.56-.27h.01a107.18912,107.18912,0,0,0,13.29-7.59q2.04-1.365,4.02-2.82c1.06-.79,2.1-1.59,3.14-2.41a108.28427,108.28427,0,0,0,14.19-13.56v-.01c.31-.34.6-.69.9-1.04.05-.05.09-.1.13-.14.19-.23.37-.45.56-.67v-.01c.25-.29.5-.59.74-.89.06-.06994.11-.12994.16-.19v-.01c.2-.24.38995-.48.59-.73.1-.12.19-.24.29-.36v-.01c.59-.74,1.16-1.48,1.73-2.24.15-.2.3-.41.46-.62.72-.97,1.42-1.96,2.1-2.95v-.01c.26-.37.51-.75.77-1.12q.375-.57.75-1.13995c.25-.4.51-.79.76-1.19l.03-.06c.25-.38.5-.78.74-1.17.19-.32.38-.63.57-.94.12-.21.25-.42.37-.63995a2.3919,2.3919,0,0,0,.13-.21q.42-.735.84-1.47c.33-.56994.65-1.15.97-1.73.12-.24.25-.48.38-.72.05-.08.09-.16.13-.24.15-.28.3-.56994.44-.86.15-.28.3-.57.45-.86.13995-.28.29-.57.43-.86.11-.22.21-.44.32-.66a.2175.2175,0,0,1,.03-.05c.05-.11.1-.21.14-.31a.21619.21619,0,0,0,.03-.05c.11-.22.21-.45.32-.67.14-.29.27-.59.41-.88.09-.2.19-.41.28-.62.13-.29.27-.58.39-.88.14-.30005.27-.6.4-.91a2.29962,2.29962,0,0,0,.1-.23c.07-.14.12-.28.18-.42.16-.37.32-.75.47-1.12.18-.42.35-.85.51-1.27.16-.39.31-.79.46-1.19l.06-.14a108.31693,108.31693,0,0,0,5.28-18.93v-.01a109.43156,109.43156,0,0,0,1.43-11.14c.01-.21.03-.41.04-.61.18-2.53.26-5.07.26-7.62,0-1.13-.02-2.25-.05-3.37a107.22221,107.22221,0,0,0-23.78-64.2c-.32-.4-.64-.8-.97-1.2-.33-.4-.67-.79-1-1.18a107.74949,107.74949,0,0,0-173.04,12.43c-.49.78-.98,1.57-1.45,2.36005-.48.8-.94,1.6-1.4,2.41A107.27619,107.27619,0,0,0,376.15456,657.20782Z"
transform="translate(-195.32909 -135.0622)"
fill="#f2f2f2"
id="path1040" />
<path
d="M479.61245,586.74147a254.162,254.162,0,0,0,54.14823,7.81528,67.79984,67.79984,0,0,0,8.29619-.05641c6.069-.51045,11.26756-2.41793,17.05716-3.46033,2.78331-.4997,5.88364-.86509,8.68575-1.40509q-.97121-1.20493-1.97732-2.38032c-6.04216.51046-12.17832,2.18151-18.293,2.95794-10.39442,1.3218-22.04079-.15314-29.92056-3.79078-3.823-1.76509-6.75141-3.96809-10.561-5.74124a4.9219,4.9219,0,0,0-1.65762-.50777,5.76807,5.76807,0,0,0-3.1272.81136c-6.39139,3.22658-15.67088,5.84869-24.28409,4.406C477.3369,585.8925,478.55662,586.46474,479.61245,586.74147Z"
transform="translate(-195.32909 -135.0622)"
fill="#fff"
id="path1042" />
<path
d="M441.49774,598.09637c-.07866-.01437-.15753-.02558-.23624-.03921a6.42127,6.42127,0,0,0,.93083-.7013Z"
transform="translate(-195.32909 -135.0622)"
fill="#fff"
id="path1044" />
<path
d="M389.93673,604.46221q5.396.4312,10.84845.6421a68.3417,68.3417,0,0,0,8.2962-.05641c6.069-.51315,11.26756-2.41793,17.05716-3.46033,5.306-.95375,11.75383-1.41315,15.12283-3.53018-8.70725-1.50718-17.72881,1.56091-26.70741,2.70271a63.77318,63.77318,0,0,1-21.76406-1.07464Q391.29884,602.03086,389.93673,604.46221Z"
transform="translate(-195.32909 -135.0622)"
fill="#fff"
id="path1046" />
<path
d="M433.4546,752.41785c.86.47,1.73.92,2.6,1.35.31.15.63.3.94.46.26.12.52.25.78.37.08.04.15.07.22.11.22.1.43.2.65.3.24.11005.48.22.71.33.27.12.53.24.8.35,1,.45,2.01.88,3.02,1.29a105.87419,105.87419,0,0,0,11.08,3.84c.17.05.35.1.53.15q2.41508.675,4.86005,1.23,4.275.99006,8.63,1.62h.02c.35.05.69.1,1.04.15.03,0,.06.01.1.01,0,.01,0,.01.01,0,.38.05.77.1,1.15.15a.24843.24843,0,0,0,.08.01c.36.05.73.09,1.09.13q.58494.06,1.17.12.45.045.9.09a.19454.19454,0,0,0,.08.01c.27.02.54.05.81.07h.04c.23.02.47.04.7.06006,2.79.22,5.59.31994,8.43.31994,3.07,0,6.12-.12,9.13-.38h.03c.65-.06,1.29-.12,1.93-.18.17-.02.33-.03.5-.05.03,0,.07-.01.1-.01.05-.01.1-.01.16-.02a.24843.24843,0,0,0,.08-.01c.22-.02.44-.05.67-.07l.75-.09c.25-.03.5-.07.75-.1.06-.01.11-.01.17-.02.18-.02.36-.05.54-.07995.09-.01.18-.02.26-.03.18-.03.35-.05.53-.08h.01a105.706,105.706,0,0,0,16.2-3.65c.28-.09.56-.17005.84-.26a40.177,40.177,0,0,0-22.03-27.86,9.49736,9.49736,0,0,0,2.73-4.86005,9.29434,9.29434,0,0,0-1.28-7.01995,8.9678,8.9678,0,0,0-5.66-3.88l-14.82-3.1a9.2231,9.2231,0,0,0-10.8,7.2,9.76284,9.76284,0,0,0-.2,2.22C450.21461,732.10785,439.94459,741.95782,433.4546,752.41785Z"
transform="translate(-195.32909 -135.0622)"
fill="#6c63ff"
id="path1048" />
<circle
cx="281.88445"
cy="541.39662"
r="30.87556"
fill="#ffb8b8"
id="circle1050" />
<path
d="M457.96832,703.07847c-3.29254-2.24911-6.65479-6.57094-8.89946-9.81005a37.07482,37.07482,0,0,1-5.72473-29.60121,11.3042,11.3042,0,0,1,2.65776-5.44507c1.45423-1.439,3.82936-2.12133,5.60422-1.10373a10.13228,10.13228,0,0,1,.41151-8.03954,17.85678,17.85678,0,0,1,5.27941-6.28793,29.73759,29.73759,0,0,1,30.2834-3.58533c1.92864.89366-1.23909-6.00834.77965-5.34279a8.22979,8.22979,0,0,0,6.21161-.10916c1.89865-.95571,8.19188,4.72263,7.46365,2.72565a9.37686,9.37686,0,0,1,2.17171,15.70349c-1.59351,1.34506-3.583,2.11958-5.32779,3.26163a15.29686,15.29686,0,0,0-4.67894,19.74039,20.26618,20.26618,0,0,0-5.96467-2.592,6.85938,6.85938,0,0,0-6.10476,1.46829,7.70642,7.70642,0,0,0-1.86711,5.81588c.06916,2.09526.56765,4.15949.6236,6.25514.16417,6.14915,3.45742,17.84-1.3102,21.10849C475.81474,709.82,462.46384,706.14933,457.96832,703.07847Z"
transform="translate(-195.32909 -135.0622)"
fill="#2f2e41"
id="path1052" />
<path
d="M498.44289,521.91827a4.93032,4.93032,0,0,1-1.89356-.38281,4.78958,4.78958,0,0,1-3.0039-4.49512v-4.8872a3.8707,3.8707,0,0,0-3.86622-3.8667,4.87211,4.87211,0,0,1-4.8667-4.8667V436.46466a4.872,4.872,0,0,1,4.86622-4.8667H711.50539a4.87211,4.87211,0,0,1,4.8667,4.8667v66.95508a4.87211,4.87211,0,0,1-4.8667,4.8667H515.64992a3.8427,3.8427,0,0,0-2.73438,1.13232l-11.062,11.062A4.79813,4.79813,0,0,1,498.44289,521.91827Z"
transform="translate(-195.32909 -135.0622)"
fill="#fff"
id="path1068" />
<path
d="M498.44289,522.91827a5.9116,5.9116,0,0,1-2.27686-.459,5.77532,5.77532,0,0,1-3.6206-5.41895v-4.88672a2.86981,2.86981,0,0,0-2.86622-2.86718,5.87351,5.87351,0,0,1-5.8667-5.86719V436.46466a5.873,5.873,0,0,1,5.86622-5.8667H711.50539a5.87341,5.87341,0,0,1,5.8667,5.8667v66.95459a5.87351,5.87351,0,0,1-5.8667,5.86719H515.64992a2.84681,2.84681,0,0,0-2.02735.83984l-11.062,11.06152A5.79479,5.79479,0,0,1,498.44289,522.91827ZM489.67873,432.598a3.8707,3.8707,0,0,0-3.86622,3.8667v66.95459a3.87091,3.87091,0,0,0,3.8667,3.86719,4.87241,4.87241,0,0,1,4.86622,4.86718v4.88672a3.86665,3.86665,0,0,0,6.60058,2.7334l11.0625-11.06152a4.83313,4.83313,0,0,1,3.44141-1.42578H711.50539a3.87091,3.87091,0,0,0,3.8667-3.86719V436.46466a3.8708,3.8708,0,0,0-3.8667-3.8667Z"
transform="translate(-195.32909 -135.0622)"
fill="#3f3d56"
id="path1070" />
<path
d="M692.44337,461.91046H512.565a2.59082,2.59082,0,0,1,0-5.18164H692.44337a2.59082,2.59082,0,0,1,0,5.18164Z"
transform="translate(-195.32909 -135.0622)"
fill="#6c63ff"
id="path1072" />
<path
d="M692.11281,472.9173H512.565a2.59082,2.59082,0,0,1,0-5.18164H692.11281a2.59082,2.59082,0,1,1,0,5.18164Z"
transform="translate(-195.32909 -135.0622)"
fill="#e6e6e6"
id="path1074" />
<path
d="M691.9341,483.92364H512.565a2.59082,2.59082,0,0,1,0-5.18164H691.9341a2.59082,2.59082,0,0,1,0,5.18164Z"
transform="translate(-195.32909 -135.0622)"
fill="#e6e6e6"
id="path1076" />
<path
d="M975.71461,421.20782c0,2.43-.08,4.86-.25,7.27-.18,2.88-.49,5.74-.91,8.58a107.2491,107.2491,0,0,1-8.02,27.73q-.29993.66-.6,1.32l-.3.66c-.1.22-.21.44-.31.65-.14.29-.27.57-.41.85-.17.35-.34.7-.52,1.05-.02.04-.04.09-.07.14-.15.3-.29.59-.45.89a1.7462,1.7462,0,0,1-.1.19c-.17.34-.35.69-.53,1.03-.2.36005-.39.73-.59,1.09-.48.89-.98,1.77-1.49,2.65-.15.26-.3.52-.46.78-.14.24-.28.48-.43.72-.1.16-.2.33-.30005.49-.16.27-.32.53-.49.8-.37.59-.75,1.18-1.12,1.77h-.01c-.17.27-.35.54-.53.8v.01c-.53.8-1.08,1.6-1.63,2.38995a.0098.0098,0,0,0-.01.01c-.44.64-.9,1.26-1.35,1.88v.01c-.3.39-.59.79-.89,1.18-.24.32-.49.65-.74.97-.05.06994-.11.12994-.16.2v.01c-.07.09-.14.17-.21.26q-1.59,2.04-3.28,3.99l-.18.21c-.06.07-.13.14-.19.22-.1.11005-.19.22-.29.33-.09.11-.19.22-.29.33a108.617,108.617,0,0,1-19.83,17.49c-.97.67-1.96,1.32-2.95,1.95-.25.16-.5.32-.75.47-.07.05-.15.1-.22.15-.14.08-.27.16-.41.25-.13.08-.26.16-.4.24-.53.33-1.07.65-1.61.97-.13.08-.27.16-.41.24006-.13.06994-.27.15-.41.23,0,.01,0,.01-.01.01h-.01v.01h-.01q-1.00506.58492-2.04,1.13995c-.37.2-.75.41-1.13.6-.86.47-1.73.92-2.6,1.35-.31.15-.63.3-.94.46-.26.12-.52.25-.78.37-.08.04-.15.07-.22.11-.22.1-.43.2-.65.3-.24.11005-.48.22-.71.33-.27.12-.53.24-.8.35-1,.45-2.01.88-3.02,1.29a105.87419,105.87419,0,0,1-11.08,3.84c-.17.05-.35.1-.53.15q-2.41508.675-4.86005,1.23-4.275.99006-8.63,1.62h-.02c-.35.05-.69.1-1.04.15-.03,0-.06.01-.1.01,0,.01,0,.01-.01,0-.38.05-.77.1-1.15.15a.24843.24843,0,0,1-.08.01c-.36.05-.73.09-1.09.13q-.58494.06-1.17.12-.45.045-.9.09a.19454.19454,0,0,1-.08.01c-.27.02-.54.05-.81.07h-.04c-.23.02-.47.04-.7.06006-2.79.22-5.59.31994-8.43.31994-3.07,0-6.12-.12-9.13-.38h-.03c-.65-.06-1.29-.12-1.93-.18-.17-.02-.33-.03-.5-.05-.03,0-.07-.01-.1-.01-.05-.01-.1-.01-.16-.02a.24843.24843,0,0,1-.08-.01c-.22-.02-.44-.05-.67-.07l-.75-.09c-.25-.03-.5-.07-.75-.1-.06-.01-.11-.01-.17-.02-.18-.02-.36-.05-.54-.07995-.09-.01-.18-.02-.26-.03-.18-.03-.35-.05-.53-.08h-.01a105.706,105.706,0,0,1-16.2-3.65c-.28-.09-.56-.17005-.84-.26-.01,0-.02-.01-.03-.01-.08-.03-.17-.05-.25-.08-.75-.24-1.49-.49-2.23-.75-1.02-.34-2.03-.71-3.04-1.09-.27-.11-.54-.21-.81-.32-.55-.21-1.09-.43-1.63-.65-.92-.37-1.84-.76-2.75-1.16-.85-.38-1.69-.76-2.53-1.16-.02-.00994-.05-.00994-.07-.03-.19-.09-.38-.18-.56-.27h-.01a107.18912,107.18912,0,0,1-13.29-7.59q-2.04-1.365-4.02-2.82c-1.06-.79-2.1-1.59-3.14-2.41a108.28427,108.28427,0,0,1-14.19-13.56v-.01c-.31-.34-.6-.69-.9-1.04-.05-.05-.09-.1-.13-.14-.19-.23-.37-.45-.56-.67v-.01c-.25-.29-.5-.59-.74-.89-.06-.06994-.11-.12994-.16-.19v-.01c-.2-.24-.39-.48-.59-.73-.1-.12-.19-.24-.29-.36v-.01c-.59-.74-1.16-1.48-1.73-2.24-.15-.2-.3-.41-.46-.62-.72-.97-1.42-1.96-2.1-2.95v-.01c-.26-.37-.51-.75-.77-1.12q-.375-.57-.75-1.13995c-.25-.4-.51-.79-.76-1.19l-.03-.06c-.25-.38-.5-.78-.74-1.17005-.19-.32-.38-.63-.57-.94-.12-.21-.25-.42-.37-.63995a2.3919,2.3919,0,0,1-.13-.21q-.42-.735-.84-1.47c-.33-.56994-.65-1.15-.97-1.73-.12-.24-.25-.48-.38-.72-.05-.08-.09-.16-.13-.24-.15-.28-.3-.56994-.44-.86-.15-.28-.3-.57-.45-.86-.14-.28-.29-.57-.43-.86-.11-.22-.21-.44-.32-.66a.2175.2175,0,0,0-.03-.05c-.05-.11-.1-.21-.14-.31a.21619.21619,0,0,1-.03-.05c-.11005-.22-.21-.45-.32-.67-.14-.29-.27-.59-.41-.88-.09-.2-.19-.41-.28-.62-.13-.29-.27-.58-.39-.88-.14-.3-.27-.6-.4-.91a2.29721,2.29721,0,0,1-.1-.23c-.07-.14-.12-.28-.18-.42-.16-.37-.32-.75-.47-1.12-.18-.42005-.35-.85-.51-1.27-.16-.39-.31-.79-.46-1.19l-.06-.14a108.31693,108.31693,0,0,1-5.28-18.93v-.01a109.43156,109.43156,0,0,1-1.43-11.14c-.01-.21-.03-.41-.04-.61-.18-2.53-.26-5.07-.26-7.62,0-1.13.02-2.25.05-3.37a107.22221,107.22221,0,0,1,23.78-64.2c.32-.4.64-.8.97-1.2.33-.4.67-.79,1-1.18a107.74949,107.74949,0,0,1,173.04,12.43c.49.78.98,1.57,1.45,2.36005.48.8.94,1.6,1.4,2.41A107.27629,107.27629,0,0,1,975.71461,421.20782Z"
transform="translate(-195.32909 -135.0622)"
fill="#f2f2f2"
id="path1078" />
<path
d="M872.25672,350.74147a254.162,254.162,0,0,1-54.14823,7.81528,67.79984,67.79984,0,0,1-8.29619-.05641c-6.069-.51045-11.26756-2.41793-17.05716-3.46033-2.78331-.4997-5.88364-.86509-8.68575-1.40509q.9712-1.20494,1.97732-2.38032c6.04216.51046,12.17832,2.18151,18.293,2.95794,10.39441,1.3218,22.04078-.15314,29.92055-3.79078,3.823-1.76509,6.75141-3.96809,10.561-5.74124a4.9219,4.9219,0,0,1,1.65762-.50777,5.768,5.768,0,0,1,3.1272.81136c6.39139,3.22658,15.67089,5.84869,24.28409,4.406C874.53227,349.8925,873.31255,350.46474,872.25672,350.74147Z"
transform="translate(-195.32909 -135.0622)"
fill="#fff"
id="path1080" />
<path
d="M910.37143,362.09637c.07866-.01437.15753-.02558.23624-.03921a6.42127,6.42127,0,0,1-.93083-.7013Z"
transform="translate(-195.32909 -135.0622)"
fill="#fff"
id="path1082" />
<path
d="M961.93244,368.46221q-5.396.4312-10.84845.6421a68.3417,68.3417,0,0,1-8.2962-.05641c-6.069-.51315-11.26756-2.41793-17.05716-3.46033-5.306-.95375-11.75383-1.41315-15.12283-3.53018,8.70725-1.50718,17.72881,1.56091,26.70741,2.70271a63.77318,63.77318,0,0,0,21.76406-1.07464Q960.57033,366.03086,961.93244,368.46221Z"
transform="translate(-195.32909 -135.0622)"
fill="#fff"
id="path1084" />
<ellipse
cx="660.50012"
cy="300.20735"
rx="32.35846"
ry="33.07962"
fill="#ffb6b6"
id="ellipse1086" />
<path
d="M918.41457,516.41785c-.86.47-1.73.92-2.6,1.35-.31.15-.63.3-.94.46-.26.12-.52.25-.78.37-.08.04-.15.07-.22.11-.22.1-.43.2-.65.3-.24.11005-.48.22-.71.33-.27.12-.53.24-.8.35-1,.45-2.01.88-3.02,1.29a105.87419,105.87419,0,0,1-11.08,3.84c-.17.05-.35.1-.53.15q-2.41508.675-4.86005,1.23-4.275.99006-8.63,1.62h-.02c-.35.05-.69.1-1.04.15-.03,0-.06.01-.1.01,0,.01,0,.01-.01,0-.38.05-.77.1-1.15.15a.24843.24843,0,0,1-.08.01c-.36.05-.73.09-1.09.13q-.58494.06-1.17.12-.45.045-.9.09a.19454.19454,0,0,1-.08.01c-.27.02-.54.05-.81.07h-.04c-.23.02-.47.04-.7.06006-2.79.22-5.59.31994-8.43.31994-3.07,0-6.12-.12-9.13-.38h-.03c-.65-.06-1.29-.12-1.93-.18-.17-.02-.33-.03-.5-.05-.03,0-.07-.01-.1-.01-.05-.01-.1-.01-.16-.02a.24843.24843,0,0,1-.08-.01c-.22-.02-.44-.05-.67-.07l-.75-.09c-.25-.03-.5-.07-.75-.1-.06-.01-.11-.01-.17-.02-.18-.02-.36-.05-.54-.07995-.09-.01-.18-.02-.26-.03-.18-.03-.35-.05-.53-.08h-.01a105.706,105.706,0,0,1-16.2-3.65c-.28-.09-.56-.17005-.84-.26a40.177,40.177,0,0,1,22.03-27.86,9.49736,9.49736,0,0,1-2.73-4.86005,9.29434,9.29434,0,0,1,1.28-7.01995,8.9678,8.9678,0,0,1,5.66-3.88l14.82-3.1a9.22312,9.22312,0,0,1,10.8,7.2,9.7634,9.7634,0,0,1,.2,2.22C901.65456,496.10785,911.92458,505.95782,918.41457,516.41785Z"
transform="translate(-195.32909 -135.0622)"
fill="#6c63ff"
id="path1088" />
<path
d="M942.41713,420.92308c-5.50024-6.41419-8.11524-14.9242-12.51227-22.20783-4.39709-7.28369-12.04883-13.80261-20.13245-12.1076-5.57232,1.16852-9.87878,6.08563-12.059,11.56769-2.17974,5.482-2.58838,11.52755-2.97046,17.45013.135-12.17114-7.88372-23.97028-18.92974-27.85394-11.04517-3.88364-24.26429.44839-31.205,10.22647a16.8664,16.8664,0,0,0-22.40161-.47025c-6.20825,5.54874-7.58216,16.10984-3.01642,23.18738s14.4538,9.71414,21.70959,5.78885l2.56519,2.35968c-1.44391,5.05014,1.3562,11.01556,6.06323,12.9166,3.81684,1.54174,9.20532,1.28512,10.63361,5.29061.99817,2.79877-.86743,5.78617-2.852,7.93683-1.98407,2.15064-4.34015,4.37152-4.57843,7.3476-.36011,4.49042,4.28027,7.63605,8.49853,8.5834,10.17145,2.28446,21.4574-2.42572,27.31989-11.4021q.16946-.25964.36853-.52a39.27849,39.27849,0,0,0,7.90057-30.03344c-.32977-2.07468.0025-3.61783,1.43292-4.10916,6.18256-2.12369,23.50434,2.33352,28.74653,6.36187,5.24261,4.02835,9.44061,9.3595,14.49,13.64783,5.04986,4.28839,11.48462,7.59625,17.89618,6.42847,5.135-.93528,9.50348-4.66327,12.44189-9.16,2.938-4.4967,4.629-9.73825,6.14374-14.94729C966.61519,433.76451,951.07466,431.01866,942.41713,420.92308Z"
transform="translate(-195.32909 -135.0622)"
fill="#2f2e41"
id="path1090" />
<path
d="M278.15456,354.20782c0,2.43.08,4.86.25,7.27.18,2.88.49,5.74.91,8.58a107.2491,107.2491,0,0,0,8.02,27.73q.29993.66.6,1.32l.3.66c.1.22.21.44.31.65.14.29.27.57.41.85.17.35.34.7.52,1.05.02.04.04.09.07.14.15.3.29.59.45.89a1.7462,1.7462,0,0,0,.1.19c.17.34.35.69.53,1.03.2.36005.39.73.59,1.09.48.89.98,1.77,1.49,2.65.15.26.3.52.46.78.14.24.28.48.43.72.1.16.2.33.3.49.16.27.32.53.49.8.37.59.75,1.18,1.12,1.77h.01c.17.27.35.54.53.8v.01c.53.8,1.08,1.6,1.63,2.38995a.0098.0098,0,0,1,.01.01c.44.64.9,1.26,1.35,1.88v.01c.3.39.59.79.89,1.18.24.32.49.65.74.97.05.06994.11.12994.16.2v.01c.07.09.14.17.21.26q1.59,2.04,3.28,3.99l.18.21c.06.07.13.14.19.22.1.11005.19.22.29.33.09.11.19.22.29.33a108.617,108.617,0,0,0,19.83,17.49c.97.67,1.96,1.32,2.95,1.95.25.16.5.32.75.47.07.05.15.1.22.15.14.08.27.16.41.25.13.08.26.16.4.24.53.33,1.07.65,1.61.97.13.08.27.16.41.24006.13.06994.27.15.41.23,0,.01,0,.01.01.01h.01v.01h.01q1.00506.58492,2.04,1.13995c.37.2.75.41,1.13.6.86.47,1.73.92,2.6,1.35.31.15.63.3.94.46.26.12.52.25.78.37.08.04.15.07.22.11.22.1.43.2.65.3.24.11005.48.22.71.33.27.12.53.24.8.35,1,.45,2.01.88,3.02,1.29a105.87419,105.87419,0,0,0,11.08,3.84c.17.05.35.1.53.15q2.41508.675,4.86005,1.23,4.275.99006,8.63,1.62h.02c.35.05.69.1,1.04.15.03,0,.06.01.1.01,0,.01,0,.01.01,0,.38.05.77.1,1.15.15a.24843.24843,0,0,0,.08.01c.36.05.73.09,1.09.13q.58494.06,1.17.12.45.045.9.09a.19454.19454,0,0,0,.08.01c.27.02.54.05.81.07h.04c.23.02.47.04.7.06006,2.79.22,5.59.31994,8.43.31994,3.07,0,6.12-.12,9.13-.38h.03c.65-.06,1.29-.12,1.93-.18.17-.02.33-.03.5-.05.03,0,.07-.01.1-.01.05-.01.1-.01.16-.02a.24843.24843,0,0,0,.08-.01c.22-.02.44-.05.67-.07l.75-.09c.25-.03.5-.07.75-.1.06-.01.11-.01.17-.02.18-.02.36-.05.54-.07995.09-.01.18-.02.26-.03.18-.03.35-.05.53-.08h.01a105.706,105.706,0,0,0,16.2-3.65c.28-.09.56-.17005.84-.26.01,0,.02-.01.03-.01.08-.03.17-.05.25-.08.75-.24,1.49-.49,2.23-.75,1.02-.34,2.03-.71,3.04-1.09.27-.11.54-.21.81-.32.55-.21,1.09-.43,1.63-.65.92-.37,1.84-.76,2.75-1.16.85-.38,1.69-.76,2.53-1.16.02-.00994.05-.00994.07-.03.19-.09.38-.18.56-.27h.01a107.18912,107.18912,0,0,0,13.29-7.59q2.04-1.365,4.02-2.82c1.06-.79,2.1-1.59,3.14-2.41a108.28427,108.28427,0,0,0,14.19-13.56v-.01c.31-.34.6-.69.9-1.04.05-.05.09-.1.13-.14.19-.23.37-.45.56-.67v-.01c.25-.29.5-.59.74-.89.06-.06994.11-.12994.16-.19v-.01c.2-.24.38995-.48.59-.73.1-.12.19-.24.29-.36v-.01c.59-.74,1.16-1.48,1.73-2.24.15-.2.3-.41.46-.62.72-.97,1.42-1.96,2.1-2.95v-.01c.26-.37.51-.75.77-1.12q.375-.57.75-1.13995c.25-.4.51-.79.76-1.19l.03-.06c.25-.38.5-.78.74-1.17005.19-.32.38-.63.57-.94.12-.21.25-.42.37-.63995a2.3919,2.3919,0,0,0,.13-.21q.42-.735.84-1.47c.33-.56994.65-1.15.97-1.73.12-.24.25-.48.38-.72.05-.08.09-.16.13-.24.15-.28.3-.56994.44-.86.15-.28.3-.57.45-.86.13995-.28.29-.57.43-.86.11-.22.21-.44.32-.66a.2175.2175,0,0,1,.03-.05c.05-.11.1-.21.14-.31a.21619.21619,0,0,0,.03-.05c.11005-.22.21-.45.32-.67.14-.29.27-.59.41-.88.09-.2.19-.41.28-.62.13-.29.27-.58.39-.88.14-.3.27-.6.4-.91a2.29962,2.29962,0,0,0,.1-.23c.07-.14.12-.28.18-.42.16-.37.32-.75.47-1.12.18-.42005.35-.85.51-1.27.16-.39.31-.79.46-1.19l.06-.14a108.31693,108.31693,0,0,0,5.28-18.93v-.01a109.43156,109.43156,0,0,0,1.43-11.14c.01-.21.03-.41.04-.61.18-2.53.26-5.07.26-7.62,0-1.13-.02-2.25-.05-3.37a107.22221,107.22221,0,0,0-23.78-64.2c-.32-.4-.64-.8-.97-1.2-.33-.4-.67-.79-1-1.18a107.74949,107.74949,0,0,0-173.04,12.43c-.49.78-.98,1.57-1.45,2.36005-.48.8-.94,1.6-1.4,2.41A107.27619,107.27619,0,0,0,278.15456,354.20782Z"
transform="translate(-195.32909 -135.0622)"
fill="#f2f2f2"
id="path1092" />
<path
d="M381.61245,283.74147a254.162,254.162,0,0,0,54.14823,7.81528,67.79984,67.79984,0,0,0,8.29619-.05641c6.069-.51045,11.26756-2.41793,17.05716-3.46033,2.78331-.4997,5.88364-.86509,8.68575-1.40509q-.9712-1.20494-1.97732-2.38032c-6.04216.51046-12.17832,2.18151-18.293,2.95794-10.39442,1.3218-22.04079-.15314-29.92056-3.79078-3.823-1.76509-6.75141-3.96809-10.561-5.74124a4.9219,4.9219,0,0,0-1.65762-.50777,5.76807,5.76807,0,0,0-3.1272.81136c-6.39139,3.22658-15.67088,5.84869-24.28409,4.406C379.3369,282.8925,380.55662,283.46474,381.61245,283.74147Z"
transform="translate(-195.32909 -135.0622)"
fill="#fff"
id="path1094" />
<path
d="M343.49774,295.09637c-.07866-.01437-.15753-.02558-.23624-.03921a6.42127,6.42127,0,0,0,.93083-.7013Z"
transform="translate(-195.32909 -135.0622)"
fill="#fff"
id="path1096" />
<path
d="M291.93673,301.46221q5.396.4312,10.84845.6421a68.3417,68.3417,0,0,0,8.2962-.05641c6.069-.51315,11.26756-2.41793,17.05716-3.46033,5.306-.95375,11.75383-1.41315,15.12283-3.53018-8.70725-1.50718-17.72881,1.56091-26.70741,2.70271a63.77318,63.77318,0,0,1-21.76406-1.07464Q293.29884,299.03086,291.93673,301.46221Z"
transform="translate(-195.32909 -135.0622)"
fill="#fff"
id="path1098" />
<path
d="M335.4546,449.41785c.86.47,1.73.92,2.6,1.35.31.15.63.3.94.46.26.12.52.25.78.37.08.04.15.07.22.11.22.1.43.2.65.3.24.11005.48.22.71.33.27.12.53.24.8.35,1,.45,2.01.88,3.02,1.29a105.87419,105.87419,0,0,0,11.08,3.84c.17.05.35.1.53.15q2.41508.675,4.86005,1.23,4.275.99006,8.63,1.62h.02c.35.05.69.1,1.04.15.03,0,.06.01.1.01,0,.01,0,.01.01,0,.38.05.77.1,1.15.15a.24843.24843,0,0,0,.08.01c.36.05.73.09,1.09.13q.58494.06,1.17.12.45.045.9.09a.19454.19454,0,0,0,.08.01c.27.02.54.05.81.07h.04c.23.02.47.04.7.06006,2.79.22,5.59.31994,8.43.31994,3.07,0,6.12-.12,9.13-.38h.03c.65-.06,1.29-.12,1.93-.18.17-.02.33-.03.5-.05.03,0,.07-.01.1-.01.05-.01.1-.01.16-.02a.24843.24843,0,0,0,.08-.01c.22-.02.44-.05.67-.07l.75-.09c.25-.03.5-.07.75-.1.06-.01.11-.01.17-.02.18-.02.36-.05.54-.07995.09-.01.18-.02.26-.03.18-.03.35-.05.53-.08h.01a105.706,105.706,0,0,0,16.2-3.65c.28-.09.56-.17005.84-.26a40.177,40.177,0,0,0-22.03-27.86,9.49736,9.49736,0,0,0,2.73-4.86005,9.29434,9.29434,0,0,0-1.28-7.01995,8.9678,8.9678,0,0,0-5.66-3.88l-14.82-3.1a9.2231,9.2231,0,0,0-10.8,7.2,9.76284,9.76284,0,0,0-.2,2.22C352.21461,429.10785,341.94459,438.95782,335.4546,449.41785Z"
transform="translate(-195.32909 -135.0622)"
fill="#6c63ff"
id="path1100" />
<circle
cx="198.75404"
cy="229.04877"
r="36.07316"
fill="#9e616a"
id="circle1102" />
<path
d="M388.957,398.256c-9.03088,2.43906-18.98715-.80368-26.03488-6.95474s-11.45636-14.86707-13.99658-23.87c-1.86041-6.59356-2.74253-13.91271.1933-20.10281s10.83712-10.36639,16.933-7.23959c-3.7605-4.38129-1.88181-11.62772,2.51016-15.37573s10.42282-4.82261,16.16669-5.41c8.33594-.85248,16.9465-.9479,24.87878,1.75269s15.13953,8.60486,17.62207,16.60809a12.64027,12.64027,0,0,0,15.07663-3.404,11.45182,11.45182,0,0,1-6.30975,12.41714l12.15372-3.94033c2.09021,4.42428-1.57233,9.9-6.25589,11.31683s-9.73932-.13181-14.18381-2.17871-8.75159-4.63474-13.57922-5.433-10.49836.7529-12.73139,5.10685c-1.16958,2.28042-1.23059,4.95123-1.90964,7.42248s-2.3992,5.03348-4.9541,5.23572c-1.61416.12778-3.19455-.732-4.8042-.55621a5.17666,5.17666,0,0,0-4.05441,3.96139,15.1987,15.1987,0,0,0-.0144,5.98l3.08725,23.598Z"
transform="translate(-195.32909 -135.0622)"
fill="#2f2e41"
id="path1104" />
</svg>

After

Width:  |  Height:  |  Size: 39 KiB

2
public/images/cross-circle.svg

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" id="Outline" viewBox="0 0 24 24" width="16" height="16"><path fill="#FFF" d="M16,8a1,1,0,0,0-1.414,0L12,10.586,9.414,8A1,1,0,0,0,8,9.414L10.586,12,8,14.586A1,1,0,0,0,9.414,16L12,13.414,14.586,16A1,1,0,0,0,16,14.586L13.414,12,16,9.414A1,1,0,0,0,16,8Z"/><path fill="#FFF" d="M12,0A12,12,0,1,0,24,12,12.013,12.013,0,0,0,12,0Zm0,22A10,10,0,1,1,22,12,10.011,10.011,0,0,1,12,22Z"/></svg>

After

Width:  |  Height:  |  Size: 461 B

1
public/images/double-check-seen.svg

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 15" width="16" height="15"><path fill="#4FC3F7" d="M15.01 3.316l-.478-.372a.365.365 0 0 0-.51.063L8.666 9.879a.32.32 0 0 1-.484.033l-.358-.325a.319.319 0 0 0-.484.032l-.378.483a.418.418 0 0 0 .036.541l1.32 1.266c.143.14.361.125.484-.033l6.272-8.048a.366.366 0 0 0-.064-.512zm-4.1 0l-.478-.372a.365.365 0 0 0-.51.063L4.566 9.879a.32.32 0 0 1-.484.033L1.891 7.769a.366.366 0 0 0-.515.006l-.423.433a.364.364 0 0 0 .006.514l3.258 3.185c.143.14.361.125.484-.033l6.272-8.048a.365.365 0 0 0-.063-.51z"></path></svg>

After

Width:  |  Height:  |  Size: 564 B

1
public/images/double-check-unseen.svg

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 15" width="16" height="15"><path fill="#919191" d="M15.01 3.316l-.478-.372a.365.365 0 0 0-.51.063L8.666 9.879a.32.32 0 0 1-.484.033l-.358-.325a.319.319 0 0 0-.484.032l-.378.483a.418.418 0 0 0 .036.541l1.32 1.266c.143.14.361.125.484-.033l6.272-8.048a.366.366 0 0 0-.064-.512zm-4.1 0l-.478-.372a.365.365 0 0 0-.51.063L4.566 9.879a.32.32 0 0 1-.484.033L1.891 7.769a.366.366 0 0 0-.515.006l-.423.433a.364.364 0 0 0 .006.514l3.258 3.185c.143.14.361.125.484-.033l6.272-8.048a.365.365 0 0 0-.063-.51z"></path></svg>

After

Width:  |  Height:  |  Size: 564 B

1
public/images/double-check.svg

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 15" width="16" height="15"><path fill="currentColor" d="M15.01 3.316l-.478-.372a.365.365 0 0 0-.51.063L8.666 9.879a.32.32 0 0 1-.484.033l-.358-.325a.319.319 0 0 0-.484.032l-.378.483a.418.418 0 0 0 .036.541l1.32 1.266c.143.14.361.125.484-.033l6.272-8.048a.366.366 0 0 0-.064-.512zm-4.1 0l-.478-.372a.365.365 0 0 0-.51.063L4.566 9.879a.32.32 0 0 1-.484.033L1.891 7.769a.366.366 0 0 0-.515.006l-.423.433a.364.364 0 0 0 .006.514l3.258 3.185c.143.14.361.125.484-.033l6.272-8.048a.365.365 0 0 0-.063-.51z"></path></svg>

After

Width:  |  Height:  |  Size: 569 B

1
public/images/down-arrow.svg

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 18" width="18" height="18"><path fill="#919191" d="M3.3 4.6L9 10.3l5.7-5.7 1.6 1.6L9 13.4 1.7 6.2l1.6-1.6z"></path></svg>

After

Width:  |  Height:  |  Size: 177 B

2
public/images/enter.svg

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" id="Layer_1" data-name="Layer 1" viewBox="0 0 24 24" width="18" height="18"><path fill="#919191" d="M18.589,0H5.411A5.371,5.371,0,0,0,0,5.318V7.182a1.5,1.5,0,0,0,3,0V5.318A2.369,2.369,0,0,1,5.411,3H18.589A2.369,2.369,0,0,1,21,5.318V18.682A2.369,2.369,0,0,1,18.589,21H5.411A2.369,2.369,0,0,1,3,18.682V16.818a1.5,1.5,0,1,0-3,0v1.864A5.371,5.371,0,0,0,5.411,24H18.589A5.371,5.371,0,0,0,24,18.682V5.318A5.371,5.371,0,0,0,18.589,0Z"/><path fill="#919191" d="M3.5,12A1.5,1.5,0,0,0,5,13.5H5l9.975-.027-3.466,3.466a1.5,1.5,0,0,0,2.121,2.122l4.586-4.586a3.5,3.5,0,0,0,0-4.95L13.634,4.939a1.5,1.5,0,1,0-2.121,2.122l3.413,3.412L5,10.5A1.5,1.5,0,0,0,3.5,12Z"/></svg>

After

Width:  |  Height:  |  Size: 734 B

BIN
public/images/favicon.ico

Binary file not shown.

After

Width:  |  Height:  |  Size: 438 B

2
public/images/file.svg

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" id="Outline" viewBox="0 0 24 24" width="512" height="512"><path fill="#919191" d="M19.949,5.536,16.465,2.05A6.958,6.958,0,0,0,11.515,0H7A5.006,5.006,0,0,0,2,5V19a5.006,5.006,0,0,0,5,5H17a5.006,5.006,0,0,0,5-5V10.485A6.951,6.951,0,0,0,19.949,5.536ZM18.535,6.95A4.983,4.983,0,0,1,19.316,8H15a1,1,0,0,1-1-1V2.684a5.01,5.01,0,0,1,1.051.78ZM20,19a3,3,0,0,1-3,3H7a3,3,0,0,1-3-3V5A3,3,0,0,1,7,2h4.515c.164,0,.323.032.485.047V7a3,3,0,0,0,3,3h4.953c.015.162.047.32.047.485Z"/></svg>

After

Width:  |  Height:  |  Size: 553 B

1
public/images/gt-arrow.svg

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 8 12" width="8" height="12"><path fill="#404B4F" d="M2.173 1l4.584 4.725-4.615 4.615-1.103-1.103 3.512-3.512L1 2.173 2.173 1z"></path></svg>

After

Width:  |  Height:  |  Size: 193 B

1
public/images/icons.svg

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="#919191" d="M9.153 11.603c.795 0 1.439-.879 1.439-1.962s-.644-1.962-1.439-1.962-1.439.879-1.439 1.962.644 1.962 1.439 1.962zm-3.204 1.362c-.026-.307-.131 5.218 6.063 5.551 6.066-.25 6.066-5.551 6.066-5.551-6.078 1.416-12.129 0-12.129 0zm11.363 1.108s-.669 1.959-5.051 1.959c-3.505 0-5.388-1.164-5.607-1.959 0 0 5.912 1.055 10.658 0zM11.804 1.011C5.609 1.011.978 6.033.978 12.228s4.826 10.761 11.021 10.761S23.02 18.423 23.02 12.228c.001-6.195-5.021-11.217-11.216-11.217zM12 21.354c-5.273 0-9.381-3.886-9.381-9.159s3.942-9.548 9.215-9.548 9.548 4.275 9.548 9.548c-.001 5.272-4.109 9.159-9.382 9.159zm3.108-9.751c.795 0 1.439-.879 1.439-1.962s-.644-1.962-1.439-1.962-1.439.879-1.439 1.962.644 1.962 1.439 1.962z"></path></svg>

After

Width:  |  Height:  |  Size: 820 B

BIN
public/images/icons/csv-file.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
public/images/icons/doc-file.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

BIN
public/images/icons/notfound-file.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
public/images/icons/pdf-file.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

BIN
public/images/icons/ppt-file.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

BIN
public/images/icons/txt-file.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

BIN
public/images/icons/xls-file.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
public/images/icons/zip-file.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

BIN
public/images/loading.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

1
public/images/manage_chats.svg

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 11 KiB

1
public/images/menu-icon.svg

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="#919191" d="M12 7a2 2 0 1 0-.001-4.001A2 2 0 0 0 12 7zm0 2a2 2 0 1 0-.001 3.999A2 2 0 0 0 12 9zm0 6a2 2 0 1 0-.001 3.999A2 2 0 0 0 12 15z"></path></svg>

After

Width:  |  Height:  |  Size: 247 B

1
public/images/message-icon.svg

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="#919191" d="M19.005 3.175H4.674C3.642 3.175 3 3.789 3 4.821V21.02l3.544-3.514h12.461c1.033 0 2.064-1.06 2.064-2.093V4.821c-.001-1.032-1.032-1.646-2.064-1.646zm-4.989 9.869H7.041V11.1h6.975v1.944zm3-4H7.041V7.1h9.975v1.944z"></path></svg>

After

Width:  |  Height:  |  Size: 332 B

1
public/images/message-tail-receiver.svg

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 8 13" width="8" height="13"><path opacity=".13" fill="#FFFFFF" d="M1.533 3.568L8 12.193V1H2.812C1.042 1 .474 2.156 1.533 3.568z"></path><path fill="#FFFFFF" d="M1.533 2.568L8 11.193V0H2.812C1.042 0 .474 1.156 1.533 2.568z"></path></svg>

After

Width:  |  Height:  |  Size: 289 B

1
public/images/message-tail-sender.svg

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 8 13" width="8" height="13"><path opacity=".13" d="M5.188 1H0v11.193l6.467-8.625C7.526 2.156 6.958 1 5.188 1z"></path><path fill="#DCF8C6" d="M5.188 0H0v11.193l6.467-8.625C7.526 1.156 6.958 0 5.188 0z"></path></svg>

After

Width:  |  Height:  |  Size: 268 B

BIN
public/images/messenger.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

1
public/images/microphone-seen.svg

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 20" width="12" height="20"><path fill="#70CEF9" d="M6 11.745a2 2 0 0 0 2-2V4.941a2 2 0 0 0-4 0v4.803a2 2 0 0 0 2 2.001zm3.495-2.001c0 1.927-1.568 3.495-3.495 3.495s-3.495-1.568-3.495-3.495H1.11c0 2.458 1.828 4.477 4.192 4.819v2.495h1.395v-2.495c2.364-.342 4.193-2.362 4.193-4.82H9.495v.001z"></path></svg>

After

Width:  |  Height:  |  Size: 361 B

2
public/images/microphone.svg

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" id="Outline" viewBox="0 0 24 24" width="24" height="24"><path fill="#919191" d="M12,20a8.009,8.009,0,0,0,8-8V8A8,8,0,0,0,4,8v4A8.009,8.009,0,0,0,12,20ZM12,2a6.006,6.006,0,0,1,5.91,5H15a1,1,0,0,0,0,2h3v2H15a1,1,0,0,0,0,2h2.91A5.993,5.993,0,0,1,6.09,13H9a1,1,0,0,0,0-2H6V9H9A1,1,0,0,0,9,7H6.09A6.006,6.006,0,0,1,12,2Z"/><path fill="#919191" d="M23,12a1,1,0,0,0-1,1,9.01,9.01,0,0,1-9,9H11a9.011,9.011,0,0,1-9-9,1,1,0,0,0-2,0A11.013,11.013,0,0,0,11,24h2A11.013,11.013,0,0,0,24,13,1,1,0,0,0,23,12Z"/></svg>

After

Width:  |  Height:  |  Size: 581 B

1
public/images/notifications.svg

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="48" height="48"><path fill="#F5FCFF" d="M24.154 2C11.919 2 2 11.924 2 24.165S11.919 46.33 24.154 46.33s22.154-9.924 22.154-22.165S36.389 2 24.154 2zm-.744 15.428v-.618c0-.706.618-1.324 1.324-1.324s1.323.618 1.323 1.324v.618c2.559.618 4.412 2.823 4.412 5.559v3.176l-8.294-8.294a5.056 5.056 0 0 1 1.235-.441zm1.323 15.706a1.77 1.77 0 0 1-1.765-1.765h3.529a1.768 1.768 0 0 1-1.764 1.765zm7.236-.883l-1.765-1.765H17.233v-.882l1.765-1.765v-4.853a5.56 5.56 0 0 1 .794-2.912l-2.559-2.559 1.147-1.147 14.735 14.736-1.146 1.147z"></path></svg>

After

Width:  |  Height:  |  Size: 601 B

2
public/images/paper-plane.svg

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" id="Outline" viewBox="0 0 24 24" width="24" height="24"><path fill="#919191" d="M23.119.882a2.966,2.966,0,0,0-2.8-.8l-16,3.37a4.995,4.995,0,0,0-2.853,8.481L3.184,13.65a1,1,0,0,1,.293.708v3.168a2.965,2.965,0,0,0,.3,1.285l-.008.007.026.026A3,3,0,0,0,5.157,20.2l.026.026.007-.008a2.965,2.965,0,0,0,1.285.3H9.643a1,1,0,0,1,.707.292l1.717,1.717A4.963,4.963,0,0,0,15.587,24a5.049,5.049,0,0,0,1.605-.264,4.933,4.933,0,0,0,3.344-3.986L23.911,3.715A2.975,2.975,0,0,0,23.119.882ZM4.6,12.238,2.881,10.521a2.94,2.94,0,0,1-.722-3.074,2.978,2.978,0,0,1,2.5-2.026L20.5,2.086,5.475,17.113V14.358A2.978,2.978,0,0,0,4.6,12.238Zm13.971,7.17a3,3,0,0,1-5.089,1.712L11.762,19.4a2.978,2.978,0,0,0-2.119-.878H6.888L21.915,3.5Z"/></svg>

After

Width:  |  Height:  |  Size: 791 B

1
public/images/pause.svg

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" id="Outline" viewBox="0 0 24 24" width="20" height="20"><path fill="#919191" d="M6.5,0A3.5,3.5,0,0,0,3,3.5v17a3.5,3.5,0,0,0,7,0V3.5A3.5,3.5,0,0,0,6.5,0ZM8,20.5a1.5,1.5,0,0,1-3,0V3.5a1.5,1.5,0,0,1,3,0Z"/><path fill="#919191" d="M17.5,0A3.5,3.5,0,0,0,14,3.5v17a3.5,3.5,0,0,0,7,0V3.5A3.5,3.5,0,0,0,17.5,0ZM19,20.5a1.5,1.5,0,0,1-3,0V3.5a1.5,1.5,0,0,1,3,0Z"/></svg>

After

Width:  |  Height:  |  Size: 400 B

2
public/images/picture.svg

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" id="Outline" viewBox="0 0 24 24" width="512" height="512"><path fill="#919191" d="M19,0H5A5.006,5.006,0,0,0,0,5V19a5.006,5.006,0,0,0,5,5H19a5.006,5.006,0,0,0,5-5V5A5.006,5.006,0,0,0,19,0ZM5,2H19a3,3,0,0,1,3,3V19a2.951,2.951,0,0,1-.3,1.285l-9.163-9.163a5,5,0,0,0-7.072,0L2,14.586V5A3,3,0,0,1,5,2ZM5,22a3,3,0,0,1-3-3V17.414l4.878-4.878a3,3,0,0,1,4.244,0L20.285,21.7A2.951,2.951,0,0,1,19,22Z"/><path fill="#919191" d="M16,10.5A3.5,3.5,0,1,0,12.5,7,3.5,3.5,0,0,0,16,10.5Zm0-5A1.5,1.5,0,1,1,14.5,7,1.5,1.5,0,0,1,16,5.5Z"/></svg>

After

Width:  |  Height:  |  Size: 603 B

1
public/images/placeholder-image.svg

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 212 212" width="212" height="212"><path fill="#DFE5E7" class="background" d="M106.251.5C164.653.5 212 47.846 212 106.25S164.653 212 106.25 212C47.846 212 .5 164.654.5 106.25S47.846.5 106.251.5z"></path><path fill="#FFF" class="primary" d="M173.561 171.615a62.767 62.767 0 0 0-2.065-2.955 67.7 67.7 0 0 0-2.608-3.299 70.112 70.112 0 0 0-3.184-3.527 71.097 71.097 0 0 0-5.924-5.47 72.458 72.458 0 0 0-10.204-7.026 75.2 75.2 0 0 0-5.98-3.055c-.062-.028-.118-.059-.18-.087-9.792-4.44-22.106-7.529-37.416-7.529s-27.624 3.089-37.416 7.529c-.338.153-.653.318-.985.474a75.37 75.37 0 0 0-6.229 3.298 72.589 72.589 0 0 0-9.15 6.395 71.243 71.243 0 0 0-5.924 5.47 70.064 70.064 0 0 0-3.184 3.527 67.142 67.142 0 0 0-2.609 3.299 63.292 63.292 0 0 0-2.065 2.955 56.33 56.33 0 0 0-1.447 2.324c-.033.056-.073.119-.104.174a47.92 47.92 0 0 0-1.07 1.926c-.559 1.068-.818 1.678-.818 1.678v.398c18.285 17.927 43.322 28.985 70.945 28.985 27.678 0 52.761-11.103 71.055-29.095v-.289s-.619-1.45-1.992-3.778a58.346 58.346 0 0 0-1.446-2.322zM106.002 125.5c2.645 0 5.212-.253 7.68-.737a38.272 38.272 0 0 0 3.624-.896 37.124 37.124 0 0 0 5.12-1.958 36.307 36.307 0 0 0 6.15-3.67 35.923 35.923 0 0 0 9.489-10.48 36.558 36.558 0 0 0 2.422-4.84 37.051 37.051 0 0 0 1.716-5.25c.299-1.208.542-2.443.725-3.701.275-1.887.417-3.827.417-5.811s-.142-3.925-.417-5.811a38.734 38.734 0 0 0-1.215-5.494 36.68 36.68 0 0 0-3.648-8.298 35.923 35.923 0 0 0-9.489-10.48 36.347 36.347 0 0 0-6.15-3.67 37.124 37.124 0 0 0-5.12-1.958 37.67 37.67 0 0 0-3.624-.896 39.875 39.875 0 0 0-7.68-.737c-21.162 0-37.345 16.183-37.345 37.345 0 21.159 16.183 37.342 37.345 37.342z"></path></svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

1
public/images/play-audio-icon.svg

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 34 34" width="34" height="34"><path fill="#999999" d="M8.5 8.7c0-1.7 1.2-2.4 2.6-1.5l14.4 8.3c1.4.8 1.4 2.2 0 3l-14.4 8.3c-1.4.8-2.6.2-2.6-1.5V8.7z"></path></svg>

After

Width:  |  Height:  |  Size: 215 B

2
public/images/play.svg

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" id="Outline" viewBox="0 0 24 24" width="20" height="20"><path fill="#919191" d="M20.494,7.968l-9.54-7A5,5,0,0,0,3,5V19a5,5,0,0,0,7.957,4.031l9.54-7a5,5,0,0,0,0-8.064Zm-1.184,6.45-9.54,7A3,3,0,0,1,5,19V5A2.948,2.948,0,0,1,6.641,2.328,3.018,3.018,0,0,1,8.006,2a2.97,2.97,0,0,1,1.764.589l9.54,7a3,3,0,0,1,0,4.836Z"/></svg>

After

Width:  |  Height:  |  Size: 399 B

1
public/images/power.svg

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="#919191" d="M15,3.849h0a1.02,1.02,0,0,0,.629.926A9,9,0,0,1,21,13.292,9,9,0,0,1,3,13,9,9,0,0,1,8.371,4.776,1.023,1.023,0,0,0,9,3.848H9a1,1,0,0,0-1.374-.929,11,11,0,1,0,8.751,0A1,1,0,0,0,15,3.849Z"/><rect x="11" fill="#919191" width="2" height="8" rx="1"/></svg>

After

Width:  |  Height:  |  Size: 357 B

2
public/images/redo.svg

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" id="Outline" viewBox="0 0 24 24" width="16" height="16"><path fill="#FFF" d="M0,23V16A9.01,9.01,0,0,1,9,7h4.83V5.414A2,2,0,0,1,17.244,4l5.88,5.879a3,3,0,0,1,0,4.242L17.244,20a2,2,0,0,1-3.414-1.414V17H8a6.006,6.006,0,0,0-6,6,1,1,0,0,1-2,0ZM15.83,8a1,1,0,0,1-1,1H9a7.008,7.008,0,0,0-7,7v1.714A7.984,7.984,0,0,1,8,15h6.83a1,1,0,0,1,1,1v2.586l5.879-5.879a1,1,0,0,0,0-1.414L15.83,5.414Z"/></svg>

After

Width:  |  Height:  |  Size: 470 B

1
public/images/search-icon.svg

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="#919191" d="M15.9 14.3H15l-.3-.3c1-1.1 1.6-2.7 1.6-4.3 0-3.7-3-6.7-6.7-6.7S3 6 3 9.7s3 6.7 6.7 6.7c1.6 0 3.2-.6 4.3-1.6l.3.3v.8l5.1 5.1 1.5-1.5-5-5.2zm-6.2 0c-2.6 0-4.6-2.1-4.6-4.6s2.1-4.6 4.6-4.6 4.6 2.1 4.6 4.6-2 4.6-4.6 4.6z"></path></svg>

After

Width:  |  Height:  |  Size: 337 B

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save