From ed02947fd18c5e9044c66638180f2dbf369b0c9f Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Tue, 9 Nov 2021 18:00:32 -0400 Subject: [PATCH 001/144] Create .gitignore --- .gitignore | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..576e4c4 --- /dev/null +++ b/.gitignore @@ -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 +/.vscode +/stubs +/public/node_modules +/public/vendor +/banco +/websocket/vendor \ No newline at end of file From 5c34ab7b32db21ad29865a50f9f49a9cdeb9c830 Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Wed, 17 Nov 2021 10:04:48 -0400 Subject: [PATCH 002/144] update all --- Dockerfile | 28 + apache2.conf | 228 ++++ app/Controllers/AgentController.php | 2 + app/Core/Connect.php | 2 +- app/Middleware/ApiAgente.php | 58 + app/Middleware/Middleware.php | 10 +- app/Providers/Positus.php | 11 +- composer.json | 24 +- composer.lock | 1405 +++++++++++++++++++++- config/app.php | 4 +- config/display_errors.php | 2 +- docker-compose.yml | 10 + inicia.sh | 3 + ports.conf | 15 + vendor/composer/ClassLoader.php | 4 +- vendor/composer/InstalledVersions.php | 598 +++++----- vendor/composer/autoload_classmap.php | 4 + vendor/composer/autoload_namespaces.php | 1 + vendor/composer/autoload_psr4.php | 18 + vendor/composer/autoload_real.php | 20 + vendor/composer/autoload_static.php | 133 +++ vendor/composer/installed.json | 1461 ++++++++++++++++++++++- vendor/composer/installed.php | 227 +++- websocket/Servidorsocket.php | 69 ++ websocket/WsInterface.php | 29 + websocket/websocket.php | 20 + 26 files changed, 4067 insertions(+), 319 deletions(-) create mode 100644 Dockerfile create mode 100644 apache2.conf create mode 100644 app/Middleware/ApiAgente.php create mode 100644 docker-compose.yml create mode 100644 inicia.sh create mode 100644 ports.conf create mode 100644 websocket/Servidorsocket.php create mode 100644 websocket/WsInterface.php create mode 100644 websocket/websocket.php diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..e3d1c5c --- /dev/null +++ b/Dockerfile @@ -0,0 +1,28 @@ +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 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"] \ No newline at end of file diff --git a/apache2.conf b/apache2.conf new file mode 100644 index 0000000..cdbbd7c --- /dev/null +++ b/apache2.conf @@ -0,0 +1,228 @@ +# This is the main Apache server configuration file. It contains the +# configuration directives that give the server its instructions. +# See http://httpd.apache.org/docs/2.4/ for detailed information about +# the directives and /usr/share/doc/apache2/README.Debian about Debian specific +# hints. +# +# +# Summary of how the Apache 2 configuration works in Debian: +# The Apache 2 web server configuration in Debian is quite different to +# upstream's suggested way to configure the web server. This is because Debian's +# default Apache2 installation attempts to make adding and removing modules, +# virtual hosts, and extra configuration directives as flexible as possible, in +# order to make automating the changes and administering the server as easy as +# possible. + +# It is split into several files forming the configuration hierarchy outlined +# below, all located in the /etc/apache2/ directory: +# +# /etc/apache2/ +# |-- apache2.conf +# | `-- ports.conf +# |-- mods-enabled +# | |-- *.load +# | `-- *.conf +# |-- conf-enabled +# | `-- *.conf +# `-- sites-enabled +# `-- *.conf +# +# +# * apache2.conf is the main configuration file (this file). It puts the pieces +# together by including all remaining configuration files when starting up the +# web server. +# +# * ports.conf is always included from the main configuration file. It is +# supposed to determine listening ports for incoming connections which can be +# customized anytime. +# +# * Configuration files in the mods-enabled/, conf-enabled/ and sites-enabled/ +# directories contain particular configuration snippets which manage modules, +# global configuration fragments, or virtual host configurations, +# respectively. +# +# They are activated by symlinking available configuration files from their +# respective *-available/ counterparts. These should be managed by using our +# helpers a2enmod/a2dismod, a2ensite/a2dissite and a2enconf/a2disconf. See +# their respective man pages for detailed information. +# +# * The binary is called apache2. Due to the use of environment variables, in +# the default configuration, apache2 needs to be started/stopped with +# /etc/init.d/apache2 or apache2ctl. Calling /usr/bin/apache2 directly will not +# work with the default configuration. + + +# Global configuration +# + +# +# ServerRoot: The top of the directory tree under which the server's +# configuration, error, and log files are kept. +# +# NOTE! If you intend to place this on an NFS (or otherwise network) +# mounted filesystem then please read the Mutex documentation (available +# at ); +# you will save yourself a lot of trouble. +# +# Do NOT add a slash at the end of the directory path. +# +#ServerRoot "/etc/apache2" + +# +# The accept serialization lock file MUST BE STORED ON A LOCAL DISK. +# +#Mutex file:${APACHE_LOCK_DIR} default + +# +# The directory where shm and other runtime files will be stored. +# + +DefaultRuntimeDir ${APACHE_RUN_DIR} + +# +# PidFile: The file in which the server should record its process +# identification number when it starts. +# This needs to be set in /etc/apache2/envvars +# +PidFile ${APACHE_PID_FILE} + +# +# Timeout: The number of seconds before receives and sends time out. +# +Timeout 300 + +# +# KeepAlive: Whether or not to allow persistent connections (more than +# one request per connection). Set to "Off" to deactivate. +# +KeepAlive On + +# +# MaxKeepAliveRequests: The maximum number of requests to allow +# during a persistent connection. Set to 0 to allow an unlimited amount. +# We recommend you leave this number high, for maximum performance. +# +MaxKeepAliveRequests 100 + +# +# KeepAliveTimeout: Number of seconds to wait for the next request from the +# same client on the same connection. +# +KeepAliveTimeout 5 + + +# These need to be set in /etc/apache2/envvars +User ${APACHE_RUN_USER} +Group ${APACHE_RUN_GROUP} + +# +# HostnameLookups: Log the names of clients or just their IP addresses +# e.g., www.apache.org (on) or 204.62.129.132 (off). +# The default is off because it'd be overall better for the net if people +# had to knowingly turn this feature on, since enabling it means that +# each client request will result in AT LEAST one lookup request to the +# nameserver. +# +HostnameLookups Off + +# ErrorLog: The location of the error log file. +# If you do not specify an ErrorLog directive within a +# container, error messages relating to that virtual host will be +# logged here. If you *do* define an error logfile for a +# container, that host's errors will be logged there and not here. +# +ErrorLog ${APACHE_LOG_DIR}/error.log + +# +# LogLevel: Control the severity of messages logged to the error_log. +# Available values: trace8, ..., trace1, debug, info, notice, warn, +# error, crit, alert, emerg. +# It is also possible to configure the log level for particular modules, e.g. +# "LogLevel info ssl:warn" +# +LogLevel warn + +# Include module configuration: +IncludeOptional mods-enabled/*.load +IncludeOptional mods-enabled/*.conf + +# Include list of ports to listen on +Include ports.conf + + +# Sets the default security model of the Apache2 HTTPD server. It does +# not allow access to the root filesystem outside of /usr/share and /var/www. +# The former is used by web applications packaged in Debian, +# the latter may be used for local directories served by the web server. If +# your system is serving content from a sub-directory in /srv you must allow +# access here, or in any related virtual host. + + Options FollowSymLinks + AllowOverride None + Require all denied + + + + AllowOverride None + Require all granted + + + + Options Indexes FollowSymLinks + AllowOverride None + Require all granted + + +# +# Options Indexes FollowSymLinks +# AllowOverride None +# Require all granted +# + + + + +# AccessFileName: The name of the file to look for in each directory +# for additional configuration directives. See also the AllowOverride +# directive. +# +AccessFileName .htaccess + +# +# The following lines prevent .htaccess and .htpasswd files from being +# viewed by Web clients. +# + + Require all denied + + + +# +# The following directives define some format nicknames for use with +# a CustomLog directive. +# +# These deviate from the Common Log Format definitions in that they use %O +# (the actual bytes sent including headers) instead of %b (the size of the +# requested file), because the latter makes it impossible to detect partial +# requests. +# +# Note that the use of %{X-Forwarded-For}i instead of %h is not recommended. +# Use mod_remoteip instead. +# +LogFormat "%v:%p %h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" vhost_combined +LogFormat "%h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" combined +LogFormat "%h %l %u %t \"%r\" %>s %O" common +LogFormat "%{Referer}i -> %U" referer +LogFormat "%{User-agent}i" agent + +# Include of directories ignores editors' and dpkg's backup files, +# see README.Debian for details. + +# Include generic snippets of statements +IncludeOptional conf-enabled/*.conf + +# Include the virtual host configurations: +IncludeOptional sites-enabled/*.conf + +# vim: syntax=apache ts=4 sw=4 sts=4 sr noet +ServerName 192.168.115.65 diff --git a/app/Controllers/AgentController.php b/app/Controllers/AgentController.php index f6d6148..7dc7654 100644 --- a/app/Controllers/AgentController.php +++ b/app/Controllers/AgentController.php @@ -111,6 +111,7 @@ class AgentController extends Controller } } catch (Exception $ex) { logger()->error($ex->getMessage()); + return $ex->getMessage(); } return false; } @@ -161,6 +162,7 @@ class AgentController extends Controller $this->agent->rollback(); $this->message($ex->getMessage()); logger()->error($ex->getMessage()); + return $ex->getMessage(); } return false; } diff --git a/app/Core/Connect.php b/app/Core/Connect.php index 8a10e3d..1e71a80 100644 --- a/app/Core/Connect.php +++ b/app/Core/Connect.php @@ -41,7 +41,7 @@ class Connect self::$instance = new PDO( CONF_DB_DRIVER . - ":host=" . $credentials['host_db'] . + ":host=" . CONF_DB_HOST . ";port=" . $credentials['porta_db'] . ";dbname=" . $credentials['base_db'], $credentials['usuario'], diff --git a/app/Middleware/ApiAgente.php b/app/Middleware/ApiAgente.php new file mode 100644 index 0000000..43140d3 --- /dev/null +++ b/app/Middleware/ApiAgente.php @@ -0,0 +1,58 @@ +agentController = new AgentController(); + } + + function router($rota, $request) + { + switch ($rota) { + case 'login': + $this->login($request); + break; + case 'listaFilas': + $this->listaFilas($request); + break; + default: + echo json_encode(['status' => '404']); + break; + } + } + + function login($request) + { + $ret = $this->agentController->login( + $request['ramal'], + $request['login'], + $request['fila'], + 'WHATSAPP' + ); + echo json_encode(['status' => $ret]); + return $ret; + } + + public function listaFilas() + { + $queue = new QueueController(); + $search = $queue->listaAllFilas(); + $lista = []; + foreach ($search as $key => $value) { + array_push($lista, [ + 'title' => strtolower($value->nome), + ]); + } + echo json_encode($lista); + return null; + } +} \ No newline at end of file diff --git a/app/Middleware/Middleware.php b/app/Middleware/Middleware.php index aae4152..0ee199e 100644 --- a/app/Middleware/Middleware.php +++ b/app/Middleware/Middleware.php @@ -5,10 +5,12 @@ 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 websocket\WsInterface; /** * Description of WppController @@ -50,27 +52,29 @@ class Middleware extends Http private function router() { $coremedia = new CoreMedia(); + $apiAgente = new ApiAgente(); $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': + $ws = new WsInterface(); + $ws->enviaMsg(json_encode($this->request)); $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]); return null; + case 'api': + $apiAgente->router($this->param()[2], $this->request); default: $this->header->redirect(); } diff --git a/app/Providers/Positus.php b/app/Providers/Positus.php index cbca212..15de5a7 100644 --- a/app/Providers/Positus.php +++ b/app/Providers/Positus.php @@ -496,10 +496,15 @@ class Positus implements IApiMedia function response($result) { if ($result) { - if (json_decode($result, true) !== null) { - return json_decode($result, true); + try { + if (json_decode($result, true) !== null) { + return json_decode($result, true); + } + return $result; + } catch (\Throwable $th) { + logger('deburguer')->info(print_r($result, true)); + return false; } - return $result; } else { return false; } diff --git a/composer.json b/composer.json index a33f16d..ece3a7e 100644 --- a/composer.json +++ b/composer.json @@ -1,17 +1,19 @@ { "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/" } - }, - "require": {} -} + }, + "require": { + "cboden/ratchet": "^0.4", + "textalk/websocket": "^1.5" + } +} \ No newline at end of file diff --git a/composer.lock b/composer.lock index 59ee7fe..bc2cd74 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,1407 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "824e80b41f5059974728f7d2650347b2", - "packages": [], + "content-hash": "55a870c2fc1078fca8d21d42928df082", + "packages": [ + { + "name": "cboden/ratchet", + "version": "v0.4.3", + "source": { + "type": "git", + "url": "https://github.com/ratchetphp/Ratchet.git", + "reference": "466a0ecc83209c75b76645eb823401b5c52e5f21" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ratchetphp/Ratchet/zipball/466a0ecc83209c75b76645eb823401b5c52e5f21", + "reference": "466a0ecc83209c75b76645eb823401b5c52e5f21", + "shasum": "" + }, + "require": { + "guzzlehttp/psr7": "^1.0", + "php": ">=5.4.2", + "ratchet/rfc6455": "^0.3", + "react/socket": "^1.0 || ^0.8 || ^0.7 || ^0.6 || ^0.5", + "symfony/http-foundation": "^2.6|^3.0|^4.0|^5.0", + "symfony/routing": "^2.6|^3.0|^4.0|^5.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.8" + }, + "type": "library", + "autoload": { + "psr-4": { + "Ratchet\\": "src/Ratchet" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "role": "Developer" + }, + { + "name": "Matt Bonneau", + "role": "Developer" + } + ], + "description": "PHP WebSocket library", + "homepage": "http://socketo.me", + "keywords": [ + "Ratchet", + "WebSockets", + "server", + "sockets", + "websocket" + ], + "support": { + "chat": "https://gitter.im/reactphp/reactphp", + "issues": "https://github.com/ratchetphp/Ratchet/issues", + "source": "https://github.com/ratchetphp/Ratchet/tree/master" + }, + "time": "2020-07-07T15:50:14+00:00" + }, + { + "name": "evenement/evenement", + "version": "v3.0.1", + "source": { + "type": "git", + "url": "https://github.com/igorw/evenement.git", + "reference": "531bfb9d15f8aa57454f5f0285b18bec903b8fb7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/igorw/evenement/zipball/531bfb9d15f8aa57454f5f0285b18bec903b8fb7", + "reference": "531bfb9d15f8aa57454f5f0285b18bec903b8fb7", + "shasum": "" + }, + "require": { + "php": ">=7.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0" + }, + "type": "library", + "autoload": { + "psr-0": { + "Evenement": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Igor Wiedler", + "email": "igor@wiedler.ch" + } + ], + "description": "Événement is a very simple event dispatching library for PHP", + "keywords": [ + "event-dispatcher", + "event-emitter" + ], + "support": { + "issues": "https://github.com/igorw/evenement/issues", + "source": "https://github.com/igorw/evenement/tree/master" + }, + "time": "2017-07-23T21:35:13+00:00" + }, + { + "name": "guzzlehttp/psr7", + "version": "1.8.3", + "source": { + "type": "git", + "url": "https://github.com/guzzle/psr7.git", + "reference": "1afdd860a2566ed3c2b0b4a3de6e23434a79ec85" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/1afdd860a2566ed3c2b0b4a3de6e23434a79ec85", + "reference": "1afdd860a2566ed3c2b0b4a3de6e23434a79ec85", + "shasum": "" + }, + "require": { + "php": ">=5.4.0", + "psr/http-message": "~1.0", + "ralouphie/getallheaders": "^2.0.5 || ^3.0.0" + }, + "provide": { + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "ext-zlib": "*", + "phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.8 || ^9.3.10" + }, + "suggest": { + "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.7-dev" + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Psr7\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "PSR-7 message implementation that also provides common utility methods", + "keywords": [ + "http", + "message", + "psr-7", + "request", + "response", + "stream", + "uri", + "url" + ], + "support": { + "issues": "https://github.com/guzzle/psr7/issues", + "source": "https://github.com/guzzle/psr7/tree/1.8.3" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", + "type": "tidelift" + } + ], + "time": "2021-10-05T13:56:00+00:00" + }, + { + "name": "psr/http-message", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-message/tree/master" + }, + "time": "2016-08-06T14:39:51+00:00" + }, + { + "name": "psr/log", + "version": "1.1.4", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "d49695b909c3b7628b6289db5479a1c204601f11" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", + "reference": "d49695b909c3b7628b6289db5479a1c204601f11", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "Psr/Log/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/1.1.4" + }, + "time": "2021-05-03T11:20:27+00:00" + }, + { + "name": "ralouphie/getallheaders", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/ralouphie/getallheaders.git", + "reference": "120b605dfeb996808c31b6477290a714d356e822" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", + "reference": "120b605dfeb996808c31b6477290a714d356e822", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^5 || ^6.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/getallheaders.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" + } + ], + "description": "A polyfill for getallheaders.", + "support": { + "issues": "https://github.com/ralouphie/getallheaders/issues", + "source": "https://github.com/ralouphie/getallheaders/tree/develop" + }, + "time": "2019-03-08T08:55:37+00:00" + }, + { + "name": "ratchet/rfc6455", + "version": "v0.3", + "source": { + "type": "git", + "url": "https://github.com/ratchetphp/RFC6455.git", + "reference": "c8651c7938651c2d55f5d8c2422ac5e57a183341" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ratchetphp/RFC6455/zipball/c8651c7938651c2d55f5d8c2422ac5e57a183341", + "reference": "c8651c7938651c2d55f5d8c2422ac5e57a183341", + "shasum": "" + }, + "require": { + "guzzlehttp/psr7": "^1.0", + "php": ">=5.4.2" + }, + "require-dev": { + "phpunit/phpunit": "5.7.*", + "react/socket": "^1.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Ratchet\\RFC6455\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "role": "Developer" + }, + { + "name": "Matt Bonneau", + "role": "Developer" + } + ], + "description": "RFC6455 WebSocket protocol handler", + "homepage": "http://socketo.me", + "keywords": [ + "WebSockets", + "rfc6455", + "websocket" + ], + "support": { + "chat": "https://gitter.im/reactphp/reactphp", + "issues": "https://github.com/ratchetphp/RFC6455/issues", + "source": "https://github.com/ratchetphp/RFC6455/tree/v0.3" + }, + "time": "2020-05-15T18:31:24+00:00" + }, + { + "name": "react/cache", + "version": "v1.1.1", + "source": { + "type": "git", + "url": "https://github.com/reactphp/cache.git", + "reference": "4bf736a2cccec7298bdf745db77585966fc2ca7e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/cache/zipball/4bf736a2cccec7298bdf745db77585966fc2ca7e", + "reference": "4bf736a2cccec7298bdf745db77585966fc2ca7e", + "shasum": "" + }, + "require": { + "php": ">=5.3.0", + "react/promise": "^3.0 || ^2.0 || ^1.1" + }, + "require-dev": { + "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\Cache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "Async, Promise-based cache interface for ReactPHP", + "keywords": [ + "cache", + "caching", + "promise", + "reactphp" + ], + "support": { + "issues": "https://github.com/reactphp/cache/issues", + "source": "https://github.com/reactphp/cache/tree/v1.1.1" + }, + "funding": [ + { + "url": "https://github.com/WyriHaximus", + "type": "github" + }, + { + "url": "https://github.com/clue", + "type": "github" + } + ], + "time": "2021-02-02T06:47:52+00:00" + }, + { + "name": "react/dns", + "version": "v1.8.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/dns.git", + "reference": "2a5a74ab751e53863b45fb87e1d3913884f88248" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/dns/zipball/2a5a74ab751e53863b45fb87e1d3913884f88248", + "reference": "2a5a74ab751e53863b45fb87e1d3913884f88248", + "shasum": "" + }, + "require": { + "php": ">=5.3.0", + "react/cache": "^1.0 || ^0.6 || ^0.5", + "react/event-loop": "^1.2", + "react/promise": "^3.0 || ^2.7 || ^1.2.1", + "react/promise-timer": "^1.2" + }, + "require-dev": { + "clue/block-react": "^1.2", + "phpunit/phpunit": "^9.3 || ^4.8.35" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\Dns\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "Async DNS resolver for ReactPHP", + "keywords": [ + "async", + "dns", + "dns-resolver", + "reactphp" + ], + "support": { + "issues": "https://github.com/reactphp/dns/issues", + "source": "https://github.com/reactphp/dns/tree/v1.8.0" + }, + "funding": [ + { + "url": "https://github.com/WyriHaximus", + "type": "github" + }, + { + "url": "https://github.com/clue", + "type": "github" + } + ], + "time": "2021-07-11T12:40:34+00:00" + }, + { + "name": "react/event-loop", + "version": "v1.2.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/event-loop.git", + "reference": "be6dee480fc4692cec0504e65eb486e3be1aa6f2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/event-loop/zipball/be6dee480fc4692cec0504e65eb486e3be1aa6f2", + "reference": "be6dee480fc4692cec0504e65eb486e3be1aa6f2", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35" + }, + "suggest": { + "ext-event": "~1.0 for ExtEventLoop", + "ext-pcntl": "For signal handling support when using the StreamSelectLoop", + "ext-uv": "* for ExtUvLoop" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\EventLoop\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "ReactPHP's core reactor event loop that libraries can use for evented I/O.", + "keywords": [ + "asynchronous", + "event-loop" + ], + "support": { + "issues": "https://github.com/reactphp/event-loop/issues", + "source": "https://github.com/reactphp/event-loop/tree/v1.2.0" + }, + "funding": [ + { + "url": "https://github.com/WyriHaximus", + "type": "github" + }, + { + "url": "https://github.com/clue", + "type": "github" + } + ], + "time": "2021-07-11T12:31:24+00:00" + }, + { + "name": "react/promise", + "version": "v2.8.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/promise.git", + "reference": "f3cff96a19736714524ca0dd1d4130de73dbbbc4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/promise/zipball/f3cff96a19736714524ca0dd1d4130de73dbbbc4", + "reference": "f3cff96a19736714524ca0dd1d4130de73dbbbc4", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "^7.0 || ^6.5 || ^5.7 || ^4.8.36" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\Promise\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com" + } + ], + "description": "A lightweight implementation of CommonJS Promises/A for PHP", + "keywords": [ + "promise", + "promises" + ], + "support": { + "issues": "https://github.com/reactphp/promise/issues", + "source": "https://github.com/reactphp/promise/tree/v2.8.0" + }, + "time": "2020-05-12T15:16:56+00:00" + }, + { + "name": "react/promise-timer", + "version": "v1.7.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/promise-timer.git", + "reference": "607dd79990e32fcb402cb0a176b4a4be12f97e7c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/promise-timer/zipball/607dd79990e32fcb402cb0a176b4a4be12f97e7c", + "reference": "607dd79990e32fcb402cb0a176b4a4be12f97e7c", + "shasum": "" + }, + "require": { + "php": ">=5.3", + "react/event-loop": "^1.2", + "react/promise": "^3.0 || ^2.7.0 || ^1.2.1" + }, + "require-dev": { + "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\Promise\\Timer\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "A trivial implementation of timeouts for Promises, built on top of ReactPHP.", + "homepage": "https://github.com/reactphp/promise-timer", + "keywords": [ + "async", + "event-loop", + "promise", + "reactphp", + "timeout", + "timer" + ], + "support": { + "issues": "https://github.com/reactphp/promise-timer/issues", + "source": "https://github.com/reactphp/promise-timer/tree/v1.7.0" + }, + "funding": [ + { + "url": "https://github.com/WyriHaximus", + "type": "github" + }, + { + "url": "https://github.com/clue", + "type": "github" + } + ], + "time": "2021-07-11T13:08:51+00:00" + }, + { + "name": "react/socket", + "version": "v1.9.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/socket.git", + "reference": "aa6e3f8ebcd6dec3ad1ee92a449b4cc341994001" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/socket/zipball/aa6e3f8ebcd6dec3ad1ee92a449b4cc341994001", + "reference": "aa6e3f8ebcd6dec3ad1ee92a449b4cc341994001", + "shasum": "" + }, + "require": { + "evenement/evenement": "^3.0 || ^2.0 || ^1.0", + "php": ">=5.3.0", + "react/dns": "^1.8", + "react/event-loop": "^1.2", + "react/promise": "^2.6.0 || ^1.2.1", + "react/promise-timer": "^1.4.0", + "react/stream": "^1.2" + }, + "require-dev": { + "clue/block-react": "^1.2", + "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35", + "react/promise-stream": "^1.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\Socket\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "Async, streaming plaintext TCP/IP and secure TLS socket server and client connections for ReactPHP", + "keywords": [ + "Connection", + "Socket", + "async", + "reactphp", + "stream" + ], + "support": { + "issues": "https://github.com/reactphp/socket/issues", + "source": "https://github.com/reactphp/socket/tree/v1.9.0" + }, + "funding": [ + { + "url": "https://github.com/WyriHaximus", + "type": "github" + }, + { + "url": "https://github.com/clue", + "type": "github" + } + ], + "time": "2021-08-03T12:37:06+00:00" + }, + { + "name": "react/stream", + "version": "v1.2.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/stream.git", + "reference": "7a423506ee1903e89f1e08ec5f0ed430ff784ae9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/stream/zipball/7a423506ee1903e89f1e08ec5f0ed430ff784ae9", + "reference": "7a423506ee1903e89f1e08ec5f0ed430ff784ae9", + "shasum": "" + }, + "require": { + "evenement/evenement": "^3.0 || ^2.0 || ^1.0", + "php": ">=5.3.8", + "react/event-loop": "^1.2" + }, + "require-dev": { + "clue/stream-filter": "~1.2", + "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\Stream\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "Event-driven readable and writable streams for non-blocking I/O in ReactPHP", + "keywords": [ + "event-driven", + "io", + "non-blocking", + "pipe", + "reactphp", + "readable", + "stream", + "writable" + ], + "support": { + "issues": "https://github.com/reactphp/stream/issues", + "source": "https://github.com/reactphp/stream/tree/v1.2.0" + }, + "funding": [ + { + "url": "https://github.com/WyriHaximus", + "type": "github" + }, + { + "url": "https://github.com/clue", + "type": "github" + } + ], + "time": "2021-07-11T12:37:55+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v2.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "5f38c8804a9e97d23e0c8d63341088cd8a22d627" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/5f38c8804a9e97d23e0c8d63341088cd8a22d627", + "reference": "5f38c8804a9e97d23e0c8d63341088cd8a22d627", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.4-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v2.4.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-03-23T23:28:01+00:00" + }, + { + "name": "symfony/http-foundation", + "version": "v5.3.10", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-foundation.git", + "reference": "9f34f02e8a5fdc7a56bafe011cea1ce97300e54c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/9f34f02e8a5fdc7a56bafe011cea1ce97300e54c", + "reference": "9f34f02e8a5fdc7a56bafe011cea1ce97300e54c", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1", + "symfony/polyfill-mbstring": "~1.1", + "symfony/polyfill-php80": "^1.16" + }, + "require-dev": { + "predis/predis": "~1.0", + "symfony/cache": "^4.4|^5.0", + "symfony/expression-language": "^4.4|^5.0", + "symfony/mime": "^4.4|^5.0" + }, + "suggest": { + "symfony/mime": "To use the file extension guesser" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpFoundation\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Defines an object-oriented layer for the HTTP specification", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/http-foundation/tree/v5.3.10" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-10-11T15:41:55+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.23.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "9174a3d80210dca8daa7f31fec659150bbeabfc6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9174a3d80210dca8daa7f31fec659150bbeabfc6", + "reference": "9174a3d80210dca8daa7f31fec659150bbeabfc6", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.23-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.23.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-05-27T12:26:48+00:00" + }, + { + "name": "symfony/polyfill-php80", + "version": "v1.23.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "1100343ed1a92e3a38f9ae122fc0eb21602547be" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/1100343ed1a92e3a38f9ae122fc0eb21602547be", + "reference": "1100343ed1a92e3a38f9ae122fc0eb21602547be", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.23-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, + "files": [ + "bootstrap.php" + ], + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.23.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-07-28T13:41:28+00:00" + }, + { + "name": "symfony/routing", + "version": "v5.3.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/routing.git", + "reference": "be865017746fe869007d94220ad3f5297951811b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/routing/zipball/be865017746fe869007d94220ad3f5297951811b", + "reference": "be865017746fe869007d94220ad3f5297951811b", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1", + "symfony/polyfill-php80": "^1.16" + }, + "conflict": { + "doctrine/annotations": "<1.12", + "symfony/config": "<5.3", + "symfony/dependency-injection": "<4.4", + "symfony/yaml": "<4.4" + }, + "require-dev": { + "doctrine/annotations": "^1.12", + "psr/log": "^1|^2|^3", + "symfony/config": "^5.3", + "symfony/dependency-injection": "^4.4|^5.0", + "symfony/expression-language": "^4.4|^5.0", + "symfony/http-foundation": "^4.4|^5.0", + "symfony/yaml": "^4.4|^5.0" + }, + "suggest": { + "symfony/config": "For using the all-in-one router or any loader", + "symfony/expression-language": "For using expression matching", + "symfony/http-foundation": "For using a Symfony Request object", + "symfony/yaml": "For using the YAML loader" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Routing\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Maps an HTTP request to a set of configuration variables", + "homepage": "https://symfony.com", + "keywords": [ + "router", + "routing", + "uri", + "url" + ], + "support": { + "source": "https://github.com/symfony/routing/tree/v5.3.7" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-08-04T21:42:42+00:00" + }, + { + "name": "textalk/websocket", + "version": "1.5.5", + "source": { + "type": "git", + "url": "https://github.com/Textalk/websocket-php.git", + "reference": "846542f82658132cd36acb7a7e8ce0f03960c295" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Textalk/websocket-php/zipball/846542f82658132cd36acb7a7e8ce0f03960c295", + "reference": "846542f82658132cd36acb7a7e8ce0f03960c295", + "shasum": "" + }, + "require": { + "php": "^7.2 | ^8.0", + "psr/log": "^1 | ^2 | ^3" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.0", + "phpunit/phpunit": "^8.0|^9.0", + "squizlabs/php_codesniffer": "^3.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "WebSocket\\": "lib" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "ISC" + ], + "authors": [ + { + "name": "Fredrik Liljegren" + }, + { + "name": "Sören Jensen", + "email": "soren@abicart.se" + } + ], + "description": "WebSocket client and server", + "support": { + "issues": "https://github.com/Textalk/websocket-php/issues", + "source": "https://github.com/Textalk/websocket-php/tree/1.5.5" + }, + "time": "2021-08-07T10:21:40+00:00" + } + ], "packages-dev": [], "aliases": [], "minimum-stability": "stable", @@ -14,5 +1413,5 @@ "prefer-lowest": false, "platform": [], "platform-dev": [], - "plugin-api-version": "2.0.0" + "plugin-api-version": "2.1.0" } diff --git a/config/app.php b/config/app.php index e6b7884..8280087 100644 --- a/config/app.php +++ b/config/app.php @@ -18,7 +18,7 @@ define("CONF_NAME_REPONSE", 'Simples IP'); | Local para armazenar os dados referente a API utilzada. | */ -define("CONF_LOG_PATH", '/var/www/html/aplicativo/integracao/media/storage/log/'); +define("CONF_LOG_PATH", '/var/www/html/storage/log/'); /* @@ -42,7 +42,7 @@ 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', ''); diff --git a/config/display_errors.php b/config/display_errors.php index 8e524ca..694e60d 100644 --- a/config/display_errors.php +++ b/config/display_errors.php @@ -1,6 +1,6 @@ +Listen 443 + + + +Listen 443 + + +# vim: syntax=apache ts=4 sw=4 sts=4 sr noet diff --git a/vendor/composer/ClassLoader.php b/vendor/composer/ClassLoader.php index 247294d..6d0c3f2 100644 --- a/vendor/composer/ClassLoader.php +++ b/vendor/composer/ClassLoader.php @@ -338,7 +338,7 @@ class ClassLoader * Loads the given class or interface. * * @param string $class The name of the class - * @return bool|null True if loaded, null otherwise + * @return true|null True if loaded, null otherwise */ public function loadClass($class) { @@ -347,6 +347,8 @@ class ClassLoader return true; } + + return null; } /** diff --git a/vendor/composer/InstalledVersions.php b/vendor/composer/InstalledVersions.php index 7a488c4..b3a4e16 100644 --- a/vendor/composer/InstalledVersions.php +++ b/vendor/composer/InstalledVersions.php @@ -1,283 +1,337 @@ + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ namespace Composer; use Composer\Autoload\ClassLoader; use Composer\Semver\VersionParser; - - - - - +/** + * This class is copied in every Composer installed project and available to all + * + * See also https://getcomposer.org/doc/07-runtime.md#installed-versions + * + * To require it's presence, you can require `composer-runtime-api ^2.0` + */ class InstalledVersions { -private static $installed = array ( - 'root' => - array ( - 'pretty_version' => '1.0.0+no-version-set', - 'version' => '1.0.0.0', - 'aliases' => - array ( - ), - 'reference' => NULL, - 'name' => 'simplesip/whatsapp', - ), - 'versions' => - array ( - 'simplesip/whatsapp' => - array ( - 'pretty_version' => '1.0.0+no-version-set', - 'version' => '1.0.0.0', - 'aliases' => - array ( - ), - 'reference' => NULL, - ), - ), -); -private static $canGetVendors; -private static $installedByVendor = array(); - - - - - - - -public static function getInstalledPackages() -{ -$packages = array(); -foreach (self::getInstalled() as $installed) { -$packages[] = array_keys($installed['versions']); -} - - -if (1 === \count($packages)) { -return $packages[0]; -} - -return array_keys(array_flip(\call_user_func_array('array_merge', $packages))); -} - - - - - - - - - -public static function isInstalled($packageName) -{ -foreach (self::getInstalled() as $installed) { -if (isset($installed['versions'][$packageName])) { -return true; -} -} - -return false; -} - - - - - - - - - - - - - - -public static function satisfies(VersionParser $parser, $packageName, $constraint) -{ -$constraint = $parser->parseConstraints($constraint); -$provided = $parser->parseConstraints(self::getVersionRanges($packageName)); - -return $provided->matches($constraint); -} - - - - - - - - - - -public static function getVersionRanges($packageName) -{ -foreach (self::getInstalled() as $installed) { -if (!isset($installed['versions'][$packageName])) { -continue; -} - -$ranges = array(); -if (isset($installed['versions'][$packageName]['pretty_version'])) { -$ranges[] = $installed['versions'][$packageName]['pretty_version']; -} -if (array_key_exists('aliases', $installed['versions'][$packageName])) { -$ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']); -} -if (array_key_exists('replaced', $installed['versions'][$packageName])) { -$ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']); -} -if (array_key_exists('provided', $installed['versions'][$packageName])) { -$ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']); -} - -return implode(' || ', $ranges); -} - -throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); -} - - - - - -public static function getVersion($packageName) -{ -foreach (self::getInstalled() as $installed) { -if (!isset($installed['versions'][$packageName])) { -continue; -} - -if (!isset($installed['versions'][$packageName]['version'])) { -return null; -} - -return $installed['versions'][$packageName]['version']; -} - -throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); -} - - - - - -public static function getPrettyVersion($packageName) -{ -foreach (self::getInstalled() as $installed) { -if (!isset($installed['versions'][$packageName])) { -continue; -} - -if (!isset($installed['versions'][$packageName]['pretty_version'])) { -return null; -} - -return $installed['versions'][$packageName]['pretty_version']; -} - -throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); -} - - - - - -public static function getReference($packageName) -{ -foreach (self::getInstalled() as $installed) { -if (!isset($installed['versions'][$packageName])) { -continue; -} - -if (!isset($installed['versions'][$packageName]['reference'])) { -return null; -} - -return $installed['versions'][$packageName]['reference']; -} - -throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); -} - - - - - -public static function getRootPackage() -{ -$installed = self::getInstalled(); - -return $installed[0]['root']; -} - - - - - - - -public static function getRawData() -{ -return self::$installed; -} - - - - - - - - - - - - - - - - - - - -public static function reload($data) -{ -self::$installed = $data; -self::$installedByVendor = array(); -} - - - - -private static function getInstalled() -{ -if (null === self::$canGetVendors) { -self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders'); -} - -$installed = array(); - -if (self::$canGetVendors) { -foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) { -if (isset(self::$installedByVendor[$vendorDir])) { -$installed[] = self::$installedByVendor[$vendorDir]; -} elseif (is_file($vendorDir.'/composer/installed.php')) { -$installed[] = self::$installedByVendor[$vendorDir] = require $vendorDir.'/composer/installed.php'; -} -} -} - -$installed[] = self::$installed; - -return $installed; -} + private static $installed; + private static $canGetVendors; + private static $installedByVendor = array(); + + /** + * Returns a list of all package names which are present, either by being installed, replaced or provided + * + * @return string[] + * @psalm-return list + */ + public static function getInstalledPackages() + { + $packages = array(); + foreach (self::getInstalled() as $installed) { + $packages[] = array_keys($installed['versions']); + } + + if (1 === \count($packages)) { + return $packages[0]; + } + + return array_keys(array_flip(\call_user_func_array('array_merge', $packages))); + } + + /** + * Returns a list of all package names with a specific type e.g. 'library' + * + * @param string $type + * @return string[] + * @psalm-return list + */ + public static function getInstalledPackagesByType($type) + { + $packagesByType = array(); + + foreach (self::getInstalled() as $installed) { + foreach ($installed['versions'] as $name => $package) { + if (isset($package['type']) && $package['type'] === $type) { + $packagesByType[] = $name; + } + } + } + + return $packagesByType; + } + + /** + * Checks whether the given package is installed + * + * This also returns true if the package name is provided or replaced by another package + * + * @param string $packageName + * @param bool $includeDevRequirements + * @return bool + */ + public static function isInstalled($packageName, $includeDevRequirements = true) + { + foreach (self::getInstalled() as $installed) { + if (isset($installed['versions'][$packageName])) { + return $includeDevRequirements || empty($installed['versions'][$packageName]['dev_requirement']); + } + } + + return false; + } + + /** + * Checks whether the given package satisfies a version constraint + * + * e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call: + * + * Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3') + * + * @param VersionParser $parser Install composer/semver to have access to this class and functionality + * @param string $packageName + * @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package + * @return bool + */ + public static function satisfies(VersionParser $parser, $packageName, $constraint) + { + $constraint = $parser->parseConstraints($constraint); + $provided = $parser->parseConstraints(self::getVersionRanges($packageName)); + + return $provided->matches($constraint); + } + + /** + * Returns a version constraint representing all the range(s) which are installed for a given package + * + * It is easier to use this via isInstalled() with the $constraint argument if you need to check + * whether a given version of a package is installed, and not just whether it exists + * + * @param string $packageName + * @return string Version constraint usable with composer/semver + */ + public static function getVersionRanges($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + $ranges = array(); + if (isset($installed['versions'][$packageName]['pretty_version'])) { + $ranges[] = $installed['versions'][$packageName]['pretty_version']; + } + if (array_key_exists('aliases', $installed['versions'][$packageName])) { + $ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']); + } + if (array_key_exists('replaced', $installed['versions'][$packageName])) { + $ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']); + } + if (array_key_exists('provided', $installed['versions'][$packageName])) { + $ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']); + } + + return implode(' || ', $ranges); + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present + */ + public static function getVersion($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + if (!isset($installed['versions'][$packageName]['version'])) { + return null; + } + + return $installed['versions'][$packageName]['version']; + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present + */ + public static function getPrettyVersion($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + if (!isset($installed['versions'][$packageName]['pretty_version'])) { + return null; + } + + return $installed['versions'][$packageName]['pretty_version']; + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference + */ + public static function getReference($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + if (!isset($installed['versions'][$packageName]['reference'])) { + return null; + } + + return $installed['versions'][$packageName]['reference']; + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path. + */ + public static function getInstallPath($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null; + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @return array + * @psalm-return array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string} + */ + public static function getRootPackage() + { + $installed = self::getInstalled(); + + return $installed[0]['root']; + } + + /** + * Returns the raw installed.php data for custom implementations + * + * @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect. + * @return array[] + * @psalm-return array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string}, versions: array} + */ + public static function getRawData() + { + @trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED); + + if (null === self::$installed) { + // only require the installed.php file if this file is loaded from its dumped location, + // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937 + if (substr(__DIR__, -8, 1) !== 'C') { + self::$installed = include __DIR__ . '/installed.php'; + } else { + self::$installed = array(); + } + } + + return self::$installed; + } + + /** + * Returns the raw data of all installed.php which are currently loaded for custom implementations + * + * @return array[] + * @psalm-return list}> + */ + public static function getAllRawData() + { + return self::getInstalled(); + } + + /** + * Lets you reload the static array from another file + * + * This is only useful for complex integrations in which a project needs to use + * this class but then also needs to execute another project's autoloader in process, + * and wants to ensure both projects have access to their version of installed.php. + * + * A typical case would be PHPUnit, where it would need to make sure it reads all + * the data it needs from this class, then call reload() with + * `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure + * the project in which it runs can then also use this class safely, without + * interference between PHPUnit's dependencies and the project's dependencies. + * + * @param array[] $data A vendor/composer/installed.php data set + * @return void + * + * @psalm-param array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string}, versions: array} $data + */ + public static function reload($data) + { + self::$installed = $data; + self::$installedByVendor = array(); + } + + /** + * @return array[] + * @psalm-return list}> + */ + private static function getInstalled() + { + if (null === self::$canGetVendors) { + self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders'); + } + + $installed = array(); + + if (self::$canGetVendors) { + foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) { + if (isset(self::$installedByVendor[$vendorDir])) { + $installed[] = self::$installedByVendor[$vendorDir]; + } elseif (is_file($vendorDir.'/composer/installed.php')) { + $installed[] = self::$installedByVendor[$vendorDir] = require $vendorDir.'/composer/installed.php'; + if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) { + self::$installed = $installed[count($installed) - 1]; + } + } + } + } + + if (null === self::$installed) { + // only require the installed.php file if this file is loaded from its dumped location, + // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937 + if (substr(__DIR__, -8, 1) !== 'C') { + self::$installed = require __DIR__ . '/installed.php'; + } else { + self::$installed = array(); + } + } + $installed[] = self::$installed; + + return $installed; + } } diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php index b26f1b1..4ebd53f 100644 --- a/vendor/composer/autoload_classmap.php +++ b/vendor/composer/autoload_classmap.php @@ -6,5 +6,9 @@ $vendorDir = dirname(dirname(__FILE__)); $baseDir = dirname($vendorDir); return array( + 'Attribute' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Attribute.php', 'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php', + 'Stringable' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Stringable.php', + 'UnhandledMatchError' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php', + 'ValueError' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/ValueError.php', ); diff --git a/vendor/composer/autoload_namespaces.php b/vendor/composer/autoload_namespaces.php index b7fc012..02066fb 100644 --- a/vendor/composer/autoload_namespaces.php +++ b/vendor/composer/autoload_namespaces.php @@ -6,4 +6,5 @@ $vendorDir = dirname(dirname(__FILE__)); $baseDir = dirname($vendorDir); return array( + 'Evenement' => array($vendorDir . '/evenement/evenement/src'), ); diff --git a/vendor/composer/autoload_psr4.php b/vendor/composer/autoload_psr4.php index b7c5fc7..7eac0ee 100644 --- a/vendor/composer/autoload_psr4.php +++ b/vendor/composer/autoload_psr4.php @@ -6,6 +6,24 @@ $vendorDir = dirname(dirname(__FILE__)); $baseDir = dirname($vendorDir); return array( + 'websocket\\' => array($baseDir . '/websocket'), 'scripts\\' => array($baseDir . '/scripts'), 'app\\' => array($baseDir . '/app'), + 'WebSocket\\' => array($vendorDir . '/textalk/websocket/lib'), + 'Symfony\\Polyfill\\Php80\\' => array($vendorDir . '/symfony/polyfill-php80'), + 'Symfony\\Polyfill\\Mbstring\\' => array($vendorDir . '/symfony/polyfill-mbstring'), + 'Symfony\\Component\\Routing\\' => array($vendorDir . '/symfony/routing'), + 'Symfony\\Component\\HttpFoundation\\' => array($vendorDir . '/symfony/http-foundation'), + 'React\\Stream\\' => array($vendorDir . '/react/stream/src'), + 'React\\Socket\\' => array($vendorDir . '/react/socket/src'), + 'React\\Promise\\Timer\\' => array($vendorDir . '/react/promise-timer/src'), + 'React\\Promise\\' => array($vendorDir . '/react/promise/src'), + 'React\\EventLoop\\' => array($vendorDir . '/react/event-loop/src'), + 'React\\Dns\\' => array($vendorDir . '/react/dns/src'), + 'React\\Cache\\' => array($vendorDir . '/react/cache/src'), + 'Ratchet\\RFC6455\\' => array($vendorDir . '/ratchet/rfc6455/src'), + 'Ratchet\\' => array($vendorDir . '/cboden/ratchet/src/Ratchet'), + 'Psr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'), + 'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-message/src'), + 'GuzzleHttp\\Psr7\\' => array($vendorDir . '/guzzlehttp/psr7/src'), ); diff --git a/vendor/composer/autoload_real.php b/vendor/composer/autoload_real.php index 3652898..95fa6ca 100644 --- a/vendor/composer/autoload_real.php +++ b/vendor/composer/autoload_real.php @@ -22,6 +22,8 @@ class ComposerAutoloaderInit16f85458e9b741f7f2b22e1ee483f627 return self::$loader; } + require __DIR__ . '/platform_check.php'; + spl_autoload_register(array('ComposerAutoloaderInit16f85458e9b741f7f2b22e1ee483f627', 'loadClassLoader'), true, true); self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__))); spl_autoload_unregister(array('ComposerAutoloaderInit16f85458e9b741f7f2b22e1ee483f627', 'loadClassLoader')); @@ -50,6 +52,24 @@ class ComposerAutoloaderInit16f85458e9b741f7f2b22e1ee483f627 $loader->register(true); + if ($useStaticLoader) { + $includeFiles = Composer\Autoload\ComposerStaticInit16f85458e9b741f7f2b22e1ee483f627::$files; + } else { + $includeFiles = require __DIR__ . '/autoload_files.php'; + } + foreach ($includeFiles as $fileIdentifier => $file) { + composerRequire16f85458e9b741f7f2b22e1ee483f627($fileIdentifier, $file); + } + return $loader; } } + +function composerRequire16f85458e9b741f7f2b22e1ee483f627($fileIdentifier, $file) +{ + if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { + require $file; + + $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true; + } +} diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php index 0c55c56..b852587 100644 --- a/vendor/composer/autoload_static.php +++ b/vendor/composer/autoload_static.php @@ -6,7 +6,21 @@ namespace Composer\Autoload; class ComposerStaticInit16f85458e9b741f7f2b22e1ee483f627 { + public static $files = array ( + 'ad155f8f1cf0d418fe49e248db8c661b' => __DIR__ . '/..' . '/react/promise/src/functions_include.php', + '972fda704d680a3a53c68e34e193cb22' => __DIR__ . '/..' . '/react/promise-timer/src/functions_include.php', + '6e3fae29631ef280660b3cdad06f25a8' => __DIR__ . '/..' . '/symfony/deprecation-contracts/function.php', + 'a4a119a56e50fbb293281d9a48007e0e' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.php', + '7b11c4dc42b3b3023073cb14e519683c' => __DIR__ . '/..' . '/ralouphie/getallheaders/src/getallheaders.php', + '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php', + 'a0edc8309cc5e1d60e3047b5df6b7052' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/functions_include.php', + ); + public static $prefixLengthsPsr4 = array ( + 'w' => + array ( + 'websocket\\' => 10, + ), 's' => array ( 'scripts\\' => 8, @@ -15,9 +29,45 @@ class ComposerStaticInit16f85458e9b741f7f2b22e1ee483f627 array ( 'app\\' => 4, ), + 'W' => + array ( + 'WebSocket\\' => 10, + ), + 'S' => + array ( + 'Symfony\\Polyfill\\Php80\\' => 23, + 'Symfony\\Polyfill\\Mbstring\\' => 26, + 'Symfony\\Component\\Routing\\' => 26, + 'Symfony\\Component\\HttpFoundation\\' => 33, + ), + 'R' => + array ( + 'React\\Stream\\' => 13, + 'React\\Socket\\' => 13, + 'React\\Promise\\Timer\\' => 20, + 'React\\Promise\\' => 14, + 'React\\EventLoop\\' => 16, + 'React\\Dns\\' => 10, + 'React\\Cache\\' => 12, + 'Ratchet\\RFC6455\\' => 16, + 'Ratchet\\' => 8, + ), + 'P' => + array ( + 'Psr\\Log\\' => 8, + 'Psr\\Http\\Message\\' => 17, + ), + 'G' => + array ( + 'GuzzleHttp\\Psr7\\' => 16, + ), ); public static $prefixDirsPsr4 = array ( + 'websocket\\' => + array ( + 0 => __DIR__ . '/../..' . '/websocket', + ), 'scripts\\' => array ( 0 => __DIR__ . '/../..' . '/scripts', @@ -26,10 +76,92 @@ class ComposerStaticInit16f85458e9b741f7f2b22e1ee483f627 array ( 0 => __DIR__ . '/../..' . '/app', ), + 'WebSocket\\' => + array ( + 0 => __DIR__ . '/..' . '/textalk/websocket/lib', + ), + 'Symfony\\Polyfill\\Php80\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/polyfill-php80', + ), + 'Symfony\\Polyfill\\Mbstring\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/polyfill-mbstring', + ), + 'Symfony\\Component\\Routing\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/routing', + ), + 'Symfony\\Component\\HttpFoundation\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/http-foundation', + ), + 'React\\Stream\\' => + array ( + 0 => __DIR__ . '/..' . '/react/stream/src', + ), + 'React\\Socket\\' => + array ( + 0 => __DIR__ . '/..' . '/react/socket/src', + ), + 'React\\Promise\\Timer\\' => + array ( + 0 => __DIR__ . '/..' . '/react/promise-timer/src', + ), + 'React\\Promise\\' => + array ( + 0 => __DIR__ . '/..' . '/react/promise/src', + ), + 'React\\EventLoop\\' => + array ( + 0 => __DIR__ . '/..' . '/react/event-loop/src', + ), + 'React\\Dns\\' => + array ( + 0 => __DIR__ . '/..' . '/react/dns/src', + ), + 'React\\Cache\\' => + array ( + 0 => __DIR__ . '/..' . '/react/cache/src', + ), + 'Ratchet\\RFC6455\\' => + array ( + 0 => __DIR__ . '/..' . '/ratchet/rfc6455/src', + ), + 'Ratchet\\' => + array ( + 0 => __DIR__ . '/..' . '/cboden/ratchet/src/Ratchet', + ), + 'Psr\\Log\\' => + array ( + 0 => __DIR__ . '/..' . '/psr/log/Psr/Log', + ), + 'Psr\\Http\\Message\\' => + array ( + 0 => __DIR__ . '/..' . '/psr/http-message/src', + ), + 'GuzzleHttp\\Psr7\\' => + array ( + 0 => __DIR__ . '/..' . '/guzzlehttp/psr7/src', + ), + ); + + public static $prefixesPsr0 = array ( + 'E' => + array ( + 'Evenement' => + array ( + 0 => __DIR__ . '/..' . '/evenement/evenement/src', + ), + ), ); public static $classMap = array ( + 'Attribute' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Attribute.php', 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php', + 'Stringable' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Stringable.php', + 'UnhandledMatchError' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php', + 'ValueError' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/ValueError.php', ); public static function getInitializer(ClassLoader $loader) @@ -37,6 +169,7 @@ class ComposerStaticInit16f85458e9b741f7f2b22e1ee483f627 return \Closure::bind(function () use ($loader) { $loader->prefixLengthsPsr4 = ComposerStaticInit16f85458e9b741f7f2b22e1ee483f627::$prefixLengthsPsr4; $loader->prefixDirsPsr4 = ComposerStaticInit16f85458e9b741f7f2b22e1ee483f627::$prefixDirsPsr4; + $loader->prefixesPsr0 = ComposerStaticInit16f85458e9b741f7f2b22e1ee483f627::$prefixesPsr0; $loader->classMap = ComposerStaticInit16f85458e9b741f7f2b22e1ee483f627::$classMap; }, null, ClassLoader::class); diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json index 87fda74..b3e94c2 100644 --- a/vendor/composer/installed.json +++ b/vendor/composer/installed.json @@ -1,5 +1,1464 @@ { - "packages": [], + "packages": [ + { + "name": "cboden/ratchet", + "version": "v0.4.3", + "version_normalized": "0.4.3.0", + "source": { + "type": "git", + "url": "https://github.com/ratchetphp/Ratchet.git", + "reference": "466a0ecc83209c75b76645eb823401b5c52e5f21" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ratchetphp/Ratchet/zipball/466a0ecc83209c75b76645eb823401b5c52e5f21", + "reference": "466a0ecc83209c75b76645eb823401b5c52e5f21", + "shasum": "" + }, + "require": { + "guzzlehttp/psr7": "^1.0", + "php": ">=5.4.2", + "ratchet/rfc6455": "^0.3", + "react/socket": "^1.0 || ^0.8 || ^0.7 || ^0.6 || ^0.5", + "symfony/http-foundation": "^2.6|^3.0|^4.0|^5.0", + "symfony/routing": "^2.6|^3.0|^4.0|^5.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.8" + }, + "time": "2020-07-07T15:50:14+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "Ratchet\\": "src/Ratchet" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "role": "Developer" + }, + { + "name": "Matt Bonneau", + "role": "Developer" + } + ], + "description": "PHP WebSocket library", + "homepage": "http://socketo.me", + "keywords": [ + "Ratchet", + "WebSockets", + "server", + "sockets", + "websocket" + ], + "support": { + "chat": "https://gitter.im/reactphp/reactphp", + "issues": "https://github.com/ratchetphp/Ratchet/issues", + "source": "https://github.com/ratchetphp/Ratchet/tree/master" + }, + "install-path": "../cboden/ratchet" + }, + { + "name": "evenement/evenement", + "version": "v3.0.1", + "version_normalized": "3.0.1.0", + "source": { + "type": "git", + "url": "https://github.com/igorw/evenement.git", + "reference": "531bfb9d15f8aa57454f5f0285b18bec903b8fb7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/igorw/evenement/zipball/531bfb9d15f8aa57454f5f0285b18bec903b8fb7", + "reference": "531bfb9d15f8aa57454f5f0285b18bec903b8fb7", + "shasum": "" + }, + "require": { + "php": ">=7.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0" + }, + "time": "2017-07-23T21:35:13+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-0": { + "Evenement": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Igor Wiedler", + "email": "igor@wiedler.ch" + } + ], + "description": "Événement is a very simple event dispatching library for PHP", + "keywords": [ + "event-dispatcher", + "event-emitter" + ], + "support": { + "issues": "https://github.com/igorw/evenement/issues", + "source": "https://github.com/igorw/evenement/tree/master" + }, + "install-path": "../evenement/evenement" + }, + { + "name": "guzzlehttp/psr7", + "version": "1.8.3", + "version_normalized": "1.8.3.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/psr7.git", + "reference": "1afdd860a2566ed3c2b0b4a3de6e23434a79ec85" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/1afdd860a2566ed3c2b0b4a3de6e23434a79ec85", + "reference": "1afdd860a2566ed3c2b0b4a3de6e23434a79ec85", + "shasum": "" + }, + "require": { + "php": ">=5.4.0", + "psr/http-message": "~1.0", + "ralouphie/getallheaders": "^2.0.5 || ^3.0.0" + }, + "provide": { + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "ext-zlib": "*", + "phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.8 || ^9.3.10" + }, + "suggest": { + "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" + }, + "time": "2021-10-05T13:56:00+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.7-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "GuzzleHttp\\Psr7\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "PSR-7 message implementation that also provides common utility methods", + "keywords": [ + "http", + "message", + "psr-7", + "request", + "response", + "stream", + "uri", + "url" + ], + "support": { + "issues": "https://github.com/guzzle/psr7/issues", + "source": "https://github.com/guzzle/psr7/tree/1.8.3" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", + "type": "tidelift" + } + ], + "install-path": "../guzzlehttp/psr7" + }, + { + "name": "psr/http-message", + "version": "1.0.1", + "version_normalized": "1.0.1.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "time": "2016-08-06T14:39:51+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-message/tree/master" + }, + "install-path": "../psr/http-message" + }, + { + "name": "psr/log", + "version": "1.1.4", + "version_normalized": "1.1.4.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "d49695b909c3b7628b6289db5479a1c204601f11" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", + "reference": "d49695b909c3b7628b6289db5479a1c204601f11", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "time": "2021-05-03T11:20:27+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Psr\\Log\\": "Psr/Log/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/1.1.4" + }, + "install-path": "../psr/log" + }, + { + "name": "ralouphie/getallheaders", + "version": "3.0.3", + "version_normalized": "3.0.3.0", + "source": { + "type": "git", + "url": "https://github.com/ralouphie/getallheaders.git", + "reference": "120b605dfeb996808c31b6477290a714d356e822" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", + "reference": "120b605dfeb996808c31b6477290a714d356e822", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^5 || ^6.5" + }, + "time": "2019-03-08T08:55:37+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "files": [ + "src/getallheaders.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" + } + ], + "description": "A polyfill for getallheaders.", + "support": { + "issues": "https://github.com/ralouphie/getallheaders/issues", + "source": "https://github.com/ralouphie/getallheaders/tree/develop" + }, + "install-path": "../ralouphie/getallheaders" + }, + { + "name": "ratchet/rfc6455", + "version": "v0.3", + "version_normalized": "0.3.0.0", + "source": { + "type": "git", + "url": "https://github.com/ratchetphp/RFC6455.git", + "reference": "c8651c7938651c2d55f5d8c2422ac5e57a183341" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ratchetphp/RFC6455/zipball/c8651c7938651c2d55f5d8c2422ac5e57a183341", + "reference": "c8651c7938651c2d55f5d8c2422ac5e57a183341", + "shasum": "" + }, + "require": { + "guzzlehttp/psr7": "^1.0", + "php": ">=5.4.2" + }, + "require-dev": { + "phpunit/phpunit": "5.7.*", + "react/socket": "^1.3" + }, + "time": "2020-05-15T18:31:24+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "Ratchet\\RFC6455\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "role": "Developer" + }, + { + "name": "Matt Bonneau", + "role": "Developer" + } + ], + "description": "RFC6455 WebSocket protocol handler", + "homepage": "http://socketo.me", + "keywords": [ + "WebSockets", + "rfc6455", + "websocket" + ], + "support": { + "chat": "https://gitter.im/reactphp/reactphp", + "issues": "https://github.com/ratchetphp/RFC6455/issues", + "source": "https://github.com/ratchetphp/RFC6455/tree/v0.3" + }, + "install-path": "../ratchet/rfc6455" + }, + { + "name": "react/cache", + "version": "v1.1.1", + "version_normalized": "1.1.1.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/cache.git", + "reference": "4bf736a2cccec7298bdf745db77585966fc2ca7e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/cache/zipball/4bf736a2cccec7298bdf745db77585966fc2ca7e", + "reference": "4bf736a2cccec7298bdf745db77585966fc2ca7e", + "shasum": "" + }, + "require": { + "php": ">=5.3.0", + "react/promise": "^3.0 || ^2.0 || ^1.1" + }, + "require-dev": { + "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35" + }, + "time": "2021-02-02T06:47:52+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "React\\Cache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "Async, Promise-based cache interface for ReactPHP", + "keywords": [ + "cache", + "caching", + "promise", + "reactphp" + ], + "support": { + "issues": "https://github.com/reactphp/cache/issues", + "source": "https://github.com/reactphp/cache/tree/v1.1.1" + }, + "funding": [ + { + "url": "https://github.com/WyriHaximus", + "type": "github" + }, + { + "url": "https://github.com/clue", + "type": "github" + } + ], + "install-path": "../react/cache" + }, + { + "name": "react/dns", + "version": "v1.8.0", + "version_normalized": "1.8.0.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/dns.git", + "reference": "2a5a74ab751e53863b45fb87e1d3913884f88248" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/dns/zipball/2a5a74ab751e53863b45fb87e1d3913884f88248", + "reference": "2a5a74ab751e53863b45fb87e1d3913884f88248", + "shasum": "" + }, + "require": { + "php": ">=5.3.0", + "react/cache": "^1.0 || ^0.6 || ^0.5", + "react/event-loop": "^1.2", + "react/promise": "^3.0 || ^2.7 || ^1.2.1", + "react/promise-timer": "^1.2" + }, + "require-dev": { + "clue/block-react": "^1.2", + "phpunit/phpunit": "^9.3 || ^4.8.35" + }, + "time": "2021-07-11T12:40:34+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "React\\Dns\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "Async DNS resolver for ReactPHP", + "keywords": [ + "async", + "dns", + "dns-resolver", + "reactphp" + ], + "support": { + "issues": "https://github.com/reactphp/dns/issues", + "source": "https://github.com/reactphp/dns/tree/v1.8.0" + }, + "funding": [ + { + "url": "https://github.com/WyriHaximus", + "type": "github" + }, + { + "url": "https://github.com/clue", + "type": "github" + } + ], + "install-path": "../react/dns" + }, + { + "name": "react/event-loop", + "version": "v1.2.0", + "version_normalized": "1.2.0.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/event-loop.git", + "reference": "be6dee480fc4692cec0504e65eb486e3be1aa6f2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/event-loop/zipball/be6dee480fc4692cec0504e65eb486e3be1aa6f2", + "reference": "be6dee480fc4692cec0504e65eb486e3be1aa6f2", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35" + }, + "suggest": { + "ext-event": "~1.0 for ExtEventLoop", + "ext-pcntl": "For signal handling support when using the StreamSelectLoop", + "ext-uv": "* for ExtUvLoop" + }, + "time": "2021-07-11T12:31:24+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "React\\EventLoop\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "ReactPHP's core reactor event loop that libraries can use for evented I/O.", + "keywords": [ + "asynchronous", + "event-loop" + ], + "support": { + "issues": "https://github.com/reactphp/event-loop/issues", + "source": "https://github.com/reactphp/event-loop/tree/v1.2.0" + }, + "funding": [ + { + "url": "https://github.com/WyriHaximus", + "type": "github" + }, + { + "url": "https://github.com/clue", + "type": "github" + } + ], + "install-path": "../react/event-loop" + }, + { + "name": "react/promise", + "version": "v2.8.0", + "version_normalized": "2.8.0.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/promise.git", + "reference": "f3cff96a19736714524ca0dd1d4130de73dbbbc4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/promise/zipball/f3cff96a19736714524ca0dd1d4130de73dbbbc4", + "reference": "f3cff96a19736714524ca0dd1d4130de73dbbbc4", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "^7.0 || ^6.5 || ^5.7 || ^4.8.36" + }, + "time": "2020-05-12T15:16:56+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "React\\Promise\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com" + } + ], + "description": "A lightweight implementation of CommonJS Promises/A for PHP", + "keywords": [ + "promise", + "promises" + ], + "support": { + "issues": "https://github.com/reactphp/promise/issues", + "source": "https://github.com/reactphp/promise/tree/v2.8.0" + }, + "install-path": "../react/promise" + }, + { + "name": "react/promise-timer", + "version": "v1.7.0", + "version_normalized": "1.7.0.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/promise-timer.git", + "reference": "607dd79990e32fcb402cb0a176b4a4be12f97e7c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/promise-timer/zipball/607dd79990e32fcb402cb0a176b4a4be12f97e7c", + "reference": "607dd79990e32fcb402cb0a176b4a4be12f97e7c", + "shasum": "" + }, + "require": { + "php": ">=5.3", + "react/event-loop": "^1.2", + "react/promise": "^3.0 || ^2.7.0 || ^1.2.1" + }, + "require-dev": { + "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35" + }, + "time": "2021-07-11T13:08:51+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "React\\Promise\\Timer\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "A trivial implementation of timeouts for Promises, built on top of ReactPHP.", + "homepage": "https://github.com/reactphp/promise-timer", + "keywords": [ + "async", + "event-loop", + "promise", + "reactphp", + "timeout", + "timer" + ], + "support": { + "issues": "https://github.com/reactphp/promise-timer/issues", + "source": "https://github.com/reactphp/promise-timer/tree/v1.7.0" + }, + "funding": [ + { + "url": "https://github.com/WyriHaximus", + "type": "github" + }, + { + "url": "https://github.com/clue", + "type": "github" + } + ], + "install-path": "../react/promise-timer" + }, + { + "name": "react/socket", + "version": "v1.9.0", + "version_normalized": "1.9.0.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/socket.git", + "reference": "aa6e3f8ebcd6dec3ad1ee92a449b4cc341994001" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/socket/zipball/aa6e3f8ebcd6dec3ad1ee92a449b4cc341994001", + "reference": "aa6e3f8ebcd6dec3ad1ee92a449b4cc341994001", + "shasum": "" + }, + "require": { + "evenement/evenement": "^3.0 || ^2.0 || ^1.0", + "php": ">=5.3.0", + "react/dns": "^1.8", + "react/event-loop": "^1.2", + "react/promise": "^2.6.0 || ^1.2.1", + "react/promise-timer": "^1.4.0", + "react/stream": "^1.2" + }, + "require-dev": { + "clue/block-react": "^1.2", + "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35", + "react/promise-stream": "^1.2" + }, + "time": "2021-08-03T12:37:06+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "React\\Socket\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "Async, streaming plaintext TCP/IP and secure TLS socket server and client connections for ReactPHP", + "keywords": [ + "Connection", + "Socket", + "async", + "reactphp", + "stream" + ], + "support": { + "issues": "https://github.com/reactphp/socket/issues", + "source": "https://github.com/reactphp/socket/tree/v1.9.0" + }, + "funding": [ + { + "url": "https://github.com/WyriHaximus", + "type": "github" + }, + { + "url": "https://github.com/clue", + "type": "github" + } + ], + "install-path": "../react/socket" + }, + { + "name": "react/stream", + "version": "v1.2.0", + "version_normalized": "1.2.0.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/stream.git", + "reference": "7a423506ee1903e89f1e08ec5f0ed430ff784ae9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/stream/zipball/7a423506ee1903e89f1e08ec5f0ed430ff784ae9", + "reference": "7a423506ee1903e89f1e08ec5f0ed430ff784ae9", + "shasum": "" + }, + "require": { + "evenement/evenement": "^3.0 || ^2.0 || ^1.0", + "php": ">=5.3.8", + "react/event-loop": "^1.2" + }, + "require-dev": { + "clue/stream-filter": "~1.2", + "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35" + }, + "time": "2021-07-11T12:37:55+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "React\\Stream\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "Event-driven readable and writable streams for non-blocking I/O in ReactPHP", + "keywords": [ + "event-driven", + "io", + "non-blocking", + "pipe", + "reactphp", + "readable", + "stream", + "writable" + ], + "support": { + "issues": "https://github.com/reactphp/stream/issues", + "source": "https://github.com/reactphp/stream/tree/v1.2.0" + }, + "funding": [ + { + "url": "https://github.com/WyriHaximus", + "type": "github" + }, + { + "url": "https://github.com/clue", + "type": "github" + } + ], + "install-path": "../react/stream" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v2.4.0", + "version_normalized": "2.4.0.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "5f38c8804a9e97d23e0c8d63341088cd8a22d627" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/5f38c8804a9e97d23e0c8d63341088cd8a22d627", + "reference": "5f38c8804a9e97d23e0c8d63341088cd8a22d627", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "time": "2021-03-23T23:28:01+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.4-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "installation-source": "dist", + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v2.4.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "install-path": "../symfony/deprecation-contracts" + }, + { + "name": "symfony/http-foundation", + "version": "v5.3.10", + "version_normalized": "5.3.10.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-foundation.git", + "reference": "9f34f02e8a5fdc7a56bafe011cea1ce97300e54c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/9f34f02e8a5fdc7a56bafe011cea1ce97300e54c", + "reference": "9f34f02e8a5fdc7a56bafe011cea1ce97300e54c", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1", + "symfony/polyfill-mbstring": "~1.1", + "symfony/polyfill-php80": "^1.16" + }, + "require-dev": { + "predis/predis": "~1.0", + "symfony/cache": "^4.4|^5.0", + "symfony/expression-language": "^4.4|^5.0", + "symfony/mime": "^4.4|^5.0" + }, + "suggest": { + "symfony/mime": "To use the file extension guesser" + }, + "time": "2021-10-11T15:41:55+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpFoundation\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Defines an object-oriented layer for the HTTP specification", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/http-foundation/tree/v5.3.10" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "install-path": "../symfony/http-foundation" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.23.1", + "version_normalized": "1.23.1.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "9174a3d80210dca8daa7f31fec659150bbeabfc6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9174a3d80210dca8daa7f31fec659150bbeabfc6", + "reference": "9174a3d80210dca8daa7f31fec659150bbeabfc6", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "time": "2021-05-27T12:26:48+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.23-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.23.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "install-path": "../symfony/polyfill-mbstring" + }, + { + "name": "symfony/polyfill-php80", + "version": "v1.23.1", + "version_normalized": "1.23.1.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "1100343ed1a92e3a38f9ae122fc0eb21602547be" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/1100343ed1a92e3a38f9ae122fc0eb21602547be", + "reference": "1100343ed1a92e3a38f9ae122fc0eb21602547be", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "time": "2021-07-28T13:41:28+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.23-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, + "files": [ + "bootstrap.php" + ], + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.23.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "install-path": "../symfony/polyfill-php80" + }, + { + "name": "symfony/routing", + "version": "v5.3.7", + "version_normalized": "5.3.7.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/routing.git", + "reference": "be865017746fe869007d94220ad3f5297951811b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/routing/zipball/be865017746fe869007d94220ad3f5297951811b", + "reference": "be865017746fe869007d94220ad3f5297951811b", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1", + "symfony/polyfill-php80": "^1.16" + }, + "conflict": { + "doctrine/annotations": "<1.12", + "symfony/config": "<5.3", + "symfony/dependency-injection": "<4.4", + "symfony/yaml": "<4.4" + }, + "require-dev": { + "doctrine/annotations": "^1.12", + "psr/log": "^1|^2|^3", + "symfony/config": "^5.3", + "symfony/dependency-injection": "^4.4|^5.0", + "symfony/expression-language": "^4.4|^5.0", + "symfony/http-foundation": "^4.4|^5.0", + "symfony/yaml": "^4.4|^5.0" + }, + "suggest": { + "symfony/config": "For using the all-in-one router or any loader", + "symfony/expression-language": "For using expression matching", + "symfony/http-foundation": "For using a Symfony Request object", + "symfony/yaml": "For using the YAML loader" + }, + "time": "2021-08-04T21:42:42+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Component\\Routing\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Maps an HTTP request to a set of configuration variables", + "homepage": "https://symfony.com", + "keywords": [ + "router", + "routing", + "uri", + "url" + ], + "support": { + "source": "https://github.com/symfony/routing/tree/v5.3.7" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "install-path": "../symfony/routing" + }, + { + "name": "textalk/websocket", + "version": "1.5.5", + "version_normalized": "1.5.5.0", + "source": { + "type": "git", + "url": "https://github.com/Textalk/websocket-php.git", + "reference": "846542f82658132cd36acb7a7e8ce0f03960c295" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Textalk/websocket-php/zipball/846542f82658132cd36acb7a7e8ce0f03960c295", + "reference": "846542f82658132cd36acb7a7e8ce0f03960c295", + "shasum": "" + }, + "require": { + "php": "^7.2 | ^8.0", + "psr/log": "^1 | ^2 | ^3" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.0", + "phpunit/phpunit": "^8.0|^9.0", + "squizlabs/php_codesniffer": "^3.5" + }, + "time": "2021-08-07T10:21:40+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "WebSocket\\": "lib" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "ISC" + ], + "authors": [ + { + "name": "Fredrik Liljegren" + }, + { + "name": "Sören Jensen", + "email": "soren@abicart.se" + } + ], + "description": "WebSocket client and server", + "support": { + "issues": "https://github.com/Textalk/websocket-php/issues", + "source": "https://github.com/Textalk/websocket-php/tree/1.5.5" + }, + "install-path": "../textalk/websocket" + } + ], "dev": true, "dev-package-names": [] } diff --git a/vendor/composer/installed.php b/vendor/composer/installed.php index 4eb6874..2fcc8bf 100644 --- a/vendor/composer/installed.php +++ b/vendor/composer/installed.php @@ -1,24 +1,209 @@ - - array ( - 'pretty_version' => '1.0.0+no-version-set', - 'version' => '1.0.0.0', - 'aliases' => - array ( + array( + 'pretty_version' => 'dev-main', + 'version' => 'dev-main', + 'type' => 'library', + 'install_path' => __DIR__ . '/../../', + 'aliases' => array(), + 'reference' => 'ed02947fd18c5e9044c66638180f2dbf369b0c9f', + 'name' => 'simplesip/whatsapp', + 'dev' => true, ), - 'reference' => NULL, - 'name' => 'simplesip/whatsapp', - ), - 'versions' => - array ( - 'simplesip/whatsapp' => - array ( - 'pretty_version' => '1.0.0+no-version-set', - 'version' => '1.0.0.0', - 'aliases' => - array ( - ), - 'reference' => NULL, + 'versions' => array( + 'cboden/ratchet' => array( + 'pretty_version' => 'v0.4.3', + 'version' => '0.4.3.0', + 'type' => 'library', + 'install_path' => __DIR__ . '/../cboden/ratchet', + 'aliases' => array(), + 'reference' => '466a0ecc83209c75b76645eb823401b5c52e5f21', + 'dev_requirement' => false, + ), + 'evenement/evenement' => array( + 'pretty_version' => 'v3.0.1', + 'version' => '3.0.1.0', + 'type' => 'library', + 'install_path' => __DIR__ . '/../evenement/evenement', + 'aliases' => array(), + 'reference' => '531bfb9d15f8aa57454f5f0285b18bec903b8fb7', + 'dev_requirement' => false, + ), + 'guzzlehttp/psr7' => array( + 'pretty_version' => '1.8.3', + 'version' => '1.8.3.0', + 'type' => 'library', + 'install_path' => __DIR__ . '/../guzzlehttp/psr7', + 'aliases' => array(), + 'reference' => '1afdd860a2566ed3c2b0b4a3de6e23434a79ec85', + 'dev_requirement' => false, + ), + 'psr/http-message' => array( + 'pretty_version' => '1.0.1', + 'version' => '1.0.1.0', + 'type' => 'library', + 'install_path' => __DIR__ . '/../psr/http-message', + 'aliases' => array(), + 'reference' => 'f6561bf28d520154e4b0ec72be95418abe6d9363', + 'dev_requirement' => false, + ), + 'psr/http-message-implementation' => array( + 'dev_requirement' => false, + 'provided' => array( + 0 => '1.0', + ), + ), + 'psr/log' => array( + 'pretty_version' => '1.1.4', + 'version' => '1.1.4.0', + 'type' => 'library', + 'install_path' => __DIR__ . '/../psr/log', + 'aliases' => array(), + 'reference' => 'd49695b909c3b7628b6289db5479a1c204601f11', + 'dev_requirement' => false, + ), + 'ralouphie/getallheaders' => array( + 'pretty_version' => '3.0.3', + 'version' => '3.0.3.0', + 'type' => 'library', + 'install_path' => __DIR__ . '/../ralouphie/getallheaders', + 'aliases' => array(), + 'reference' => '120b605dfeb996808c31b6477290a714d356e822', + 'dev_requirement' => false, + ), + 'ratchet/rfc6455' => array( + 'pretty_version' => 'v0.3', + 'version' => '0.3.0.0', + 'type' => 'library', + 'install_path' => __DIR__ . '/../ratchet/rfc6455', + 'aliases' => array(), + 'reference' => 'c8651c7938651c2d55f5d8c2422ac5e57a183341', + 'dev_requirement' => false, + ), + 'react/cache' => array( + 'pretty_version' => 'v1.1.1', + 'version' => '1.1.1.0', + 'type' => 'library', + 'install_path' => __DIR__ . '/../react/cache', + 'aliases' => array(), + 'reference' => '4bf736a2cccec7298bdf745db77585966fc2ca7e', + 'dev_requirement' => false, + ), + 'react/dns' => array( + 'pretty_version' => 'v1.8.0', + 'version' => '1.8.0.0', + 'type' => 'library', + 'install_path' => __DIR__ . '/../react/dns', + 'aliases' => array(), + 'reference' => '2a5a74ab751e53863b45fb87e1d3913884f88248', + 'dev_requirement' => false, + ), + 'react/event-loop' => array( + 'pretty_version' => 'v1.2.0', + 'version' => '1.2.0.0', + 'type' => 'library', + 'install_path' => __DIR__ . '/../react/event-loop', + 'aliases' => array(), + 'reference' => 'be6dee480fc4692cec0504e65eb486e3be1aa6f2', + 'dev_requirement' => false, + ), + 'react/promise' => array( + 'pretty_version' => 'v2.8.0', + 'version' => '2.8.0.0', + 'type' => 'library', + 'install_path' => __DIR__ . '/../react/promise', + 'aliases' => array(), + 'reference' => 'f3cff96a19736714524ca0dd1d4130de73dbbbc4', + 'dev_requirement' => false, + ), + 'react/promise-timer' => array( + 'pretty_version' => 'v1.7.0', + 'version' => '1.7.0.0', + 'type' => 'library', + 'install_path' => __DIR__ . '/../react/promise-timer', + 'aliases' => array(), + 'reference' => '607dd79990e32fcb402cb0a176b4a4be12f97e7c', + 'dev_requirement' => false, + ), + 'react/socket' => array( + 'pretty_version' => 'v1.9.0', + 'version' => '1.9.0.0', + 'type' => 'library', + 'install_path' => __DIR__ . '/../react/socket', + 'aliases' => array(), + 'reference' => 'aa6e3f8ebcd6dec3ad1ee92a449b4cc341994001', + 'dev_requirement' => false, + ), + 'react/stream' => array( + 'pretty_version' => 'v1.2.0', + 'version' => '1.2.0.0', + 'type' => 'library', + 'install_path' => __DIR__ . '/../react/stream', + 'aliases' => array(), + 'reference' => '7a423506ee1903e89f1e08ec5f0ed430ff784ae9', + 'dev_requirement' => false, + ), + 'simplesip/whatsapp' => array( + 'pretty_version' => 'dev-main', + 'version' => 'dev-main', + 'type' => 'library', + 'install_path' => __DIR__ . '/../../', + 'aliases' => array(), + 'reference' => 'ed02947fd18c5e9044c66638180f2dbf369b0c9f', + 'dev_requirement' => false, + ), + 'symfony/deprecation-contracts' => array( + 'pretty_version' => 'v2.4.0', + 'version' => '2.4.0.0', + 'type' => 'library', + 'install_path' => __DIR__ . '/../symfony/deprecation-contracts', + 'aliases' => array(), + 'reference' => '5f38c8804a9e97d23e0c8d63341088cd8a22d627', + 'dev_requirement' => false, + ), + 'symfony/http-foundation' => array( + 'pretty_version' => 'v5.3.10', + 'version' => '5.3.10.0', + 'type' => 'library', + 'install_path' => __DIR__ . '/../symfony/http-foundation', + 'aliases' => array(), + 'reference' => '9f34f02e8a5fdc7a56bafe011cea1ce97300e54c', + 'dev_requirement' => false, + ), + 'symfony/polyfill-mbstring' => array( + 'pretty_version' => 'v1.23.1', + 'version' => '1.23.1.0', + 'type' => 'library', + 'install_path' => __DIR__ . '/../symfony/polyfill-mbstring', + 'aliases' => array(), + 'reference' => '9174a3d80210dca8daa7f31fec659150bbeabfc6', + 'dev_requirement' => false, + ), + 'symfony/polyfill-php80' => array( + 'pretty_version' => 'v1.23.1', + 'version' => '1.23.1.0', + 'type' => 'library', + 'install_path' => __DIR__ . '/../symfony/polyfill-php80', + 'aliases' => array(), + 'reference' => '1100343ed1a92e3a38f9ae122fc0eb21602547be', + 'dev_requirement' => false, + ), + 'symfony/routing' => array( + 'pretty_version' => 'v5.3.7', + 'version' => '5.3.7.0', + 'type' => 'library', + 'install_path' => __DIR__ . '/../symfony/routing', + 'aliases' => array(), + 'reference' => 'be865017746fe869007d94220ad3f5297951811b', + 'dev_requirement' => false, + ), + 'textalk/websocket' => array( + 'pretty_version' => '1.5.5', + 'version' => '1.5.5.0', + 'type' => 'library', + 'install_path' => __DIR__ . '/../textalk/websocket', + 'aliases' => array(), + 'reference' => '846542f82658132cd36acb7a7e8ce0f03960c295', + 'dev_requirement' => false, + ), ), - ), ); diff --git a/websocket/Servidorsocket.php b/websocket/Servidorsocket.php new file mode 100644 index 0000000..eb5d482 --- /dev/null +++ b/websocket/Servidorsocket.php @@ -0,0 +1,69 @@ +clients = new SplObjectStorage(); + $this->provider = new Positus(); + } + + public function onOpen(ConnectionInterface $conn) + { + // Store the new connection to send messages to later + foreach ($this->clients as $key => $value) { + $conn->send(mb_convert_encoding('{"type": "Contato", "id": ' . $value->resourceId . ', "nome": "outros" }', "UTF-8")); + } + $conn->send(mb_convert_encoding('{"type": "Client", "id": ' . $conn->resourceId . ', "nome": "vc" }', "UTF-8")); + $this->clients->attach($conn); + $this->connection[$conn->resourceId] = null; + $this->shell[$conn->resourceId] = null; + $this->conectado[$conn->resourceId] = null; + $this->idConexion[$conn->resourceId] = null; + $this->clientes[$conn->resourceId] = $conn; + } + + + public function onMessage(ConnectionInterface $from, $msg) + { + $mensagem = json_decode($msg); + if ($mensagem->msg) { + $this->provider->enviarMsg($mensagem->para, $mensagem->msg); + } + try { + foreach ($this->clients as $key => $cliente) { + $cliente->send($msg); + } + } catch (\Throwable $th) { + $from->send(mb_convert_encoding('deu erro boy', "UTF-8")); + } + } + + + public function onClose(ConnectionInterface $conn) + { + // The connection is closed, remove it, as we can no longer send it messages + $this->conectado[$conn->resourceId] = false; + $this->clients->detach($conn); + } + + public function onError(ConnectionInterface $conn, \Exception $e) + { + $conn->close(); + } +} \ No newline at end of file diff --git a/websocket/WsInterface.php b/websocket/WsInterface.php new file mode 100644 index 0000000..c2b0277 --- /dev/null +++ b/websocket/WsInterface.php @@ -0,0 +1,29 @@ +client = new Client("ws://192.168.115.65:8090/ws"); + } + + function enviaMsg($msg) + { + if ($this->client->isConnected()) { + $this->client->text($msg); + $this->client->close(); + return null; + } else { + $this->client = new Client("ws://192.168.115.65:8090/ws"); + $this->client->text($msg); + $this->client->close(); + return null; + } + } +} \ No newline at end of file diff --git a/websocket/websocket.php b/websocket/websocket.php new file mode 100644 index 0000000..e47b4d2 --- /dev/null +++ b/websocket/websocket.php @@ -0,0 +1,20 @@ +run(); \ No newline at end of file From 8485c8ff5a790f31c04e056026fc3184ea860663 Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Tue, 23 Nov 2021 10:34:58 -0400 Subject: [PATCH 003/144] add modelo de evento de mensagens --- database/modelo_evento.json | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 database/modelo_evento.json diff --git a/database/modelo_evento.json b/database/modelo_evento.json new file mode 100644 index 0000000..49200b5 --- /dev/null +++ b/database/modelo_evento.json @@ -0,0 +1,18 @@ +{ + "event": { + "type": "mensagem", + "contact": { + "name": "Lucas Awade", + "number": "556581282842" + }, + "mensagem": { + "id": "425435243", + "idAtendimento": "43524352345", + "type": "text", + "media": "WHATSAPP", + "datetime": 1637677973, + "content": "ap", + "status": "read|delivery|envid" + } + } +} \ No newline at end of file From f63c1ab0cfad5e4a4cc495e30c37e4d5896ed75a Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Tue, 23 Nov 2021 10:36:39 -0400 Subject: [PATCH 004/144] update modelo mensagem --- database/{modelo_evento.json => modelo_mensagem.json} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename database/{modelo_evento.json => modelo_mensagem.json} (100%) diff --git a/database/modelo_evento.json b/database/modelo_mensagem.json similarity index 100% rename from database/modelo_evento.json rename to database/modelo_mensagem.json From b944758e21abbfdcf9993c0dc07462030c831855 Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Tue, 23 Nov 2021 10:48:18 -0400 Subject: [PATCH 005/144] update modele mensagem --- database/modelo_mensagem.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/database/modelo_mensagem.json b/database/modelo_mensagem.json index 49200b5..d239d66 100644 --- a/database/modelo_mensagem.json +++ b/database/modelo_mensagem.json @@ -3,7 +3,8 @@ "type": "mensagem", "contact": { "name": "Lucas Awade", - "number": "556581282842" + "number": "556581282842", + "matricula": "1022" }, "mensagem": { "id": "425435243", From 78ea110a4beeda8ff22b4aabc095bb60f5cf3e43 Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Tue, 23 Nov 2021 10:49:36 -0400 Subject: [PATCH 006/144] Update modelo_mensagem.json --- database/modelo_mensagem.json | 1 + 1 file changed, 1 insertion(+) diff --git a/database/modelo_mensagem.json b/database/modelo_mensagem.json index d239d66..1a2d36e 100644 --- a/database/modelo_mensagem.json +++ b/database/modelo_mensagem.json @@ -8,6 +8,7 @@ }, "mensagem": { "id": "425435243", + "dst": "871643214123", "idAtendimento": "43524352345", "type": "text", "media": "WHATSAPP", From 1595971c326d0e0cdf922754bea2c220e0cd179b Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Tue, 23 Nov 2021 13:36:15 -0400 Subject: [PATCH 007/144] updade nos cors e headers apache2 --- .htaccess | 4 +++- Dockerfile | 1 + apache2.conf | 4 ++++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/.htaccess b/.htaccess index 5cce2a1..3b8eaed 100644 --- a/.htaccess +++ b/.htaccess @@ -1,4 +1,6 @@ RewriteEngine on RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d -RewriteRule ^(.*)$ index.php/$1 [L] \ No newline at end of file +RewriteRule ^(.*)$ index.php/$1 [L] +AddType application/json json php +php_value default_mimetype application/json \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index e3d1c5c..6575983 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,6 +13,7 @@ RUN echo "ServerName 192.168.115.65" >> /etc/apache2/apache2.conf &&\ COPY apache2.conf /etc/apache2/ COPY ports.conf /etc/apache2/ COPY . . +RUN a2enmod headers RUN service apache2 restart WORKDIR /var/www/html diff --git a/apache2.conf b/apache2.conf index cdbbd7c..2ba5c3a 100644 --- a/apache2.conf +++ b/apache2.conf @@ -158,16 +158,19 @@ Include ports.conf # access here, or in any related virtual host. Options FollowSymLinks + Header set Access-Control-Allow-Origin "*" AllowOverride None Require all denied + Header set Access-Control-Allow-Origin "*" AllowOverride None Require all granted + Header set Access-Control-Allow-Origin "*" Options Indexes FollowSymLinks AllowOverride None Require all granted @@ -193,6 +196,7 @@ AccessFileName .htaccess # viewed by Web clients. # + Header set Access-Control-Allow-Origin "*" Require all denied From 32bc6726547890428fcbb0b7fe2a6cf0c8cd2d0e Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Tue, 23 Nov 2021 15:01:08 -0400 Subject: [PATCH 008/144] add api agente --- app/Core/Model.php | 6 +++ app/Interfaces/IApiMedia.php | 1 + app/Middleware/ApiAgente.php | 90 ++++++++++++++++++++++++++++++++++- app/Middleware/Middleware.php | 3 +- app/Models/Message.php | 21 ++++++-- app/Providers/ApiTelegram.php | 6 ++- app/Providers/Positus.php | 43 ++++++++++++----- service/request.php | 29 +++++++++++ 8 files changed, 178 insertions(+), 21 deletions(-) create mode 100644 service/request.php diff --git a/app/Core/Model.php b/app/Core/Model.php index 93b2e84..ac5d3e0 100644 --- a/app/Core/Model.php +++ b/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,6 +56,7 @@ abstract class Model Connect::getInstance()->rollBack(); return 1; } catch (PDOException $ex) { + Connect::setInstance(null); logger('PDOExcep')->error('PDOException: ' . $ex->getMessage(), debug_backtrace()); return null; } @@ -71,6 +74,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; } @@ -100,6 +104,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; } @@ -114,6 +119,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; } diff --git a/app/Interfaces/IApiMedia.php b/app/Interfaces/IApiMedia.php index e49f711..1f947a2 100644 --- a/app/Interfaces/IApiMedia.php +++ b/app/Interfaces/IApiMedia.php @@ -36,4 +36,5 @@ interface IApiMedia function response($result); public function getLinkDownload($host); public function retornaTituloDocument($msg); + public function convertToWebsocket($msg); } \ No newline at end of file diff --git a/app/Middleware/ApiAgente.php b/app/Middleware/ApiAgente.php index 43140d3..cb3a614 100644 --- a/app/Middleware/ApiAgente.php +++ b/app/Middleware/ApiAgente.php @@ -3,15 +3,21 @@ namespace app\Middleware; use app\Controllers\AgentController; +use app\Controllers\ClassificacaoController; use app\Controllers\QueueController; +use app\Core\Media; +use app\Models\Message; use app\Models\Ramal; +use app\Providers\Positus; class ApiAgente { public AgentController $agentController; - + public ClassificacaoController $classificacao; function __construct() { + $this->queue = new QueueController(); + $this->classificacao = new ClassificacaoController(); $this->agentController = new AgentController(); } @@ -24,6 +30,15 @@ class ApiAgente case 'listaFilas': $this->listaFilas($request); break; + case 'refreshAgent': + $this->refreshAgent($request); + break; + case 'classificarAtendimento': + $this->classificarAtendimento($request); + break; + case 'enviaMsg': + $this->enviaMsg($request); + break; default: echo json_encode(['status' => '404']); break; @@ -55,4 +70,77 @@ class ApiAgente echo json_encode($lista); return null; } + + public function refreshAgent($request) + { + $retorno = $this->agentController->refreshAgent($request['ramal'], $request['option']); + if (empty($retorno)) { + echo json_encode(['status' => "false"]); + } else { + echo json_encode(['status' => "Mensagem: " . utf8_encode($retorno)]); + } + + return null; + } + + public function classificarAtendimento($request) + { + $agent = $this->agentController->getAgente($request['ramal']); + + if (!$agent) { + $msg = "Faç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']; + $provider = new Positus(); + $modelMensagem = new Message(); + $retuno = $provider->enviarMsg($mensagem['dst'], $mensagem['content']); + $modelMensagem->addMessage( + $mensagem['idAtendimento'], + $contact['matricula'], + $mensagem['dst'], + $mensagem['type'], + $mensagem['content'], + $contact['name'], + $mensagem['media'], + $mensagem['status'] + ); + echo json_encode(['status' => $retuno]); + return null; + } catch (\Throwable $th) { + echo json_encode(['status' => false, 'message' => '' . $th->getMessage()]); + return null; + } + } } \ No newline at end of file diff --git a/app/Middleware/Middleware.php b/app/Middleware/Middleware.php index 0ee199e..6204cf0 100644 --- a/app/Middleware/Middleware.php +++ b/app/Middleware/Middleware.php @@ -61,8 +61,6 @@ class Middleware extends Http ]; switch (strtolower($this->param()[1])) { case 'whatsapp': - $ws = new WsInterface(); - $ws->enviaMsg(json_encode($this->request)); $file = $coremedia->inicia($data, new Positus()); if ($file) { $this->download($file); @@ -75,6 +73,7 @@ class Middleware extends Http return null; case 'api': $apiAgente->router($this->param()[2], $this->request); + return null; default: $this->header->redirect(); } diff --git a/app/Models/Message.php b/app/Models/Message.php index 2bc5997..8a15c6b 100644 --- a/app/Models/Message.php +++ b/app/Models/Message.php @@ -1,6 +1,7 @@ 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)]); + $this->query = "INSERT INTO " . self::MESSAGE . " (uniqueid, src, dst, type, content, profile_name, media, status) + VALUES(:uniqueid, :src, :dst, :type, :content, :profile_name, :media, :status);"; + return $this->create($this->query, [ + 'uniqueid' => $idAtendimento, + 'src' => $src, + 'dst' => $dst, + 'type' => $tipo, + 'content' => utf8_decode($conteudo), + 'profile_name' => utf8_decode($profile_name), + 'media' => $media, + 'status' => $status, + ]); } public function findMessageByUniqueid($uniqueid) @@ -37,4 +48,4 @@ class Message extends Model $this->query .= " ORDER BY msg_date DESC LIMIT 1"; return $this->read($this->query, ['uniqueid' => $uniqueid])->fetch(); } -} +} \ No newline at end of file diff --git a/app/Providers/ApiTelegram.php b/app/Providers/ApiTelegram.php index 7f44f9f..26b9a56 100644 --- a/app/Providers/ApiTelegram.php +++ b/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']; } -} \ No newline at end of file +} diff --git a/app/Providers/Positus.php b/app/Providers/Positus.php index 15de5a7..b15c6ed 100644 --- a/app/Providers/Positus.php +++ b/app/Providers/Positus.php @@ -27,21 +27,39 @@ class Positus implements IApiMedia ######################################################################## ## RECURSOS DA API ## ######################################################################## - + public function convertToWebsocket($msg) + { + if ($msg['contacts']) { + $mensagem = []; + $mensagem["event"] = [ + "type" => "mensagem", + "contact" => [ + "name" => $msg['contacts'][0]['profile']['name'], + "number" => $msg['contacts'][0]['wa_id'] + ], + "mensagem" => [ + "type" => $msg['messages'][0]['type'], + "content" => $msg['messages'][0]['text']['body'] + ] + ]; + return json_encode($mensagem); + } + return ""; + } function enviarMsg($whatsapp, $mensagem) { $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(); - } - return false; + //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(); + // } + // return false; } function enviarMsgIterativaLista($whatsapp, $mensagem, $nomeButton, $lista, $prex = '') @@ -495,6 +513,7 @@ class Positus implements IApiMedia function response($result) { + logger('deburguer')->info(gettype($result)); if ($result) { try { if (json_decode($result, true) !== null) { diff --git a/service/request.php b/service/request.php new file mode 100644 index 0000000..d9a3d50 --- /dev/null +++ b/service/request.php @@ -0,0 +1,29 @@ +#!/usr/bin/php -q +findRamal('556581282842'); + +print_r($info); \ No newline at end of file From 7a2f00544b8d60332d198279ca83076aa46ee500 Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Tue, 23 Nov 2021 15:01:52 -0400 Subject: [PATCH 009/144] update vendor e compose --- composer.json | 13 ++++++++----- vendor/composer/autoload_psr4.php | 1 + vendor/composer/autoload_static.php | 5 +++++ vendor/composer/installed.php | 4 ++-- 4 files changed, 16 insertions(+), 7 deletions(-) diff --git a/composer.json b/composer.json index ece3a7e..3583933 100644 --- a/composer.json +++ b/composer.json @@ -1,15 +1,18 @@ { "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/", - "websocket\\": "websocket/" + "websocket\\": "websocket/", + "service\\": "service/" } }, "require": { diff --git a/vendor/composer/autoload_psr4.php b/vendor/composer/autoload_psr4.php index 7eac0ee..74c1aad 100644 --- a/vendor/composer/autoload_psr4.php +++ b/vendor/composer/autoload_psr4.php @@ -7,6 +7,7 @@ $baseDir = dirname($vendorDir); return array( 'websocket\\' => array($baseDir . '/websocket'), + 'service\\' => array($baseDir . '/service'), 'scripts\\' => array($baseDir . '/scripts'), 'app\\' => array($baseDir . '/app'), 'WebSocket\\' => array($vendorDir . '/textalk/websocket/lib'), diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php index b852587..8348f54 100644 --- a/vendor/composer/autoload_static.php +++ b/vendor/composer/autoload_static.php @@ -23,6 +23,7 @@ class ComposerStaticInit16f85458e9b741f7f2b22e1ee483f627 ), 's' => array ( + 'service\\' => 8, 'scripts\\' => 8, ), 'a' => @@ -68,6 +69,10 @@ class ComposerStaticInit16f85458e9b741f7f2b22e1ee483f627 array ( 0 => __DIR__ . '/../..' . '/websocket', ), + 'service\\' => + array ( + 0 => __DIR__ . '/../..' . '/service', + ), 'scripts\\' => array ( 0 => __DIR__ . '/../..' . '/scripts', diff --git a/vendor/composer/installed.php b/vendor/composer/installed.php index 2fcc8bf..8370c12 100644 --- a/vendor/composer/installed.php +++ b/vendor/composer/installed.php @@ -5,7 +5,7 @@ 'type' => 'library', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), - 'reference' => 'ed02947fd18c5e9044c66638180f2dbf369b0c9f', + 'reference' => '5c34ab7b32db21ad29865a50f9f49a9cdeb9c830', 'name' => 'simplesip/whatsapp', 'dev' => true, ), @@ -148,7 +148,7 @@ 'type' => 'library', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), - 'reference' => 'ed02947fd18c5e9044c66638180f2dbf369b0c9f', + 'reference' => '5c34ab7b32db21ad29865a50f9f49a9cdeb9c830', 'dev_requirement' => false, ), 'symfony/deprecation-contracts' => array( From 921f89df2de6a664df0a4164f90a5e18ae38867d Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Tue, 23 Nov 2021 15:06:37 -0400 Subject: [PATCH 010/144] migrate monitora agente --- scripts/service/monitora_agente_wpp.php | 211 ------------------------ scripts/service/request.php | 29 ---- scripts/service/teste.php | 8 - service/MonitoraAgente.php | 196 ++++++++++++++++++++++ websocket/Servidorsocket.php | 40 +++-- websocket/WsInterface.php | 20 ++- 6 files changed, 233 insertions(+), 271 deletions(-) delete mode 100644 scripts/service/monitora_agente_wpp.php delete mode 100644 scripts/service/request.php delete mode 100644 scripts/service/teste.php create mode 100644 service/MonitoraAgente.php diff --git a/scripts/service/monitora_agente_wpp.php b/scripts/service/monitora_agente_wpp.php deleted file mode 100644 index e23e708..0000000 --- a/scripts/service/monitora_agente_wpp.php +++ /dev/null @@ -1,211 +0,0 @@ -#!/usr/bin/php -q - "Você será desconectado em " . ($timeout_agent_desconexao - $timeout_agent_alert) . " minutos, para permanecer em atendimento clique em '/presente'", - "ALERTA_DESCONECTADO" => "Você foi desconectado por inatividade!", - "ALERTA_CLIENTE" => "Olá, como podemos lhe ajudar!", - "TIMEOUT_CLIENT_INATIVIDADE" => "O tempo da conversa foi expirado, por favor volte novamente a fila para um novo atendimento!", - "TALK_FINISHED" => "O atendimento foi finalizado!", - "TALK_ALERT_FINISH" => "Por favor, volte a comunicação com nosso atendimento em 1 minuto ou atendimento será encerrado!" -]; - -while (true) { - try { - $agentesLista = $agente->inactiveAgents(); - foreach ($agentesLista as $item) { - $positus = retornaApiMedia($item['SALA1']); - $freetime = $item['FREETIME'] / 60; - - if (($freetime >= $timeout_agent_alert) && ($freetime < $timeout_agent_desconexao) && !$item['SALA2'] && $item['STATUS'] == CONF_AGENT_STATUS_LIVRE) { - $agente->timeFinishAgente($item['MATRICULA'], $item['RAMAL'], ($timeout_agent_desconexao - $timeout_agent_alert)); - $positus->enviarMsgIterativaBotao($item['RAMAL'], $mensagem['ALERTA_INATIVIDADE'], [ - [ - "type" => "reply", - "reply" => [ - "id" => "unique-postback-id-1", - "title" => utf8_encode("/presente") - ] - ] - ]); - } else if ($freetime >= $timeout_agent_desconexao && $item['STATUS'] == CONF_AGENT_STATUS_LIVRE) { - $agente->logoff($item['RAMAL']); - $positus->enviarMsg($item['RAMAL'], utf8_encode($mensagem['ALERTA_DESCONECTADO'])); - } else { - $agente->refreshAgent($item['RAMAL'], $item['SALA2']); - } - - if ($item['STATUS'] == CONF_AGENT_STATUS_LIVRE) { - $chamadaSemClassificacao = $classificacao->agentClassificacaoPending($item['MATRICULA'], $item['FILA']); - if ($chamadaSemClassificacao) { //BLOQUEAR ATENDIMENTO SEM CLASSIFICACAO - if ($item['FREETIME'] % $timeout_agent_classificacao == 0) { - $classificacaoList = $classificacao->agentClassificacaoList($item['FILA']); - $retorno = $notificaMedia->verificaNotifica( - $chamadaSemClassificacao, - $item['RAMAL'], - utf8_encode(CONF_NAME_REPONSE . " : " . $classificacaoList) - ); - if ($retorno->quant == 0) { - $notificaMedia->addNotifica( - $chamadaSemClassificacao, - $item['RAMAL'], - utf8_encode(CONF_NAME_REPONSE . " : " . $classificacaoList) - ); - $positus->enviarMsg( - $item['RAMAL'], - utf8_encode(CONF_NAME_REPONSE . " : " . $classificacaoList) - ); - } - } - continue; - } - - $atendimento = $bilhete->bilheteForaHorario(CONF_EVENT_WHATSAPP_ESPERA, $item['FILA'], null, $item['SALA1']); - if ($atendimento) { - /** - * VALIDA O ATENDIMENTO - */ - $response = $media->validaAtendimento($positus, $atendimento[0]->fila, null, $atendimento[0]->src, $item['SALA1']); - $bilhete->atenderBilheteForaHorario($atendimento[0]->uniqueid, $atendimento[0]->src, $atendimento[0]->fila, $item['MATRICULA'], $response['DATA']['uniqueid']); - $atendimento = $bilhete->bilheteForaHorario(CONF_EVENT_WHATSAPP_ESPERA, $item['FILA'], null, $item['SALA1']); - foreach ($atendimento as $cliente) { - $mensagemFila = 'Você está na posição ' . $media2->getClientQueueData($cliente->src, $atendimento)['QUEUE_POSITION'] . '. Aguarde ser atendido!'; - $positus->enviarMsg($cliente->src, utf8_encode($mensagemFila)); - } - } - } - - if ($item['STATUS'] == CONF_AGENT_STATUS_OCUPADO) { - if ($message->timeoutTalk($item['UNIQUEID'], $item['RAMAL']) == 'FINISH' && $agente->closeService($item['ORIGEM_DESTINO'], CONF_EVENT_WHATSAPP_TIMEOUT_CLIENTE, $item['SALA1'])) { - $positus->enviarMsg($item['ORIGEM_DESTINO'], utf8_encode($mensagem['TIMEOUT_CLIENT_INATIVIDADE'])); - $positus->enviarMsg($item['ORIGEM_DESTINO'], utf8_encode($mensagem['TALK_FINISHED'])); - $positus->enviarMsg($item['RAMAL'], utf8_encode($mensagem['TALK_FINISHED'])); - } - if ($message->timeoutTalk($item['UNIQUEID'], $item['RAMAL']) == 'ALERT') { - $msg = $mensages->findLastMessage($item['UNIQUEID']); - $retorno = $notificaMedia->verificaNotifica( - $item['UNIQUEID'], - $item['ORIGEM_DESTINO'], - utf8_encode($mensagem['TALK_ALERT_FINISH']) . $msg->id - ); - if ($retorno->quant == 0) { - $notificaMedia->addNotifica( - $item['UNIQUEID'], - $item['ORIGEM_DESTINO'], - utf8_encode($mensagem['TALK_ALERT_FINISH']) . $msg->id - ); - $positus->enviarMsg( - $item['ORIGEM_DESTINO'], - utf8_encode($mensagem['TALK_ALERT_FINISH']) - ); - } - } - } - $supervisor->calcTimeAwait(); - } - - sleep(3); - ignore_user_abort(true); - - /* - * Verifica se o processo deve ser encerrado - */ - if (sig_status()) { - break; - } - } catch (Exception $ex) { - sleep(3); - Connect::setInstance(null); - logger('testeOff')->info($th->getMessage(), debug_backtrace()); - } -} - -function retornaApiMedia($media = '') -{ - switch ($media) { - case CONF_TELEGRAM_CHANNEL: - return new ApiTelegram(); - case CONF_WHATSAPP_CHANNEL: - return new Positus();; - default: - break; - } -} - -function GetDaemon($notDaemon) -{ - /* - * Se o script não for chamado com daemon sai sem executar nada. - */ - if ($notDaemon) { - return 0; - } - - $pid = pcntl_fork(); - if ($pid) { - exit(0); //success - } -} - -function sig_handler($signo) -{ - global $statusSignal; - $statusSignal = 1; -} - -function sig_status() -{ - global $statusSignal; - pcntl_signal_dispatch(); - return $statusSignal; -} \ No newline at end of file diff --git a/scripts/service/request.php b/scripts/service/request.php deleted file mode 100644 index d9a3d50..0000000 --- a/scripts/service/request.php +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/php -q -findRamal('556581282842'); - -print_r($info); \ No newline at end of file diff --git a/scripts/service/teste.php b/scripts/service/teste.php deleted file mode 100644 index 8749254..0000000 --- a/scripts/service/teste.php +++ /dev/null @@ -1,8 +0,0 @@ -agente = new AgentController(); + $this->bilhete = new BilheteController(); + $this->message = new MessageController(); + $this->media = new CoreMedia(); + $this->media2 = new Media(); + $this->notificaMedia = new NotificaMedia(); + $this->mensages = new Message(); + $this->classificacao = new ClassificacaoController(); + $this->supervisor = new SupervisorQueueController(); + $this->timeout_agent_alert = (CONF_WHATSAPP_TIMEOUT_AGENT_AVISO / 60); + $this->timeout_agent_desconexao = (CONF_WHATSAPP_TIMEOUT_AGENT_DESCONEXAO / 60); + $this->timeout_agent_classificacao = (CONF_WHATSAPP_TIMEOUT_AGENT_CLASSIFICACAO / 60); + $this->mensagem = [ + "ALERTA_INATIVIDADE" => "Você será desconectado em " . ($this->timeout_agent_desconexao - $this->timeout_agent_alert) . + " minutos, para permanecer em atendimento clique em '/presente'", + "ALERTA_DESCONECTADO" => "Você foi desconectado por inatividade!", + "ALERTA_CLIENTE" => "Olá, como podemos lhe ajudar!", + "TIMEOUT_CLIENT_INATIVIDADE" => "O tempo da conversa foi expirado, por favor volte novamente a fila para um novo atendimento!", + "TALK_FINISHED" => "O atendimento foi finalizado!", + "TALK_ALERT_FINISH" => "Por favor, volte a comunicação com nosso atendimento em 1 minuto ou atendimento será encerrado!" + ]; + } + + + function monitora(ConnectionInterface $conn) + { + try { + $agentesLista = $this->agente->inactiveAgents(); + foreach ($agentesLista as $item) { + $positus = $this->retornaApiMedia($item['SALA1']); + //verifica os timeout do agente + $this->timeoutAgente($item, $conn); + + if ($item['STATUS'] == CONF_AGENT_STATUS_LIVRE) { + $chamadaSemClassificacao = $this->classificacao->agentClassificacaoPending($item['MATRICULA'], $item['FILA']); + if ($chamadaSemClassificacao) { //BLOQUEAR ATENDIMENTO SEM CLASSIFICACAO + if ($item['FREETIME'] % $this->timeout_agent_classificacao == 0) { + $classificacaoList = $this->classificacao->agentClassificacaoList($item['FILA']); + + // $retorno = $this->notificaMedia->verificaNotifica( + // $chamadaSemClassificacao, + // $item['RAMAL'], + // utf8_encode(CONF_NAME_REPONSE . " : " . $classificacaoList) + // ); + // if ($retorno->quant == 0) { + // $this->notificaMedia->addNotifica( + // $chamadaSemClassificacao, + // $item['RAMAL'], + // utf8_encode(CONF_NAME_REPONSE . " : " . $classificacaoList) + // ); + $conn->send($this->enviaActions(CONF_NAME_REPONSE . " : " . $classificacaoList, 'classificacao')); + // $positus->enviarMsg( + // $item['RAMAL'], + // utf8_encode(CONF_NAME_REPONSE . " : " . $classificacaoList) + // ); + // } + } + continue; + } + + $atendimento = $this->bilhete->bilheteForaHorario(CONF_EVENT_WHATSAPP_ESPERA, $item['FILA'], null, $item['SALA1']); + if ($atendimento) { + /** + * VALIDA O ATENDIMENTO + */ + $response = $this->media->validaAtendimento($positus, $atendimento[0]->fila, null, $atendimento[0]->src, $item['SALA1']); + $this->bilhete->atenderBilheteForaHorario($atendimento[0]->uniqueid, $atendimento[0]->src, $atendimento[0]->fila, $item['MATRICULA'], $response['DATA']['uniqueid']); + $atendimento = $this->bilhete->bilheteForaHorario(CONF_EVENT_WHATSAPP_ESPERA, $item['FILA'], null, $item['SALA1']); + foreach ($atendimento as $cliente) { + $mensagemFila = 'Você está na posição ' . $this->media2->getClientQueueData($cliente->src, $atendimento)['QUEUE_POSITION'] . '. Aguarde ser atendido!'; + $positus->enviarMsg($cliente->src, utf8_encode($mensagemFila)); + } + } + } + + if ($item['STATUS'] == CONF_AGENT_STATUS_OCUPADO) { + if ($this->message->timeoutTalk($item['UNIQUEID'], $item['RAMAL']) == 'FINISH' && $this->agente->closeService($item['ORIGEM_DESTINO'], CONF_EVENT_WHATSAPP_TIMEOUT_CLIENTE, $item['SALA1'])) { + $positus->enviarMsg($item['ORIGEM_DESTINO'], utf8_encode($this->mensagem['TIMEOUT_CLIENT_INATIVIDADE'])); + $positus->enviarMsg($item['ORIGEM_DESTINO'], utf8_encode($this->mensagem['TALK_FINISHED'])); + $positus->enviarMsg($item['RAMAL'], utf8_encode($this->mensagem['TALK_FINISHED'])); + } + if ($this->message->timeoutTalk($item['UNIQUEID'], $item['RAMAL']) == 'ALERT') { + $msg = $this->mensages->findLastMessage($item['UNIQUEID']); + $retorno = $this->notificaMedia->verificaNotifica( + $item['UNIQUEID'], + $item['ORIGEM_DESTINO'], + utf8_encode($this->mensagem['TALK_ALERT_FINISH']) . $msg->id + ); + if ($retorno->quant == 0) { + $this->notificaMedia->addNotifica( + $item['UNIQUEID'], + $item['ORIGEM_DESTINO'], + utf8_encode($this->mensagem['TALK_ALERT_FINISH']) . $msg->id + ); + $positus->enviarMsg( + $item['ORIGEM_DESTINO'], + utf8_encode($this->mensagem['TALK_ALERT_FINISH']) + ); + } + } + } + $this->supervisor->calcTimeAwait(); + } + } catch (Exception $ex) { + Connect::setInstance(null); + logger('monitora')->info($ex->getMessage(), debug_backtrace()); + } + } + + function retornaApiMedia($media = '') + { + switch ($media) { + case CONF_TELEGRAM_CHANNEL: + return new ApiTelegram(); + case CONF_WHATSAPP_CHANNEL: + return new Positus();; + default: + break; + } + } + + function timeoutAgente($item, $conn) + { + $freetime = $item['FREETIME'] / 60; + if (($freetime >= $this->timeout_agent_alert) && ($freetime < $this->timeout_agent_desconexao) && !$item['SALA2'] && $item['STATUS'] == CONF_AGENT_STATUS_LIVRE) { + $this->agente->timeFinishAgente($item['MATRICULA'], $item['RAMAL'], ($this->timeout_agent_desconexao - $this->timeout_agent_alert)); + $conn->send($this->enviaActions($this->mensagem['ALERTA_INATIVIDADE'], 'ALERTA_INATIVIDADE')); + } else if ($freetime >= $this->timeout_agent_desconexao && $item['STATUS'] == CONF_AGENT_STATUS_LIVRE) { + $this->agente->logoff($item['RAMAL']); + $conn->send($this->enviaActions($this->mensagem['ALERTA_DESCONECTADO'], 'ALERTA_DESCONECTADO')); + } else { + $this->agente->refreshAgent($item['RAMAL'], $item['SALA2']); + } + } + + function enviaActions($msg, $tipo) + { + try { + $mensagem = []; + $mensagem["event"] = [ + "type" => 'actions', + "contact" => [ + "name" => 'Sistema', + "number" => '0' + ], + "mensagem" => [ + "type" => $tipo, + "content" => utf8_encode($msg) + ], + ]; + return json_encode($mensagem); + } catch (\Throwable $th) { + logger('monitora')->info($th->getMessage(), debug_backtrace()); + } + } +} diff --git a/websocket/Servidorsocket.php b/websocket/Servidorsocket.php index eb5d482..c7d6914 100644 --- a/websocket/Servidorsocket.php +++ b/websocket/Servidorsocket.php @@ -5,6 +5,7 @@ namespace websocket; use app\Providers\Positus; use Ratchet\MessageComponentInterface; use Ratchet\ConnectionInterface; +use service\MonitoraAgente; use SplObjectStorage; class Servidorsocket implements MessageComponentInterface @@ -25,32 +26,43 @@ class Servidorsocket implements MessageComponentInterface public function onOpen(ConnectionInterface $conn) { - // Store the new connection to send messages to later - foreach ($this->clients as $key => $value) { - $conn->send(mb_convert_encoding('{"type": "Contato", "id": ' . $value->resourceId . ', "nome": "outros" }', "UTF-8")); - } - $conn->send(mb_convert_encoding('{"type": "Client", "id": ' . $conn->resourceId . ', "nome": "vc" }', "UTF-8")); + $headers = $conn->httpRequest->getHeaders(); + $conn->send($conn->resourceId); $this->clients->attach($conn); $this->connection[$conn->resourceId] = null; $this->shell[$conn->resourceId] = null; $this->conectado[$conn->resourceId] = null; $this->idConexion[$conn->resourceId] = null; - $this->clientes[$conn->resourceId] = $conn; + try { + array_push($this->clientes, [ + "ramal" => $headers['ramal'][0], + "conection" => $conn + ]); + } catch (\Throwable $th) { + //throw $th; + } } - public function onMessage(ConnectionInterface $from, $msg) { $mensagem = json_decode($msg); if ($mensagem->msg) { $this->provider->enviarMsg($mensagem->para, $mensagem->msg); - } - try { - foreach ($this->clients as $key => $cliente) { - $cliente->send($msg); + } else { + try { + foreach ($this->clientes as $key => $value) { + if ($mensagem->event->type == 'mensagem') { + # code... + } + if ($value['ramal'] == 8081) { + $monitora = new MonitoraAgente(); + $monitora->monitora($value['conection']); + } + $value['conection']->send($msg); + } + } catch (\Throwable $th) { + $from->send(mb_convert_encoding('deu erro boy', "UTF-8")); } - } catch (\Throwable $th) { - $from->send(mb_convert_encoding('deu erro boy', "UTF-8")); } } @@ -66,4 +78,4 @@ class Servidorsocket implements MessageComponentInterface { $conn->close(); } -} \ No newline at end of file +} diff --git a/websocket/WsInterface.php b/websocket/WsInterface.php index c2b0277..23337da 100644 --- a/websocket/WsInterface.php +++ b/websocket/WsInterface.php @@ -15,15 +15,17 @@ class WsInterface function enviaMsg($msg) { - if ($this->client->isConnected()) { - $this->client->text($msg); - $this->client->close(); - return null; - } else { - $this->client = new Client("ws://192.168.115.65:8090/ws"); - $this->client->text($msg); - $this->client->close(); - return null; + if ($msg) { + if ($this->client->isConnected()) { + $this->client->text($msg); + $this->client->close(); + return null; + } else { + $this->client = new Client("ws://192.168.115.65:8090/ws"); + $this->client->text($msg); + $this->client->close(); + return null; + } } } } \ No newline at end of file From dee2daad3a9127e2fa0a50d9ce08e77a897fe010 Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Tue, 23 Nov 2021 15:07:13 -0400 Subject: [PATCH 011/144] Update CoreMedia.php --- app/Core/CoreMedia.php | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/app/Core/CoreMedia.php b/app/Core/CoreMedia.php index e4f0c31..4d2559a 100644 --- a/app/Core/CoreMedia.php +++ b/app/Core/CoreMedia.php @@ -8,8 +8,10 @@ use app\Models\Message; use app\Providers\Crypt; use app\Controllers\QueueController; use app\Controllers\AgentController; +use app\Interfaces\IApiMedia; use app\Models\ListaNegraPalavras; use app\Models\Ramal; +use websocket\WsInterface; /** * Description of WhatsApp @@ -28,10 +30,12 @@ class CoreMedia private $queue; private $agente; private $palavroes; + private $ws; public function __construct() { $this->media = new Media(); + $this->ws = new WsInterface(); $this->queue = new QueueController(); $this->commands = new Commands(); $this->agente = new AgentController(); @@ -45,9 +49,11 @@ class CoreMedia * * @return null */ - public function inicia($data, $api) + public function inicia($data, IApiMedia $api) { /** Validate $data */ + + if (!$this->build($data)) { return false; } @@ -59,7 +65,7 @@ class CoreMedia $file = $this->api->baixarMidia($this->file); return $file; } - + $this->ws->enviaMsg($api->convertToWebsocket($this->request)); /** * VERIFICA SE N?O MENSAGEM DO POSITUS DE CONFIRMA??O */ @@ -120,6 +126,7 @@ class CoreMedia return null; } } + /** * VALIDA O ATENDIMENTO */ @@ -193,6 +200,7 @@ class CoreMedia switch ($api->getType()) { case 'text': + $this->ws->enviaMsg($api->getMessage()); $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; From 946d0fa94eaf439c4d47a6f573748e17658fc67b Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Tue, 23 Nov 2021 16:06:30 -0400 Subject: [PATCH 012/144] =?UTF-8?q?ajuste=20do=20padr=C3=A3o=20de=20msg=20?= =?UTF-8?q?simples=20chat?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Core/CoreMedia.php | 2 +- app/Interfaces/IApiMedia.php | 2 +- app/Providers/Positus.php | 15 ++++++++++++--- database/msg-text-positus.json | 17 +++++++++++++++++ 4 files changed, 31 insertions(+), 5 deletions(-) create mode 100644 database/msg-text-positus.json diff --git a/app/Core/CoreMedia.php b/app/Core/CoreMedia.php index 4d2559a..f47a681 100644 --- a/app/Core/CoreMedia.php +++ b/app/Core/CoreMedia.php @@ -65,7 +65,7 @@ class CoreMedia $file = $this->api->baixarMidia($this->file); return $file; } - $this->ws->enviaMsg($api->convertToWebsocket($this->request)); + $this->ws->enviaMsg($api->convertToWebsocket($this->request, '1022', time())); /** * VERIFICA SE N?O MENSAGEM DO POSITUS DE CONFIRMA??O */ diff --git a/app/Interfaces/IApiMedia.php b/app/Interfaces/IApiMedia.php index 1f947a2..915da99 100644 --- a/app/Interfaces/IApiMedia.php +++ b/app/Interfaces/IApiMedia.php @@ -36,5 +36,5 @@ interface IApiMedia function response($result); public function getLinkDownload($host); public function retornaTituloDocument($msg); - public function convertToWebsocket($msg); + public function convertToWebsocket($msg, $matricula, $atendimento); } \ No newline at end of file diff --git a/app/Providers/Positus.php b/app/Providers/Positus.php index b15c6ed..00286b4 100644 --- a/app/Providers/Positus.php +++ b/app/Providers/Positus.php @@ -27,7 +27,7 @@ class Positus implements IApiMedia ######################################################################## ## RECURSOS DA API ## ######################################################################## - public function convertToWebsocket($msg) + public function convertToWebsocket($msg, $matricula = '', $atendimento) { if ($msg['contacts']) { $mensagem = []; @@ -35,17 +35,26 @@ class Positus implements IApiMedia "type" => "mensagem", "contact" => [ "name" => $msg['contacts'][0]['profile']['name'], - "number" => $msg['contacts'][0]['wa_id'] + "number" => $msg['contacts'][0]['wa_id'], + "matricula" => '' ], "mensagem" => [ "type" => $msg['messages'][0]['type'], - "content" => $msg['messages'][0]['text']['body'] + "content" => $msg['messages'][0]['text']['body'], + "id" => "425435243", + "dst" => $matricula, + "idAtendimento" => $atendimento, + "media" => $this->channel, + "datetime" => $msg['messages'][0]['timestamp'], + "status" => "sent" ] ]; + return json_encode($mensagem); } return ""; } + function enviarMsg($whatsapp, $mensagem) { $this->debug = debug_backtrace(); diff --git a/database/msg-text-positus.json b/database/msg-text-positus.json new file mode 100644 index 0000000..71b6349 --- /dev/null +++ b/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" + }] +} \ No newline at end of file From 8c2c407b0226e11dd3b343934fb1d701951ba82d Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Thu, 25 Nov 2021 17:09:30 -0400 Subject: [PATCH 013/144] remove vendor --- service/request.php | 29 - vendor/autoload.php | 7 - vendor/composer/ClassLoader.php | 481 -------- vendor/composer/InstalledVersions.php | 337 ------ vendor/composer/LICENSE | 21 - vendor/composer/autoload_classmap.php | 14 - vendor/composer/autoload_namespaces.php | 10 - vendor/composer/autoload_psr4.php | 30 - vendor/composer/autoload_real.php | 75 -- vendor/composer/autoload_static.php | 182 --- vendor/composer/installed.json | 1464 ----------------------- vendor/composer/installed.php | 209 ---- 12 files changed, 2859 deletions(-) delete mode 100644 service/request.php delete mode 100644 vendor/autoload.php delete mode 100644 vendor/composer/ClassLoader.php delete mode 100644 vendor/composer/InstalledVersions.php delete mode 100644 vendor/composer/LICENSE delete mode 100644 vendor/composer/autoload_classmap.php delete mode 100644 vendor/composer/autoload_namespaces.php delete mode 100644 vendor/composer/autoload_psr4.php delete mode 100644 vendor/composer/autoload_real.php delete mode 100644 vendor/composer/autoload_static.php delete mode 100644 vendor/composer/installed.json delete mode 100644 vendor/composer/installed.php diff --git a/service/request.php b/service/request.php deleted file mode 100644 index d9a3d50..0000000 --- a/service/request.php +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/php -q -findRamal('556581282842'); - -print_r($info); \ No newline at end of file diff --git a/vendor/autoload.php b/vendor/autoload.php deleted file mode 100644 index 4fff29b..0000000 --- a/vendor/autoload.php +++ /dev/null @@ -1,7 +0,0 @@ - - * Jordi Boggiano - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Composer\Autoload; - -/** - * ClassLoader implements a PSR-0, PSR-4 and classmap class loader. - * - * $loader = new \Composer\Autoload\ClassLoader(); - * - * // register classes with namespaces - * $loader->add('Symfony\Component', __DIR__.'/component'); - * $loader->add('Symfony', __DIR__.'/framework'); - * - * // activate the autoloader - * $loader->register(); - * - * // to enable searching the include path (eg. for PEAR packages) - * $loader->setUseIncludePath(true); - * - * In this example, if you try to use a class in the Symfony\Component - * namespace or one of its children (Symfony\Component\Console for instance), - * the autoloader will first look for the class under the component/ - * directory, and it will then fallback to the framework/ directory if not - * found before giving up. - * - * This class is loosely based on the Symfony UniversalClassLoader. - * - * @author Fabien Potencier - * @author Jordi Boggiano - * @see https://www.php-fig.org/psr/psr-0/ - * @see https://www.php-fig.org/psr/psr-4/ - */ -class ClassLoader -{ - private $vendorDir; - - // PSR-4 - private $prefixLengthsPsr4 = array(); - private $prefixDirsPsr4 = array(); - private $fallbackDirsPsr4 = array(); - - // PSR-0 - private $prefixesPsr0 = array(); - private $fallbackDirsPsr0 = array(); - - private $useIncludePath = false; - private $classMap = array(); - private $classMapAuthoritative = false; - private $missingClasses = array(); - private $apcuPrefix; - - private static $registeredLoaders = array(); - - public function __construct($vendorDir = null) - { - $this->vendorDir = $vendorDir; - } - - public function getPrefixes() - { - if (!empty($this->prefixesPsr0)) { - return call_user_func_array('array_merge', array_values($this->prefixesPsr0)); - } - - return array(); - } - - public function getPrefixesPsr4() - { - return $this->prefixDirsPsr4; - } - - public function getFallbackDirs() - { - return $this->fallbackDirsPsr0; - } - - public function getFallbackDirsPsr4() - { - return $this->fallbackDirsPsr4; - } - - public function getClassMap() - { - return $this->classMap; - } - - /** - * @param array $classMap Class to filename map - */ - public function addClassMap(array $classMap) - { - if ($this->classMap) { - $this->classMap = array_merge($this->classMap, $classMap); - } else { - $this->classMap = $classMap; - } - } - - /** - * Registers a set of PSR-0 directories for a given prefix, either - * appending or prepending to the ones previously set for this prefix. - * - * @param string $prefix The prefix - * @param array|string $paths The PSR-0 root directories - * @param bool $prepend Whether to prepend the directories - */ - public function add($prefix, $paths, $prepend = false) - { - if (!$prefix) { - if ($prepend) { - $this->fallbackDirsPsr0 = array_merge( - (array) $paths, - $this->fallbackDirsPsr0 - ); - } else { - $this->fallbackDirsPsr0 = array_merge( - $this->fallbackDirsPsr0, - (array) $paths - ); - } - - return; - } - - $first = $prefix[0]; - if (!isset($this->prefixesPsr0[$first][$prefix])) { - $this->prefixesPsr0[$first][$prefix] = (array) $paths; - - return; - } - if ($prepend) { - $this->prefixesPsr0[$first][$prefix] = array_merge( - (array) $paths, - $this->prefixesPsr0[$first][$prefix] - ); - } else { - $this->prefixesPsr0[$first][$prefix] = array_merge( - $this->prefixesPsr0[$first][$prefix], - (array) $paths - ); - } - } - - /** - * Registers a set of PSR-4 directories for a given namespace, either - * appending or prepending to the ones previously set for this namespace. - * - * @param string $prefix The prefix/namespace, with trailing '\\' - * @param array|string $paths The PSR-4 base directories - * @param bool $prepend Whether to prepend the directories - * - * @throws \InvalidArgumentException - */ - public function addPsr4($prefix, $paths, $prepend = false) - { - if (!$prefix) { - // Register directories for the root namespace. - if ($prepend) { - $this->fallbackDirsPsr4 = array_merge( - (array) $paths, - $this->fallbackDirsPsr4 - ); - } else { - $this->fallbackDirsPsr4 = array_merge( - $this->fallbackDirsPsr4, - (array) $paths - ); - } - } elseif (!isset($this->prefixDirsPsr4[$prefix])) { - // Register directories for a new namespace. - $length = strlen($prefix); - if ('\\' !== $prefix[$length - 1]) { - throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); - } - $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; - $this->prefixDirsPsr4[$prefix] = (array) $paths; - } elseif ($prepend) { - // Prepend directories for an already registered namespace. - $this->prefixDirsPsr4[$prefix] = array_merge( - (array) $paths, - $this->prefixDirsPsr4[$prefix] - ); - } else { - // Append directories for an already registered namespace. - $this->prefixDirsPsr4[$prefix] = array_merge( - $this->prefixDirsPsr4[$prefix], - (array) $paths - ); - } - } - - /** - * Registers a set of PSR-0 directories for a given prefix, - * replacing any others previously set for this prefix. - * - * @param string $prefix The prefix - * @param array|string $paths The PSR-0 base directories - */ - public function set($prefix, $paths) - { - if (!$prefix) { - $this->fallbackDirsPsr0 = (array) $paths; - } else { - $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths; - } - } - - /** - * Registers a set of PSR-4 directories for a given namespace, - * replacing any others previously set for this namespace. - * - * @param string $prefix The prefix/namespace, with trailing '\\' - * @param array|string $paths The PSR-4 base directories - * - * @throws \InvalidArgumentException - */ - public function setPsr4($prefix, $paths) - { - if (!$prefix) { - $this->fallbackDirsPsr4 = (array) $paths; - } else { - $length = strlen($prefix); - if ('\\' !== $prefix[$length - 1]) { - throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); - } - $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; - $this->prefixDirsPsr4[$prefix] = (array) $paths; - } - } - - /** - * Turns on searching the include path for class files. - * - * @param bool $useIncludePath - */ - public function setUseIncludePath($useIncludePath) - { - $this->useIncludePath = $useIncludePath; - } - - /** - * Can be used to check if the autoloader uses the include path to check - * for classes. - * - * @return bool - */ - public function getUseIncludePath() - { - return $this->useIncludePath; - } - - /** - * Turns off searching the prefix and fallback directories for classes - * that have not been registered with the class map. - * - * @param bool $classMapAuthoritative - */ - public function setClassMapAuthoritative($classMapAuthoritative) - { - $this->classMapAuthoritative = $classMapAuthoritative; - } - - /** - * Should class lookup fail if not found in the current class map? - * - * @return bool - */ - public function isClassMapAuthoritative() - { - return $this->classMapAuthoritative; - } - - /** - * APCu prefix to use to cache found/not-found classes, if the extension is enabled. - * - * @param string|null $apcuPrefix - */ - public function setApcuPrefix($apcuPrefix) - { - $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null; - } - - /** - * The APCu prefix in use, or null if APCu caching is not enabled. - * - * @return string|null - */ - public function getApcuPrefix() - { - return $this->apcuPrefix; - } - - /** - * Registers this instance as an autoloader. - * - * @param bool $prepend Whether to prepend the autoloader or not - */ - public function register($prepend = false) - { - spl_autoload_register(array($this, 'loadClass'), true, $prepend); - - if (null === $this->vendorDir) { - return; - } - - if ($prepend) { - self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders; - } else { - unset(self::$registeredLoaders[$this->vendorDir]); - self::$registeredLoaders[$this->vendorDir] = $this; - } - } - - /** - * Unregisters this instance as an autoloader. - */ - public function unregister() - { - spl_autoload_unregister(array($this, 'loadClass')); - - if (null !== $this->vendorDir) { - unset(self::$registeredLoaders[$this->vendorDir]); - } - } - - /** - * Loads the given class or interface. - * - * @param string $class The name of the class - * @return true|null True if loaded, null otherwise - */ - public function loadClass($class) - { - if ($file = $this->findFile($class)) { - includeFile($file); - - return true; - } - - return null; - } - - /** - * Finds the path to the file where the class is defined. - * - * @param string $class The name of the class - * - * @return string|false The path if found, false otherwise - */ - public function findFile($class) - { - // class map lookup - if (isset($this->classMap[$class])) { - return $this->classMap[$class]; - } - if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) { - return false; - } - if (null !== $this->apcuPrefix) { - $file = apcu_fetch($this->apcuPrefix.$class, $hit); - if ($hit) { - return $file; - } - } - - $file = $this->findFileWithExtension($class, '.php'); - - // Search for Hack files if we are running on HHVM - if (false === $file && defined('HHVM_VERSION')) { - $file = $this->findFileWithExtension($class, '.hh'); - } - - if (null !== $this->apcuPrefix) { - apcu_add($this->apcuPrefix.$class, $file); - } - - if (false === $file) { - // Remember that this class does not exist. - $this->missingClasses[$class] = true; - } - - return $file; - } - - /** - * Returns the currently registered loaders indexed by their corresponding vendor directories. - * - * @return self[] - */ - public static function getRegisteredLoaders() - { - return self::$registeredLoaders; - } - - private function findFileWithExtension($class, $ext) - { - // PSR-4 lookup - $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; - - $first = $class[0]; - if (isset($this->prefixLengthsPsr4[$first])) { - $subPath = $class; - while (false !== $lastPos = strrpos($subPath, '\\')) { - $subPath = substr($subPath, 0, $lastPos); - $search = $subPath . '\\'; - if (isset($this->prefixDirsPsr4[$search])) { - $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1); - foreach ($this->prefixDirsPsr4[$search] as $dir) { - if (file_exists($file = $dir . $pathEnd)) { - return $file; - } - } - } - } - } - - // PSR-4 fallback dirs - foreach ($this->fallbackDirsPsr4 as $dir) { - if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { - return $file; - } - } - - // PSR-0 lookup - if (false !== $pos = strrpos($class, '\\')) { - // namespaced class name - $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) - . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); - } else { - // PEAR-like class name - $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext; - } - - if (isset($this->prefixesPsr0[$first])) { - foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) { - if (0 === strpos($class, $prefix)) { - foreach ($dirs as $dir) { - if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { - return $file; - } - } - } - } - } - - // PSR-0 fallback dirs - foreach ($this->fallbackDirsPsr0 as $dir) { - if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { - return $file; - } - } - - // PSR-0 include paths. - if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { - return $file; - } - - return false; - } -} - -/** - * Scope isolated include. - * - * Prevents access to $this/self from included files. - */ -function includeFile($file) -{ - include $file; -} diff --git a/vendor/composer/InstalledVersions.php b/vendor/composer/InstalledVersions.php deleted file mode 100644 index b3a4e16..0000000 --- a/vendor/composer/InstalledVersions.php +++ /dev/null @@ -1,337 +0,0 @@ - - * Jordi Boggiano - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Composer; - -use Composer\Autoload\ClassLoader; -use Composer\Semver\VersionParser; - -/** - * This class is copied in every Composer installed project and available to all - * - * See also https://getcomposer.org/doc/07-runtime.md#installed-versions - * - * To require it's presence, you can require `composer-runtime-api ^2.0` - */ -class InstalledVersions -{ - private static $installed; - private static $canGetVendors; - private static $installedByVendor = array(); - - /** - * Returns a list of all package names which are present, either by being installed, replaced or provided - * - * @return string[] - * @psalm-return list - */ - public static function getInstalledPackages() - { - $packages = array(); - foreach (self::getInstalled() as $installed) { - $packages[] = array_keys($installed['versions']); - } - - if (1 === \count($packages)) { - return $packages[0]; - } - - return array_keys(array_flip(\call_user_func_array('array_merge', $packages))); - } - - /** - * Returns a list of all package names with a specific type e.g. 'library' - * - * @param string $type - * @return string[] - * @psalm-return list - */ - public static function getInstalledPackagesByType($type) - { - $packagesByType = array(); - - foreach (self::getInstalled() as $installed) { - foreach ($installed['versions'] as $name => $package) { - if (isset($package['type']) && $package['type'] === $type) { - $packagesByType[] = $name; - } - } - } - - return $packagesByType; - } - - /** - * Checks whether the given package is installed - * - * This also returns true if the package name is provided or replaced by another package - * - * @param string $packageName - * @param bool $includeDevRequirements - * @return bool - */ - public static function isInstalled($packageName, $includeDevRequirements = true) - { - foreach (self::getInstalled() as $installed) { - if (isset($installed['versions'][$packageName])) { - return $includeDevRequirements || empty($installed['versions'][$packageName]['dev_requirement']); - } - } - - return false; - } - - /** - * Checks whether the given package satisfies a version constraint - * - * e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call: - * - * Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3') - * - * @param VersionParser $parser Install composer/semver to have access to this class and functionality - * @param string $packageName - * @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package - * @return bool - */ - public static function satisfies(VersionParser $parser, $packageName, $constraint) - { - $constraint = $parser->parseConstraints($constraint); - $provided = $parser->parseConstraints(self::getVersionRanges($packageName)); - - return $provided->matches($constraint); - } - - /** - * Returns a version constraint representing all the range(s) which are installed for a given package - * - * It is easier to use this via isInstalled() with the $constraint argument if you need to check - * whether a given version of a package is installed, and not just whether it exists - * - * @param string $packageName - * @return string Version constraint usable with composer/semver - */ - public static function getVersionRanges($packageName) - { - foreach (self::getInstalled() as $installed) { - if (!isset($installed['versions'][$packageName])) { - continue; - } - - $ranges = array(); - if (isset($installed['versions'][$packageName]['pretty_version'])) { - $ranges[] = $installed['versions'][$packageName]['pretty_version']; - } - if (array_key_exists('aliases', $installed['versions'][$packageName])) { - $ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']); - } - if (array_key_exists('replaced', $installed['versions'][$packageName])) { - $ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']); - } - if (array_key_exists('provided', $installed['versions'][$packageName])) { - $ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']); - } - - return implode(' || ', $ranges); - } - - throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); - } - - /** - * @param string $packageName - * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present - */ - public static function getVersion($packageName) - { - foreach (self::getInstalled() as $installed) { - if (!isset($installed['versions'][$packageName])) { - continue; - } - - if (!isset($installed['versions'][$packageName]['version'])) { - return null; - } - - return $installed['versions'][$packageName]['version']; - } - - throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); - } - - /** - * @param string $packageName - * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present - */ - public static function getPrettyVersion($packageName) - { - foreach (self::getInstalled() as $installed) { - if (!isset($installed['versions'][$packageName])) { - continue; - } - - if (!isset($installed['versions'][$packageName]['pretty_version'])) { - return null; - } - - return $installed['versions'][$packageName]['pretty_version']; - } - - throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); - } - - /** - * @param string $packageName - * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference - */ - public static function getReference($packageName) - { - foreach (self::getInstalled() as $installed) { - if (!isset($installed['versions'][$packageName])) { - continue; - } - - if (!isset($installed['versions'][$packageName]['reference'])) { - return null; - } - - return $installed['versions'][$packageName]['reference']; - } - - throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); - } - - /** - * @param string $packageName - * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path. - */ - public static function getInstallPath($packageName) - { - foreach (self::getInstalled() as $installed) { - if (!isset($installed['versions'][$packageName])) { - continue; - } - - return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null; - } - - throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); - } - - /** - * @return array - * @psalm-return array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string} - */ - public static function getRootPackage() - { - $installed = self::getInstalled(); - - return $installed[0]['root']; - } - - /** - * Returns the raw installed.php data for custom implementations - * - * @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect. - * @return array[] - * @psalm-return array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string}, versions: array} - */ - public static function getRawData() - { - @trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED); - - if (null === self::$installed) { - // only require the installed.php file if this file is loaded from its dumped location, - // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937 - if (substr(__DIR__, -8, 1) !== 'C') { - self::$installed = include __DIR__ . '/installed.php'; - } else { - self::$installed = array(); - } - } - - return self::$installed; - } - - /** - * Returns the raw data of all installed.php which are currently loaded for custom implementations - * - * @return array[] - * @psalm-return list}> - */ - public static function getAllRawData() - { - return self::getInstalled(); - } - - /** - * Lets you reload the static array from another file - * - * This is only useful for complex integrations in which a project needs to use - * this class but then also needs to execute another project's autoloader in process, - * and wants to ensure both projects have access to their version of installed.php. - * - * A typical case would be PHPUnit, where it would need to make sure it reads all - * the data it needs from this class, then call reload() with - * `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure - * the project in which it runs can then also use this class safely, without - * interference between PHPUnit's dependencies and the project's dependencies. - * - * @param array[] $data A vendor/composer/installed.php data set - * @return void - * - * @psalm-param array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string}, versions: array} $data - */ - public static function reload($data) - { - self::$installed = $data; - self::$installedByVendor = array(); - } - - /** - * @return array[] - * @psalm-return list}> - */ - private static function getInstalled() - { - if (null === self::$canGetVendors) { - self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders'); - } - - $installed = array(); - - if (self::$canGetVendors) { - foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) { - if (isset(self::$installedByVendor[$vendorDir])) { - $installed[] = self::$installedByVendor[$vendorDir]; - } elseif (is_file($vendorDir.'/composer/installed.php')) { - $installed[] = self::$installedByVendor[$vendorDir] = require $vendorDir.'/composer/installed.php'; - if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) { - self::$installed = $installed[count($installed) - 1]; - } - } - } - } - - if (null === self::$installed) { - // only require the installed.php file if this file is loaded from its dumped location, - // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937 - if (substr(__DIR__, -8, 1) !== 'C') { - self::$installed = require __DIR__ . '/installed.php'; - } else { - self::$installed = array(); - } - } - $installed[] = self::$installed; - - return $installed; - } -} diff --git a/vendor/composer/LICENSE b/vendor/composer/LICENSE deleted file mode 100644 index f27399a..0000000 --- a/vendor/composer/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ - -Copyright (c) Nils Adermann, Jordi Boggiano - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is furnished -to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php deleted file mode 100644 index 4ebd53f..0000000 --- a/vendor/composer/autoload_classmap.php +++ /dev/null @@ -1,14 +0,0 @@ - $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Attribute.php', - 'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php', - 'Stringable' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Stringable.php', - 'UnhandledMatchError' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php', - 'ValueError' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/ValueError.php', -); diff --git a/vendor/composer/autoload_namespaces.php b/vendor/composer/autoload_namespaces.php deleted file mode 100644 index 02066fb..0000000 --- a/vendor/composer/autoload_namespaces.php +++ /dev/null @@ -1,10 +0,0 @@ - array($vendorDir . '/evenement/evenement/src'), -); diff --git a/vendor/composer/autoload_psr4.php b/vendor/composer/autoload_psr4.php deleted file mode 100644 index 74c1aad..0000000 --- a/vendor/composer/autoload_psr4.php +++ /dev/null @@ -1,30 +0,0 @@ - array($baseDir . '/websocket'), - 'service\\' => array($baseDir . '/service'), - 'scripts\\' => array($baseDir . '/scripts'), - 'app\\' => array($baseDir . '/app'), - 'WebSocket\\' => array($vendorDir . '/textalk/websocket/lib'), - 'Symfony\\Polyfill\\Php80\\' => array($vendorDir . '/symfony/polyfill-php80'), - 'Symfony\\Polyfill\\Mbstring\\' => array($vendorDir . '/symfony/polyfill-mbstring'), - 'Symfony\\Component\\Routing\\' => array($vendorDir . '/symfony/routing'), - 'Symfony\\Component\\HttpFoundation\\' => array($vendorDir . '/symfony/http-foundation'), - 'React\\Stream\\' => array($vendorDir . '/react/stream/src'), - 'React\\Socket\\' => array($vendorDir . '/react/socket/src'), - 'React\\Promise\\Timer\\' => array($vendorDir . '/react/promise-timer/src'), - 'React\\Promise\\' => array($vendorDir . '/react/promise/src'), - 'React\\EventLoop\\' => array($vendorDir . '/react/event-loop/src'), - 'React\\Dns\\' => array($vendorDir . '/react/dns/src'), - 'React\\Cache\\' => array($vendorDir . '/react/cache/src'), - 'Ratchet\\RFC6455\\' => array($vendorDir . '/ratchet/rfc6455/src'), - 'Ratchet\\' => array($vendorDir . '/cboden/ratchet/src/Ratchet'), - 'Psr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'), - 'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-message/src'), - 'GuzzleHttp\\Psr7\\' => array($vendorDir . '/guzzlehttp/psr7/src'), -); diff --git a/vendor/composer/autoload_real.php b/vendor/composer/autoload_real.php deleted file mode 100644 index 95fa6ca..0000000 --- a/vendor/composer/autoload_real.php +++ /dev/null @@ -1,75 +0,0 @@ -= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded()); - if ($useStaticLoader) { - require __DIR__ . '/autoload_static.php'; - - call_user_func(\Composer\Autoload\ComposerStaticInit16f85458e9b741f7f2b22e1ee483f627::getInitializer($loader)); - } else { - $map = require __DIR__ . '/autoload_namespaces.php'; - foreach ($map as $namespace => $path) { - $loader->set($namespace, $path); - } - - $map = require __DIR__ . '/autoload_psr4.php'; - foreach ($map as $namespace => $path) { - $loader->setPsr4($namespace, $path); - } - - $classMap = require __DIR__ . '/autoload_classmap.php'; - if ($classMap) { - $loader->addClassMap($classMap); - } - } - - $loader->register(true); - - if ($useStaticLoader) { - $includeFiles = Composer\Autoload\ComposerStaticInit16f85458e9b741f7f2b22e1ee483f627::$files; - } else { - $includeFiles = require __DIR__ . '/autoload_files.php'; - } - foreach ($includeFiles as $fileIdentifier => $file) { - composerRequire16f85458e9b741f7f2b22e1ee483f627($fileIdentifier, $file); - } - - return $loader; - } -} - -function composerRequire16f85458e9b741f7f2b22e1ee483f627($fileIdentifier, $file) -{ - if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { - require $file; - - $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true; - } -} diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php deleted file mode 100644 index 8348f54..0000000 --- a/vendor/composer/autoload_static.php +++ /dev/null @@ -1,182 +0,0 @@ - __DIR__ . '/..' . '/react/promise/src/functions_include.php', - '972fda704d680a3a53c68e34e193cb22' => __DIR__ . '/..' . '/react/promise-timer/src/functions_include.php', - '6e3fae29631ef280660b3cdad06f25a8' => __DIR__ . '/..' . '/symfony/deprecation-contracts/function.php', - 'a4a119a56e50fbb293281d9a48007e0e' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.php', - '7b11c4dc42b3b3023073cb14e519683c' => __DIR__ . '/..' . '/ralouphie/getallheaders/src/getallheaders.php', - '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php', - 'a0edc8309cc5e1d60e3047b5df6b7052' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/functions_include.php', - ); - - public static $prefixLengthsPsr4 = array ( - 'w' => - array ( - 'websocket\\' => 10, - ), - 's' => - array ( - 'service\\' => 8, - 'scripts\\' => 8, - ), - 'a' => - array ( - 'app\\' => 4, - ), - 'W' => - array ( - 'WebSocket\\' => 10, - ), - 'S' => - array ( - 'Symfony\\Polyfill\\Php80\\' => 23, - 'Symfony\\Polyfill\\Mbstring\\' => 26, - 'Symfony\\Component\\Routing\\' => 26, - 'Symfony\\Component\\HttpFoundation\\' => 33, - ), - 'R' => - array ( - 'React\\Stream\\' => 13, - 'React\\Socket\\' => 13, - 'React\\Promise\\Timer\\' => 20, - 'React\\Promise\\' => 14, - 'React\\EventLoop\\' => 16, - 'React\\Dns\\' => 10, - 'React\\Cache\\' => 12, - 'Ratchet\\RFC6455\\' => 16, - 'Ratchet\\' => 8, - ), - 'P' => - array ( - 'Psr\\Log\\' => 8, - 'Psr\\Http\\Message\\' => 17, - ), - 'G' => - array ( - 'GuzzleHttp\\Psr7\\' => 16, - ), - ); - - public static $prefixDirsPsr4 = array ( - 'websocket\\' => - array ( - 0 => __DIR__ . '/../..' . '/websocket', - ), - 'service\\' => - array ( - 0 => __DIR__ . '/../..' . '/service', - ), - 'scripts\\' => - array ( - 0 => __DIR__ . '/../..' . '/scripts', - ), - 'app\\' => - array ( - 0 => __DIR__ . '/../..' . '/app', - ), - 'WebSocket\\' => - array ( - 0 => __DIR__ . '/..' . '/textalk/websocket/lib', - ), - 'Symfony\\Polyfill\\Php80\\' => - array ( - 0 => __DIR__ . '/..' . '/symfony/polyfill-php80', - ), - 'Symfony\\Polyfill\\Mbstring\\' => - array ( - 0 => __DIR__ . '/..' . '/symfony/polyfill-mbstring', - ), - 'Symfony\\Component\\Routing\\' => - array ( - 0 => __DIR__ . '/..' . '/symfony/routing', - ), - 'Symfony\\Component\\HttpFoundation\\' => - array ( - 0 => __DIR__ . '/..' . '/symfony/http-foundation', - ), - 'React\\Stream\\' => - array ( - 0 => __DIR__ . '/..' . '/react/stream/src', - ), - 'React\\Socket\\' => - array ( - 0 => __DIR__ . '/..' . '/react/socket/src', - ), - 'React\\Promise\\Timer\\' => - array ( - 0 => __DIR__ . '/..' . '/react/promise-timer/src', - ), - 'React\\Promise\\' => - array ( - 0 => __DIR__ . '/..' . '/react/promise/src', - ), - 'React\\EventLoop\\' => - array ( - 0 => __DIR__ . '/..' . '/react/event-loop/src', - ), - 'React\\Dns\\' => - array ( - 0 => __DIR__ . '/..' . '/react/dns/src', - ), - 'React\\Cache\\' => - array ( - 0 => __DIR__ . '/..' . '/react/cache/src', - ), - 'Ratchet\\RFC6455\\' => - array ( - 0 => __DIR__ . '/..' . '/ratchet/rfc6455/src', - ), - 'Ratchet\\' => - array ( - 0 => __DIR__ . '/..' . '/cboden/ratchet/src/Ratchet', - ), - 'Psr\\Log\\' => - array ( - 0 => __DIR__ . '/..' . '/psr/log/Psr/Log', - ), - 'Psr\\Http\\Message\\' => - array ( - 0 => __DIR__ . '/..' . '/psr/http-message/src', - ), - 'GuzzleHttp\\Psr7\\' => - array ( - 0 => __DIR__ . '/..' . '/guzzlehttp/psr7/src', - ), - ); - - public static $prefixesPsr0 = array ( - 'E' => - array ( - 'Evenement' => - array ( - 0 => __DIR__ . '/..' . '/evenement/evenement/src', - ), - ), - ); - - public static $classMap = array ( - 'Attribute' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Attribute.php', - 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php', - 'Stringable' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Stringable.php', - 'UnhandledMatchError' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php', - 'ValueError' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/ValueError.php', - ); - - public static function getInitializer(ClassLoader $loader) - { - return \Closure::bind(function () use ($loader) { - $loader->prefixLengthsPsr4 = ComposerStaticInit16f85458e9b741f7f2b22e1ee483f627::$prefixLengthsPsr4; - $loader->prefixDirsPsr4 = ComposerStaticInit16f85458e9b741f7f2b22e1ee483f627::$prefixDirsPsr4; - $loader->prefixesPsr0 = ComposerStaticInit16f85458e9b741f7f2b22e1ee483f627::$prefixesPsr0; - $loader->classMap = ComposerStaticInit16f85458e9b741f7f2b22e1ee483f627::$classMap; - - }, null, ClassLoader::class); - } -} diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json deleted file mode 100644 index b3e94c2..0000000 --- a/vendor/composer/installed.json +++ /dev/null @@ -1,1464 +0,0 @@ -{ - "packages": [ - { - "name": "cboden/ratchet", - "version": "v0.4.3", - "version_normalized": "0.4.3.0", - "source": { - "type": "git", - "url": "https://github.com/ratchetphp/Ratchet.git", - "reference": "466a0ecc83209c75b76645eb823401b5c52e5f21" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/ratchetphp/Ratchet/zipball/466a0ecc83209c75b76645eb823401b5c52e5f21", - "reference": "466a0ecc83209c75b76645eb823401b5c52e5f21", - "shasum": "" - }, - "require": { - "guzzlehttp/psr7": "^1.0", - "php": ">=5.4.2", - "ratchet/rfc6455": "^0.3", - "react/socket": "^1.0 || ^0.8 || ^0.7 || ^0.6 || ^0.5", - "symfony/http-foundation": "^2.6|^3.0|^4.0|^5.0", - "symfony/routing": "^2.6|^3.0|^4.0|^5.0" - }, - "require-dev": { - "phpunit/phpunit": "~4.8" - }, - "time": "2020-07-07T15:50:14+00:00", - "type": "library", - "installation-source": "dist", - "autoload": { - "psr-4": { - "Ratchet\\": "src/Ratchet" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Chris Boden", - "email": "cboden@gmail.com", - "role": "Developer" - }, - { - "name": "Matt Bonneau", - "role": "Developer" - } - ], - "description": "PHP WebSocket library", - "homepage": "http://socketo.me", - "keywords": [ - "Ratchet", - "WebSockets", - "server", - "sockets", - "websocket" - ], - "support": { - "chat": "https://gitter.im/reactphp/reactphp", - "issues": "https://github.com/ratchetphp/Ratchet/issues", - "source": "https://github.com/ratchetphp/Ratchet/tree/master" - }, - "install-path": "../cboden/ratchet" - }, - { - "name": "evenement/evenement", - "version": "v3.0.1", - "version_normalized": "3.0.1.0", - "source": { - "type": "git", - "url": "https://github.com/igorw/evenement.git", - "reference": "531bfb9d15f8aa57454f5f0285b18bec903b8fb7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/igorw/evenement/zipball/531bfb9d15f8aa57454f5f0285b18bec903b8fb7", - "reference": "531bfb9d15f8aa57454f5f0285b18bec903b8fb7", - "shasum": "" - }, - "require": { - "php": ">=7.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.0" - }, - "time": "2017-07-23T21:35:13+00:00", - "type": "library", - "installation-source": "dist", - "autoload": { - "psr-0": { - "Evenement": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Igor Wiedler", - "email": "igor@wiedler.ch" - } - ], - "description": "Événement is a very simple event dispatching library for PHP", - "keywords": [ - "event-dispatcher", - "event-emitter" - ], - "support": { - "issues": "https://github.com/igorw/evenement/issues", - "source": "https://github.com/igorw/evenement/tree/master" - }, - "install-path": "../evenement/evenement" - }, - { - "name": "guzzlehttp/psr7", - "version": "1.8.3", - "version_normalized": "1.8.3.0", - "source": { - "type": "git", - "url": "https://github.com/guzzle/psr7.git", - "reference": "1afdd860a2566ed3c2b0b4a3de6e23434a79ec85" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/1afdd860a2566ed3c2b0b4a3de6e23434a79ec85", - "reference": "1afdd860a2566ed3c2b0b4a3de6e23434a79ec85", - "shasum": "" - }, - "require": { - "php": ">=5.4.0", - "psr/http-message": "~1.0", - "ralouphie/getallheaders": "^2.0.5 || ^3.0.0" - }, - "provide": { - "psr/http-message-implementation": "1.0" - }, - "require-dev": { - "ext-zlib": "*", - "phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.8 || ^9.3.10" - }, - "suggest": { - "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" - }, - "time": "2021-10-05T13:56:00+00:00", - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.7-dev" - } - }, - "installation-source": "dist", - "autoload": { - "psr-4": { - "GuzzleHttp\\Psr7\\": "src/" - }, - "files": [ - "src/functions_include.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Graham Campbell", - "email": "hello@gjcampbell.co.uk", - "homepage": "https://github.com/GrahamCampbell" - }, - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - }, - { - "name": "George Mponos", - "email": "gmponos@gmail.com", - "homepage": "https://github.com/gmponos" - }, - { - "name": "Tobias Nyholm", - "email": "tobias.nyholm@gmail.com", - "homepage": "https://github.com/Nyholm" - }, - { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com", - "homepage": "https://github.com/sagikazarmark" - }, - { - "name": "Tobias Schultze", - "email": "webmaster@tubo-world.de", - "homepage": "https://github.com/Tobion" - } - ], - "description": "PSR-7 message implementation that also provides common utility methods", - "keywords": [ - "http", - "message", - "psr-7", - "request", - "response", - "stream", - "uri", - "url" - ], - "support": { - "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/1.8.3" - }, - "funding": [ - { - "url": "https://github.com/GrahamCampbell", - "type": "github" - }, - { - "url": "https://github.com/Nyholm", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", - "type": "tidelift" - } - ], - "install-path": "../guzzlehttp/psr7" - }, - { - "name": "psr/http-message", - "version": "1.0.1", - "version_normalized": "1.0.1.0", - "source": { - "type": "git", - "url": "https://github.com/php-fig/http-message.git", - "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", - "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "time": "2016-08-06T14:39:51+00:00", - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "installation-source": "dist", - "autoload": { - "psr-4": { - "Psr\\Http\\Message\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" - } - ], - "description": "Common interface for HTTP messages", - "homepage": "https://github.com/php-fig/http-message", - "keywords": [ - "http", - "http-message", - "psr", - "psr-7", - "request", - "response" - ], - "support": { - "source": "https://github.com/php-fig/http-message/tree/master" - }, - "install-path": "../psr/http-message" - }, - { - "name": "psr/log", - "version": "1.1.4", - "version_normalized": "1.1.4.0", - "source": { - "type": "git", - "url": "https://github.com/php-fig/log.git", - "reference": "d49695b909c3b7628b6289db5479a1c204601f11" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", - "reference": "d49695b909c3b7628b6289db5479a1c204601f11", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "time": "2021-05-03T11:20:27+00:00", - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1.x-dev" - } - }, - "installation-source": "dist", - "autoload": { - "psr-4": { - "Psr\\Log\\": "Psr/Log/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common interface for logging libraries", - "homepage": "https://github.com/php-fig/log", - "keywords": [ - "log", - "psr", - "psr-3" - ], - "support": { - "source": "https://github.com/php-fig/log/tree/1.1.4" - }, - "install-path": "../psr/log" - }, - { - "name": "ralouphie/getallheaders", - "version": "3.0.3", - "version_normalized": "3.0.3.0", - "source": { - "type": "git", - "url": "https://github.com/ralouphie/getallheaders.git", - "reference": "120b605dfeb996808c31b6477290a714d356e822" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", - "reference": "120b605dfeb996808c31b6477290a714d356e822", - "shasum": "" - }, - "require": { - "php": ">=5.6" - }, - "require-dev": { - "php-coveralls/php-coveralls": "^2.1", - "phpunit/phpunit": "^5 || ^6.5" - }, - "time": "2019-03-08T08:55:37+00:00", - "type": "library", - "installation-source": "dist", - "autoload": { - "files": [ - "src/getallheaders.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ralph Khattar", - "email": "ralph.khattar@gmail.com" - } - ], - "description": "A polyfill for getallheaders.", - "support": { - "issues": "https://github.com/ralouphie/getallheaders/issues", - "source": "https://github.com/ralouphie/getallheaders/tree/develop" - }, - "install-path": "../ralouphie/getallheaders" - }, - { - "name": "ratchet/rfc6455", - "version": "v0.3", - "version_normalized": "0.3.0.0", - "source": { - "type": "git", - "url": "https://github.com/ratchetphp/RFC6455.git", - "reference": "c8651c7938651c2d55f5d8c2422ac5e57a183341" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/ratchetphp/RFC6455/zipball/c8651c7938651c2d55f5d8c2422ac5e57a183341", - "reference": "c8651c7938651c2d55f5d8c2422ac5e57a183341", - "shasum": "" - }, - "require": { - "guzzlehttp/psr7": "^1.0", - "php": ">=5.4.2" - }, - "require-dev": { - "phpunit/phpunit": "5.7.*", - "react/socket": "^1.3" - }, - "time": "2020-05-15T18:31:24+00:00", - "type": "library", - "installation-source": "dist", - "autoload": { - "psr-4": { - "Ratchet\\RFC6455\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Chris Boden", - "email": "cboden@gmail.com", - "role": "Developer" - }, - { - "name": "Matt Bonneau", - "role": "Developer" - } - ], - "description": "RFC6455 WebSocket protocol handler", - "homepage": "http://socketo.me", - "keywords": [ - "WebSockets", - "rfc6455", - "websocket" - ], - "support": { - "chat": "https://gitter.im/reactphp/reactphp", - "issues": "https://github.com/ratchetphp/RFC6455/issues", - "source": "https://github.com/ratchetphp/RFC6455/tree/v0.3" - }, - "install-path": "../ratchet/rfc6455" - }, - { - "name": "react/cache", - "version": "v1.1.1", - "version_normalized": "1.1.1.0", - "source": { - "type": "git", - "url": "https://github.com/reactphp/cache.git", - "reference": "4bf736a2cccec7298bdf745db77585966fc2ca7e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/reactphp/cache/zipball/4bf736a2cccec7298bdf745db77585966fc2ca7e", - "reference": "4bf736a2cccec7298bdf745db77585966fc2ca7e", - "shasum": "" - }, - "require": { - "php": ">=5.3.0", - "react/promise": "^3.0 || ^2.0 || ^1.1" - }, - "require-dev": { - "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35" - }, - "time": "2021-02-02T06:47:52+00:00", - "type": "library", - "installation-source": "dist", - "autoload": { - "psr-4": { - "React\\Cache\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Christian Lück", - "email": "christian@clue.engineering", - "homepage": "https://clue.engineering/" - }, - { - "name": "Cees-Jan Kiewiet", - "email": "reactphp@ceesjankiewiet.nl", - "homepage": "https://wyrihaximus.net/" - }, - { - "name": "Jan Sorgalla", - "email": "jsorgalla@gmail.com", - "homepage": "https://sorgalla.com/" - }, - { - "name": "Chris Boden", - "email": "cboden@gmail.com", - "homepage": "https://cboden.dev/" - } - ], - "description": "Async, Promise-based cache interface for ReactPHP", - "keywords": [ - "cache", - "caching", - "promise", - "reactphp" - ], - "support": { - "issues": "https://github.com/reactphp/cache/issues", - "source": "https://github.com/reactphp/cache/tree/v1.1.1" - }, - "funding": [ - { - "url": "https://github.com/WyriHaximus", - "type": "github" - }, - { - "url": "https://github.com/clue", - "type": "github" - } - ], - "install-path": "../react/cache" - }, - { - "name": "react/dns", - "version": "v1.8.0", - "version_normalized": "1.8.0.0", - "source": { - "type": "git", - "url": "https://github.com/reactphp/dns.git", - "reference": "2a5a74ab751e53863b45fb87e1d3913884f88248" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/reactphp/dns/zipball/2a5a74ab751e53863b45fb87e1d3913884f88248", - "reference": "2a5a74ab751e53863b45fb87e1d3913884f88248", - "shasum": "" - }, - "require": { - "php": ">=5.3.0", - "react/cache": "^1.0 || ^0.6 || ^0.5", - "react/event-loop": "^1.2", - "react/promise": "^3.0 || ^2.7 || ^1.2.1", - "react/promise-timer": "^1.2" - }, - "require-dev": { - "clue/block-react": "^1.2", - "phpunit/phpunit": "^9.3 || ^4.8.35" - }, - "time": "2021-07-11T12:40:34+00:00", - "type": "library", - "installation-source": "dist", - "autoload": { - "psr-4": { - "React\\Dns\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Christian Lück", - "email": "christian@clue.engineering", - "homepage": "https://clue.engineering/" - }, - { - "name": "Cees-Jan Kiewiet", - "email": "reactphp@ceesjankiewiet.nl", - "homepage": "https://wyrihaximus.net/" - }, - { - "name": "Jan Sorgalla", - "email": "jsorgalla@gmail.com", - "homepage": "https://sorgalla.com/" - }, - { - "name": "Chris Boden", - "email": "cboden@gmail.com", - "homepage": "https://cboden.dev/" - } - ], - "description": "Async DNS resolver for ReactPHP", - "keywords": [ - "async", - "dns", - "dns-resolver", - "reactphp" - ], - "support": { - "issues": "https://github.com/reactphp/dns/issues", - "source": "https://github.com/reactphp/dns/tree/v1.8.0" - }, - "funding": [ - { - "url": "https://github.com/WyriHaximus", - "type": "github" - }, - { - "url": "https://github.com/clue", - "type": "github" - } - ], - "install-path": "../react/dns" - }, - { - "name": "react/event-loop", - "version": "v1.2.0", - "version_normalized": "1.2.0.0", - "source": { - "type": "git", - "url": "https://github.com/reactphp/event-loop.git", - "reference": "be6dee480fc4692cec0504e65eb486e3be1aa6f2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/reactphp/event-loop/zipball/be6dee480fc4692cec0504e65eb486e3be1aa6f2", - "reference": "be6dee480fc4692cec0504e65eb486e3be1aa6f2", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35" - }, - "suggest": { - "ext-event": "~1.0 for ExtEventLoop", - "ext-pcntl": "For signal handling support when using the StreamSelectLoop", - "ext-uv": "* for ExtUvLoop" - }, - "time": "2021-07-11T12:31:24+00:00", - "type": "library", - "installation-source": "dist", - "autoload": { - "psr-4": { - "React\\EventLoop\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Christian Lück", - "email": "christian@clue.engineering", - "homepage": "https://clue.engineering/" - }, - { - "name": "Cees-Jan Kiewiet", - "email": "reactphp@ceesjankiewiet.nl", - "homepage": "https://wyrihaximus.net/" - }, - { - "name": "Jan Sorgalla", - "email": "jsorgalla@gmail.com", - "homepage": "https://sorgalla.com/" - }, - { - "name": "Chris Boden", - "email": "cboden@gmail.com", - "homepage": "https://cboden.dev/" - } - ], - "description": "ReactPHP's core reactor event loop that libraries can use for evented I/O.", - "keywords": [ - "asynchronous", - "event-loop" - ], - "support": { - "issues": "https://github.com/reactphp/event-loop/issues", - "source": "https://github.com/reactphp/event-loop/tree/v1.2.0" - }, - "funding": [ - { - "url": "https://github.com/WyriHaximus", - "type": "github" - }, - { - "url": "https://github.com/clue", - "type": "github" - } - ], - "install-path": "../react/event-loop" - }, - { - "name": "react/promise", - "version": "v2.8.0", - "version_normalized": "2.8.0.0", - "source": { - "type": "git", - "url": "https://github.com/reactphp/promise.git", - "reference": "f3cff96a19736714524ca0dd1d4130de73dbbbc4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/reactphp/promise/zipball/f3cff96a19736714524ca0dd1d4130de73dbbbc4", - "reference": "f3cff96a19736714524ca0dd1d4130de73dbbbc4", - "shasum": "" - }, - "require": { - "php": ">=5.4.0" - }, - "require-dev": { - "phpunit/phpunit": "^7.0 || ^6.5 || ^5.7 || ^4.8.36" - }, - "time": "2020-05-12T15:16:56+00:00", - "type": "library", - "installation-source": "dist", - "autoload": { - "psr-4": { - "React\\Promise\\": "src/" - }, - "files": [ - "src/functions_include.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jan Sorgalla", - "email": "jsorgalla@gmail.com" - } - ], - "description": "A lightweight implementation of CommonJS Promises/A for PHP", - "keywords": [ - "promise", - "promises" - ], - "support": { - "issues": "https://github.com/reactphp/promise/issues", - "source": "https://github.com/reactphp/promise/tree/v2.8.0" - }, - "install-path": "../react/promise" - }, - { - "name": "react/promise-timer", - "version": "v1.7.0", - "version_normalized": "1.7.0.0", - "source": { - "type": "git", - "url": "https://github.com/reactphp/promise-timer.git", - "reference": "607dd79990e32fcb402cb0a176b4a4be12f97e7c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/reactphp/promise-timer/zipball/607dd79990e32fcb402cb0a176b4a4be12f97e7c", - "reference": "607dd79990e32fcb402cb0a176b4a4be12f97e7c", - "shasum": "" - }, - "require": { - "php": ">=5.3", - "react/event-loop": "^1.2", - "react/promise": "^3.0 || ^2.7.0 || ^1.2.1" - }, - "require-dev": { - "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35" - }, - "time": "2021-07-11T13:08:51+00:00", - "type": "library", - "installation-source": "dist", - "autoload": { - "psr-4": { - "React\\Promise\\Timer\\": "src/" - }, - "files": [ - "src/functions_include.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Christian Lück", - "email": "christian@clue.engineering", - "homepage": "https://clue.engineering/" - }, - { - "name": "Cees-Jan Kiewiet", - "email": "reactphp@ceesjankiewiet.nl", - "homepage": "https://wyrihaximus.net/" - }, - { - "name": "Jan Sorgalla", - "email": "jsorgalla@gmail.com", - "homepage": "https://sorgalla.com/" - }, - { - "name": "Chris Boden", - "email": "cboden@gmail.com", - "homepage": "https://cboden.dev/" - } - ], - "description": "A trivial implementation of timeouts for Promises, built on top of ReactPHP.", - "homepage": "https://github.com/reactphp/promise-timer", - "keywords": [ - "async", - "event-loop", - "promise", - "reactphp", - "timeout", - "timer" - ], - "support": { - "issues": "https://github.com/reactphp/promise-timer/issues", - "source": "https://github.com/reactphp/promise-timer/tree/v1.7.0" - }, - "funding": [ - { - "url": "https://github.com/WyriHaximus", - "type": "github" - }, - { - "url": "https://github.com/clue", - "type": "github" - } - ], - "install-path": "../react/promise-timer" - }, - { - "name": "react/socket", - "version": "v1.9.0", - "version_normalized": "1.9.0.0", - "source": { - "type": "git", - "url": "https://github.com/reactphp/socket.git", - "reference": "aa6e3f8ebcd6dec3ad1ee92a449b4cc341994001" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/reactphp/socket/zipball/aa6e3f8ebcd6dec3ad1ee92a449b4cc341994001", - "reference": "aa6e3f8ebcd6dec3ad1ee92a449b4cc341994001", - "shasum": "" - }, - "require": { - "evenement/evenement": "^3.0 || ^2.0 || ^1.0", - "php": ">=5.3.0", - "react/dns": "^1.8", - "react/event-loop": "^1.2", - "react/promise": "^2.6.0 || ^1.2.1", - "react/promise-timer": "^1.4.0", - "react/stream": "^1.2" - }, - "require-dev": { - "clue/block-react": "^1.2", - "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35", - "react/promise-stream": "^1.2" - }, - "time": "2021-08-03T12:37:06+00:00", - "type": "library", - "installation-source": "dist", - "autoload": { - "psr-4": { - "React\\Socket\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Christian Lück", - "email": "christian@clue.engineering", - "homepage": "https://clue.engineering/" - }, - { - "name": "Cees-Jan Kiewiet", - "email": "reactphp@ceesjankiewiet.nl", - "homepage": "https://wyrihaximus.net/" - }, - { - "name": "Jan Sorgalla", - "email": "jsorgalla@gmail.com", - "homepage": "https://sorgalla.com/" - }, - { - "name": "Chris Boden", - "email": "cboden@gmail.com", - "homepage": "https://cboden.dev/" - } - ], - "description": "Async, streaming plaintext TCP/IP and secure TLS socket server and client connections for ReactPHP", - "keywords": [ - "Connection", - "Socket", - "async", - "reactphp", - "stream" - ], - "support": { - "issues": "https://github.com/reactphp/socket/issues", - "source": "https://github.com/reactphp/socket/tree/v1.9.0" - }, - "funding": [ - { - "url": "https://github.com/WyriHaximus", - "type": "github" - }, - { - "url": "https://github.com/clue", - "type": "github" - } - ], - "install-path": "../react/socket" - }, - { - "name": "react/stream", - "version": "v1.2.0", - "version_normalized": "1.2.0.0", - "source": { - "type": "git", - "url": "https://github.com/reactphp/stream.git", - "reference": "7a423506ee1903e89f1e08ec5f0ed430ff784ae9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/reactphp/stream/zipball/7a423506ee1903e89f1e08ec5f0ed430ff784ae9", - "reference": "7a423506ee1903e89f1e08ec5f0ed430ff784ae9", - "shasum": "" - }, - "require": { - "evenement/evenement": "^3.0 || ^2.0 || ^1.0", - "php": ">=5.3.8", - "react/event-loop": "^1.2" - }, - "require-dev": { - "clue/stream-filter": "~1.2", - "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35" - }, - "time": "2021-07-11T12:37:55+00:00", - "type": "library", - "installation-source": "dist", - "autoload": { - "psr-4": { - "React\\Stream\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Christian Lück", - "email": "christian@clue.engineering", - "homepage": "https://clue.engineering/" - }, - { - "name": "Cees-Jan Kiewiet", - "email": "reactphp@ceesjankiewiet.nl", - "homepage": "https://wyrihaximus.net/" - }, - { - "name": "Jan Sorgalla", - "email": "jsorgalla@gmail.com", - "homepage": "https://sorgalla.com/" - }, - { - "name": "Chris Boden", - "email": "cboden@gmail.com", - "homepage": "https://cboden.dev/" - } - ], - "description": "Event-driven readable and writable streams for non-blocking I/O in ReactPHP", - "keywords": [ - "event-driven", - "io", - "non-blocking", - "pipe", - "reactphp", - "readable", - "stream", - "writable" - ], - "support": { - "issues": "https://github.com/reactphp/stream/issues", - "source": "https://github.com/reactphp/stream/tree/v1.2.0" - }, - "funding": [ - { - "url": "https://github.com/WyriHaximus", - "type": "github" - }, - { - "url": "https://github.com/clue", - "type": "github" - } - ], - "install-path": "../react/stream" - }, - { - "name": "symfony/deprecation-contracts", - "version": "v2.4.0", - "version_normalized": "2.4.0.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "5f38c8804a9e97d23e0c8d63341088cd8a22d627" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/5f38c8804a9e97d23e0c8d63341088cd8a22d627", - "reference": "5f38c8804a9e97d23e0c8d63341088cd8a22d627", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "time": "2021-03-23T23:28:01+00:00", - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "2.4-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" - } - }, - "installation-source": "dist", - "autoload": { - "files": [ - "function.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "A generic function and convention to trigger deprecation notices", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v2.4.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "install-path": "../symfony/deprecation-contracts" - }, - { - "name": "symfony/http-foundation", - "version": "v5.3.10", - "version_normalized": "5.3.10.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/http-foundation.git", - "reference": "9f34f02e8a5fdc7a56bafe011cea1ce97300e54c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/9f34f02e8a5fdc7a56bafe011cea1ce97300e54c", - "reference": "9f34f02e8a5fdc7a56bafe011cea1ce97300e54c", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1", - "symfony/polyfill-mbstring": "~1.1", - "symfony/polyfill-php80": "^1.16" - }, - "require-dev": { - "predis/predis": "~1.0", - "symfony/cache": "^4.4|^5.0", - "symfony/expression-language": "^4.4|^5.0", - "symfony/mime": "^4.4|^5.0" - }, - "suggest": { - "symfony/mime": "To use the file extension guesser" - }, - "time": "2021-10-11T15:41:55+00:00", - "type": "library", - "installation-source": "dist", - "autoload": { - "psr-4": { - "Symfony\\Component\\HttpFoundation\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Defines an object-oriented layer for the HTTP specification", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/http-foundation/tree/v5.3.10" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "install-path": "../symfony/http-foundation" - }, - { - "name": "symfony/polyfill-mbstring", - "version": "v1.23.1", - "version_normalized": "1.23.1.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "9174a3d80210dca8daa7f31fec659150bbeabfc6" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9174a3d80210dca8daa7f31fec659150bbeabfc6", - "reference": "9174a3d80210dca8daa7f31fec659150bbeabfc6", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "suggest": { - "ext-mbstring": "For best performance" - }, - "time": "2021-05-27T12:26:48+00:00", - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.23-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "installation-source": "dist", - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Mbstring\\": "" - }, - "files": [ - "bootstrap.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for the Mbstring extension", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "mbstring", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.23.1" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "install-path": "../symfony/polyfill-mbstring" - }, - { - "name": "symfony/polyfill-php80", - "version": "v1.23.1", - "version_normalized": "1.23.1.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "1100343ed1a92e3a38f9ae122fc0eb21602547be" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/1100343ed1a92e3a38f9ae122fc0eb21602547be", - "reference": "1100343ed1a92e3a38f9ae122fc0eb21602547be", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "time": "2021-07-28T13:41:28+00:00", - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.23-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "installation-source": "dist", - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Php80\\": "" - }, - "files": [ - "bootstrap.php" - ], - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ion Bazan", - "email": "ion.bazan@gmail.com" - }, - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.23.1" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "install-path": "../symfony/polyfill-php80" - }, - { - "name": "symfony/routing", - "version": "v5.3.7", - "version_normalized": "5.3.7.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/routing.git", - "reference": "be865017746fe869007d94220ad3f5297951811b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/be865017746fe869007d94220ad3f5297951811b", - "reference": "be865017746fe869007d94220ad3f5297951811b", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1", - "symfony/polyfill-php80": "^1.16" - }, - "conflict": { - "doctrine/annotations": "<1.12", - "symfony/config": "<5.3", - "symfony/dependency-injection": "<4.4", - "symfony/yaml": "<4.4" - }, - "require-dev": { - "doctrine/annotations": "^1.12", - "psr/log": "^1|^2|^3", - "symfony/config": "^5.3", - "symfony/dependency-injection": "^4.4|^5.0", - "symfony/expression-language": "^4.4|^5.0", - "symfony/http-foundation": "^4.4|^5.0", - "symfony/yaml": "^4.4|^5.0" - }, - "suggest": { - "symfony/config": "For using the all-in-one router or any loader", - "symfony/expression-language": "For using expression matching", - "symfony/http-foundation": "For using a Symfony Request object", - "symfony/yaml": "For using the YAML loader" - }, - "time": "2021-08-04T21:42:42+00:00", - "type": "library", - "installation-source": "dist", - "autoload": { - "psr-4": { - "Symfony\\Component\\Routing\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Maps an HTTP request to a set of configuration variables", - "homepage": "https://symfony.com", - "keywords": [ - "router", - "routing", - "uri", - "url" - ], - "support": { - "source": "https://github.com/symfony/routing/tree/v5.3.7" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "install-path": "../symfony/routing" - }, - { - "name": "textalk/websocket", - "version": "1.5.5", - "version_normalized": "1.5.5.0", - "source": { - "type": "git", - "url": "https://github.com/Textalk/websocket-php.git", - "reference": "846542f82658132cd36acb7a7e8ce0f03960c295" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Textalk/websocket-php/zipball/846542f82658132cd36acb7a7e8ce0f03960c295", - "reference": "846542f82658132cd36acb7a7e8ce0f03960c295", - "shasum": "" - }, - "require": { - "php": "^7.2 | ^8.0", - "psr/log": "^1 | ^2 | ^3" - }, - "require-dev": { - "php-coveralls/php-coveralls": "^2.0", - "phpunit/phpunit": "^8.0|^9.0", - "squizlabs/php_codesniffer": "^3.5" - }, - "time": "2021-08-07T10:21:40+00:00", - "type": "library", - "installation-source": "dist", - "autoload": { - "psr-4": { - "WebSocket\\": "lib" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "ISC" - ], - "authors": [ - { - "name": "Fredrik Liljegren" - }, - { - "name": "Sören Jensen", - "email": "soren@abicart.se" - } - ], - "description": "WebSocket client and server", - "support": { - "issues": "https://github.com/Textalk/websocket-php/issues", - "source": "https://github.com/Textalk/websocket-php/tree/1.5.5" - }, - "install-path": "../textalk/websocket" - } - ], - "dev": true, - "dev-package-names": [] -} diff --git a/vendor/composer/installed.php b/vendor/composer/installed.php deleted file mode 100644 index 8370c12..0000000 --- a/vendor/composer/installed.php +++ /dev/null @@ -1,209 +0,0 @@ - array( - 'pretty_version' => 'dev-main', - 'version' => 'dev-main', - 'type' => 'library', - 'install_path' => __DIR__ . '/../../', - 'aliases' => array(), - 'reference' => '5c34ab7b32db21ad29865a50f9f49a9cdeb9c830', - 'name' => 'simplesip/whatsapp', - 'dev' => true, - ), - 'versions' => array( - 'cboden/ratchet' => array( - 'pretty_version' => 'v0.4.3', - 'version' => '0.4.3.0', - 'type' => 'library', - 'install_path' => __DIR__ . '/../cboden/ratchet', - 'aliases' => array(), - 'reference' => '466a0ecc83209c75b76645eb823401b5c52e5f21', - 'dev_requirement' => false, - ), - 'evenement/evenement' => array( - 'pretty_version' => 'v3.0.1', - 'version' => '3.0.1.0', - 'type' => 'library', - 'install_path' => __DIR__ . '/../evenement/evenement', - 'aliases' => array(), - 'reference' => '531bfb9d15f8aa57454f5f0285b18bec903b8fb7', - 'dev_requirement' => false, - ), - 'guzzlehttp/psr7' => array( - 'pretty_version' => '1.8.3', - 'version' => '1.8.3.0', - 'type' => 'library', - 'install_path' => __DIR__ . '/../guzzlehttp/psr7', - 'aliases' => array(), - 'reference' => '1afdd860a2566ed3c2b0b4a3de6e23434a79ec85', - 'dev_requirement' => false, - ), - 'psr/http-message' => array( - 'pretty_version' => '1.0.1', - 'version' => '1.0.1.0', - 'type' => 'library', - 'install_path' => __DIR__ . '/../psr/http-message', - 'aliases' => array(), - 'reference' => 'f6561bf28d520154e4b0ec72be95418abe6d9363', - 'dev_requirement' => false, - ), - 'psr/http-message-implementation' => array( - 'dev_requirement' => false, - 'provided' => array( - 0 => '1.0', - ), - ), - 'psr/log' => array( - 'pretty_version' => '1.1.4', - 'version' => '1.1.4.0', - 'type' => 'library', - 'install_path' => __DIR__ . '/../psr/log', - 'aliases' => array(), - 'reference' => 'd49695b909c3b7628b6289db5479a1c204601f11', - 'dev_requirement' => false, - ), - 'ralouphie/getallheaders' => array( - 'pretty_version' => '3.0.3', - 'version' => '3.0.3.0', - 'type' => 'library', - 'install_path' => __DIR__ . '/../ralouphie/getallheaders', - 'aliases' => array(), - 'reference' => '120b605dfeb996808c31b6477290a714d356e822', - 'dev_requirement' => false, - ), - 'ratchet/rfc6455' => array( - 'pretty_version' => 'v0.3', - 'version' => '0.3.0.0', - 'type' => 'library', - 'install_path' => __DIR__ . '/../ratchet/rfc6455', - 'aliases' => array(), - 'reference' => 'c8651c7938651c2d55f5d8c2422ac5e57a183341', - 'dev_requirement' => false, - ), - 'react/cache' => array( - 'pretty_version' => 'v1.1.1', - 'version' => '1.1.1.0', - 'type' => 'library', - 'install_path' => __DIR__ . '/../react/cache', - 'aliases' => array(), - 'reference' => '4bf736a2cccec7298bdf745db77585966fc2ca7e', - 'dev_requirement' => false, - ), - 'react/dns' => array( - 'pretty_version' => 'v1.8.0', - 'version' => '1.8.0.0', - 'type' => 'library', - 'install_path' => __DIR__ . '/../react/dns', - 'aliases' => array(), - 'reference' => '2a5a74ab751e53863b45fb87e1d3913884f88248', - 'dev_requirement' => false, - ), - 'react/event-loop' => array( - 'pretty_version' => 'v1.2.0', - 'version' => '1.2.0.0', - 'type' => 'library', - 'install_path' => __DIR__ . '/../react/event-loop', - 'aliases' => array(), - 'reference' => 'be6dee480fc4692cec0504e65eb486e3be1aa6f2', - 'dev_requirement' => false, - ), - 'react/promise' => array( - 'pretty_version' => 'v2.8.0', - 'version' => '2.8.0.0', - 'type' => 'library', - 'install_path' => __DIR__ . '/../react/promise', - 'aliases' => array(), - 'reference' => 'f3cff96a19736714524ca0dd1d4130de73dbbbc4', - 'dev_requirement' => false, - ), - 'react/promise-timer' => array( - 'pretty_version' => 'v1.7.0', - 'version' => '1.7.0.0', - 'type' => 'library', - 'install_path' => __DIR__ . '/../react/promise-timer', - 'aliases' => array(), - 'reference' => '607dd79990e32fcb402cb0a176b4a4be12f97e7c', - 'dev_requirement' => false, - ), - 'react/socket' => array( - 'pretty_version' => 'v1.9.0', - 'version' => '1.9.0.0', - 'type' => 'library', - 'install_path' => __DIR__ . '/../react/socket', - 'aliases' => array(), - 'reference' => 'aa6e3f8ebcd6dec3ad1ee92a449b4cc341994001', - 'dev_requirement' => false, - ), - 'react/stream' => array( - 'pretty_version' => 'v1.2.0', - 'version' => '1.2.0.0', - 'type' => 'library', - 'install_path' => __DIR__ . '/../react/stream', - 'aliases' => array(), - 'reference' => '7a423506ee1903e89f1e08ec5f0ed430ff784ae9', - 'dev_requirement' => false, - ), - 'simplesip/whatsapp' => array( - 'pretty_version' => 'dev-main', - 'version' => 'dev-main', - 'type' => 'library', - 'install_path' => __DIR__ . '/../../', - 'aliases' => array(), - 'reference' => '5c34ab7b32db21ad29865a50f9f49a9cdeb9c830', - 'dev_requirement' => false, - ), - 'symfony/deprecation-contracts' => array( - 'pretty_version' => 'v2.4.0', - 'version' => '2.4.0.0', - 'type' => 'library', - 'install_path' => __DIR__ . '/../symfony/deprecation-contracts', - 'aliases' => array(), - 'reference' => '5f38c8804a9e97d23e0c8d63341088cd8a22d627', - 'dev_requirement' => false, - ), - 'symfony/http-foundation' => array( - 'pretty_version' => 'v5.3.10', - 'version' => '5.3.10.0', - 'type' => 'library', - 'install_path' => __DIR__ . '/../symfony/http-foundation', - 'aliases' => array(), - 'reference' => '9f34f02e8a5fdc7a56bafe011cea1ce97300e54c', - 'dev_requirement' => false, - ), - 'symfony/polyfill-mbstring' => array( - 'pretty_version' => 'v1.23.1', - 'version' => '1.23.1.0', - 'type' => 'library', - 'install_path' => __DIR__ . '/../symfony/polyfill-mbstring', - 'aliases' => array(), - 'reference' => '9174a3d80210dca8daa7f31fec659150bbeabfc6', - 'dev_requirement' => false, - ), - 'symfony/polyfill-php80' => array( - 'pretty_version' => 'v1.23.1', - 'version' => '1.23.1.0', - 'type' => 'library', - 'install_path' => __DIR__ . '/../symfony/polyfill-php80', - 'aliases' => array(), - 'reference' => '1100343ed1a92e3a38f9ae122fc0eb21602547be', - 'dev_requirement' => false, - ), - 'symfony/routing' => array( - 'pretty_version' => 'v5.3.7', - 'version' => '5.3.7.0', - 'type' => 'library', - 'install_path' => __DIR__ . '/../symfony/routing', - 'aliases' => array(), - 'reference' => 'be865017746fe869007d94220ad3f5297951811b', - 'dev_requirement' => false, - ), - 'textalk/websocket' => array( - 'pretty_version' => '1.5.5', - 'version' => '1.5.5.0', - 'type' => 'library', - 'install_path' => __DIR__ . '/../textalk/websocket', - 'aliases' => array(), - 'reference' => '846542f82658132cd36acb7a7e8ce0f03960c295', - 'dev_requirement' => false, - ), - ), -); From 227e616e5475004a3ab424039683204ed7105e75 Mon Sep 17 00:00:00 2001 From: Simples IP Desenvolvimento Date: Thu, 25 Nov 2021 18:03:23 -0400 Subject: [PATCH 014/144] add commit client web --- public/css/.DS_Store | Bin 0 -> 6160 bytes public/css/styles.css | 1000 +++++++++++++++++++++++ public/images/audio-icon.svg | 1 + public/images/camera-icon.svg | 1 + public/images/clip.svg | 1 + public/images/community_message.svg | 363 ++++++++ public/images/cross-circle.svg | 2 + public/images/double-check-seen.svg | 1 + public/images/double-check-unseen.svg | 1 + public/images/double-check.svg | 1 + public/images/down-arrow.svg | 1 + public/images/favicon.ico | Bin 0 -> 32988 bytes public/images/file.svg | 2 + public/images/gt-arrow.svg | 1 + public/images/icons.svg | 1 + public/images/manage_chats.svg | 1 + public/images/menu-icon.svg | 1 + public/images/message-icon.svg | 1 + public/images/message-tail-receiver.svg | 1 + public/images/message-tail-sender.svg | 1 + public/images/messenger.png | Bin 0 -> 129504 bytes public/images/microphone-seen.svg | 1 + public/images/microphone.svg | 2 + public/images/notifications.svg | 1 + public/images/paper-plane.svg | 2 + public/images/pause.svg | 1 + public/images/picture.svg | 2 + public/images/placeholder-image.svg | 1 + public/images/play-audio-icon.svg | 1 + public/images/power.svg | 1 + public/images/redo.svg | 2 + public/images/search-icon.svg | 1 + public/images/single-check.svg | 1 + public/images/status.svg | 1 + public/images/stop.svg | 70 ++ public/images/telegram.png | Bin 0 -> 57029 bytes public/images/trash.svg | 2 + public/images/user.png | Bin 0 -> 29318 bytes public/images/whatsapp.png | Bin 0 -> 29936 bytes public/index.html | 159 ++++ public/js/cronometro.js | 54 ++ public/js/jquery-3.6.0.min.js | 2 + public/js/main.js | 358 ++++++++ 43 files changed, 2043 insertions(+) create mode 100644 public/css/.DS_Store create mode 100644 public/css/styles.css create mode 100644 public/images/audio-icon.svg create mode 100644 public/images/camera-icon.svg create mode 100644 public/images/clip.svg create mode 100644 public/images/community_message.svg create mode 100644 public/images/cross-circle.svg create mode 100644 public/images/double-check-seen.svg create mode 100644 public/images/double-check-unseen.svg create mode 100644 public/images/double-check.svg create mode 100644 public/images/down-arrow.svg create mode 100644 public/images/favicon.ico create mode 100644 public/images/file.svg create mode 100644 public/images/gt-arrow.svg create mode 100644 public/images/icons.svg create mode 100644 public/images/manage_chats.svg create mode 100644 public/images/menu-icon.svg create mode 100644 public/images/message-icon.svg create mode 100644 public/images/message-tail-receiver.svg create mode 100644 public/images/message-tail-sender.svg create mode 100644 public/images/messenger.png create mode 100644 public/images/microphone-seen.svg create mode 100644 public/images/microphone.svg create mode 100644 public/images/notifications.svg create mode 100644 public/images/paper-plane.svg create mode 100644 public/images/pause.svg create mode 100644 public/images/picture.svg create mode 100644 public/images/placeholder-image.svg create mode 100644 public/images/play-audio-icon.svg create mode 100644 public/images/power.svg create mode 100644 public/images/redo.svg create mode 100644 public/images/search-icon.svg create mode 100644 public/images/single-check.svg create mode 100644 public/images/status.svg create mode 100644 public/images/stop.svg create mode 100644 public/images/telegram.png create mode 100644 public/images/trash.svg create mode 100644 public/images/user.png create mode 100644 public/images/whatsapp.png create mode 100644 public/index.html create mode 100644 public/js/cronometro.js create mode 100644 public/js/jquery-3.6.0.min.js create mode 100644 public/js/main.js diff --git a/public/css/.DS_Store b/public/css/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..3cbbb78abcdcde85389bc155d61419dd2827f906 GIT binary patch literal 6160 zcmeI0u?@mN3`K275s4-x0?mJHGB(KPw zV`e^H&U>@L%z6x}?Kmxs=Q$R(dWm?}cnQfB0TB=Z5fA|p_(1|#vuXV%RI><(fCxMZ z@cU3Os5SL|yN1_39++Aa0PPTN2ETO^(82(;rbaR%h`~IPp^@y_W_T1pyySH?H4B(W z0rPA@Jn!z=r+9k;+@Z8Y^BSsI1Vms?U>%18cL4{CH-7=i_@% literal 0 HcmV?d00001 diff --git a/public/css/styles.css b/public/css/styles.css new file mode 100644 index 0000000..451100b --- /dev/null +++ b/public/css/styles.css @@ -0,0 +1,1000 @@ +/************** +* BASE STYLES * +**************/ + +/* Overwrite browser defaults */ + +* { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +html, +body { + height: 100%; +} + +html { + font-size: 62.5%; +} + +body { + font-family: "Segoe UI", "Segoe UI", Tahoma, Geneva, Verdana, sans-serif; +} + +a { + text-decoration: none; + color: inherit; +} + +/* Variables */ +:root { + --background-green: #464646; + --background-beige: #464646; + --sidebar-header: #ededed; + --notifications-text-color: rgba(48, 48, 48, 0.96); + --notifications-link-color: rgba(48, 48, 48, 0.85); + --notifications-background-color: #9de1fe; + --search-bar-bg: #f6f6f6; + --white: #ffffff; + --text-gray: #919191; + --chat-border: #ededed; + --messenger-color: #0280f5; + --whatsapp-color: #24d366; + --telegram-color: #26a4e3; + --scrollbar-color: #cccccc; + --chat-window-scrollbar-color: #bab3ae; + --box-shadow-color: #d5d5d5; + --backround-chat-panel: #fdfdfd; + --chat-hover-color: #f5f5f5; + --chat-active-color: #ebebeb; + --chat-window-beige: #e5ddd5; + --sender-message-green: #b8ffd5; + --type-message-bar: #f0f0f0; + --backgroup-icons-upload: #E9E9E9; + --background-color-message-receive: #dbf2ff; +} + +/************ +* MAIN GRID * +*************/ + +.grid { + display: grid; + height: 100%; + grid-template-columns: repeat(12, 1fr); + grid-template-rows: 12.8rem 1fr; +} + +/***************** +* APP BACKGROUND * +******************/ + +.top { + background: var(--background-green); + grid-column: 1 / -1; + grid-row: 1 / 2; +} + +.bottom { + background: var(--background-beige); + grid-column: 1 / -1; + grid-row: 2 / -1; +} + +/****** +* APP * +*******/ + +.app { + display: grid; + grid-template-columns: 42rem 1fr; + grid-column: 1 / -1; + grid-row: 1 / -1; + width: 139.6rem; + margin: 2rem auto; + border: 1px solid rgba(0, 0, 0, 0.1); + box-shadow: 0 0 1rem 0.05rem rgba(0, 0, 0, 0.2); +} + +/*********** +* SIDEBAR * +************/ + +.sidebar { + grid-column: 1 / 2; + background: var(--white); + border-right: 1px solid rgba(226, 226, 226, 0.7); + border-bottom-left-radius: 20px; + border-top-left-radius: 20px; +} + +.sidebar-span { + flex: 1; + justify-content: flex-start; + font-size: 10px; + font-weight: 700; + line-height: 14px; + color: #919191; +} + +.sidebar-name { + display: flex; + flex-direction: column; + margin-left: 20px; + padding-bottom: 3px; + margin-top: auto; + margin-bottom: auto; +} + +/* Sidebar header */ +.sidebar-header { + background: var(--sidebar-header); + display: flex; + justify-content: space-between; + height: 6rem; + padding: 1rem 2rem; + border-top-left-radius: 20px; +} + +/* Profile image */ +.sidebar-header>img { + width: 4rem; + height: 4rem; + border-radius: 50%; + cursor: pointer; +} + +.sidebar-header-icons { + display: flex; + align-items: center; + flex: 1; + justify-content: flex-end; + cursor: pointer; +} + +.sidebar-header-icons img:active { + background: var(--box-shadow-color); + border-radius: 50%; + box-shadow: 0 0 1px 8px var(--box-shadow-color); +} + +/* Adds margin to icon in the center */ +.sidebar-header-icons img:nth-child(2) { + margin: 0 3rem; +} + +/* Sidebar notifications */ +.sidebar-notifications { + display: flex; + align-items: center; + background: var(--notifications-background-color); + height: 9rem; + padding: 0 2rem; + cursor: pointer; +} + +.sidebar-notifications>img { + margin-right: 2rem; +} + +.sidebar-notifications-message { + display: flex; + flex-direction: column; + flex: 1; +} + +.sidebar-notifications-message span { + font-size: 1.6rem; + color: var(--notifications-text-color); +} + +.sidebar-notifications-message a { + font-size: 1.4rem; + color: var(--notifications-link-color); +} + +.sidebar-notifications-message img { + vertical-align: middle; +} + +.sidebar-notifications-message a:hover { + text-decoration: underline; +} + +/* Sidebar search chat */ +.search-chat { + background: var(--search-bar-bg); + border-bottom: 1px solid rgba(0, 0, 0, 0.1); +} + +.search-bar { + display: flex; + padding: 1rem; + border-bottom: 1px solid var(--chat-border); +} + +.search-bar>img { + width: 2.8rem; + background: var(--white); + border-top-left-radius: 2rem; + border-bottom-left-radius: 2rem; + padding-left: 1rem; +} + +.search-bar input { + outline: none; + border: none; + padding: 1rem 1rem 1rem 2rem; + flex: 1; + border-top-right-radius: 2rem; + border-bottom-right-radius: 2rem; + font-family: "Segoe UI"; +} + +.search-bar input::placeholder { + color: var(--text-gray); +} + +/* Chats */ +.chats { + height: 63rem; + overflow-y: scroll; + overflow-x: hidden; +} + +.chats::-webkit-scrollbar { + width: 0.7rem; + height: 3rem; +} + +.chats::-webkit-scrollbar-thumb { + background-color: var(--scrollbar-color); +} + +.chat { + display: flex; + align-items: center; + background: var(--white); + padding: 1rem 0 0 2rem; + cursor: pointer; +} + +.chat:hover { + background: var(--chat-hover-color); +} + +.chat:hover .chat-options { + display: initial; +} + +.active-chat { + background: var(--chat-active-color) !important; +} + +.chat-left img { + width: 5rem; + height: 5rem; + border-radius: 50%; +} + +.chat-right { + flex: 1; + display: flex; + flex-direction: column; + margin-left: 1.5rem; + padding-right: 2rem; + padding-bottom: 1rem; + border-bottom: 1px solid var(--chat-border); +} + +.chat-right-top { + display: flex; + align-items: baseline; + justify-content: space-between; + margin-bottom: 0.5rem; +} + +.contact-name { + font-weight: 500; + font-size: 1.7rem; + color: #585858; +} + +.chat-message, +.chat-message-typing, +.chat-message-messenger, +.chat-message-whatsapp, +.chat-message-telegram, +.chat-message-group { + font-size: 1.5rem; +} + +.chat-message, +.chat-message-group { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + max-width: 28rem; +} + +.chat-message { + color: var(--text-gray); +} + +.chat-message-group { + font-weight: 500; + color: inherit; +} + +.chat-message-typing { + color: var(--messenger-color); + font-weight: 500; +} + +.chat-message-messenger { + color: var(--messenger-color); + font-weight: 500; +} + +.chat-message-whatsapp { + color: var(--whatsapp-color); + font-weight: 500; +} + +.chat-message-telegram { + color: var(--telegram-color); + font-weight: 500; +} + +.chat-date { + font-size: 1.2rem; + color: var(--text-gray); +} + +.chat-right-bottom { + display: flex; +} + +.chat-right-bottom-left { + display: flex; + align-items: flex-end; + flex: 1; +} + +.chat-right-bottom-right { + display: flex; + align-items: center; +} + +.unread-messages-number { + display: inline-block; + width: 2rem; + height: 2rem; + background: var(--messenger-color); + color: var(--white); + font-weight: 500; + border-radius: 50%; + text-align: center; + line-height: 2rem; +} + +.chat-options { + display: none; + margin-left: 0.5rem; +} + +.chat-options img { + vertical-align: middle; +} + +.single-check-mark, +.double-check-mark { + margin-right: 0.2rem; +} + +.single-check-mark { + width: 1.4rem; +} + +.double-check-mark { + width: 2rem; +} + +.image-icon, +.microphone-icon { + margin-right: 0.3rem; +} + +/****** +* MAIN * +*******/ + +.main { + grid-column: 2 / -1; + background: var(--chat-window-beige); + position: relative; + border-bottom-right-radius: 20px; + border-top-right-radius: 20px; +} + +/* Main chat window header */ +.chat-window-header { + display: flex; + justify-content: space-between; + align-items: center; + height: 6rem; + padding: 1rem 2rem; + background: var(--sidebar-header); + cursor: pointer; +} + +.chat-window-header-left { + display: flex; + align-items: center; +} + +.chat-window-header-right { + display: flex; + align-items: center; +} + +.chat-window-header-right-commands { + display: flex; + margin-right: 5px; +} + +.contact-name-and-status-container { + display: flex; + flex-direction: column; +} + +.chat-window-contact-image { + width: 4rem; + height: 4rem; + border-radius: 50%; + cursor: pointer; + margin-right: 1.5rem; +} + +.chat-window-contact-name { + font-weight: 500; + font-size: 1.7rem; + color: #585858; +} + +.chat-window-contact-status { + color: var(--text-gray); + font-size: 1.3rem; +} + +.chat-window-menu-icon { + margin-left: 2rem; + margin: auto; +} + +.chat-window-menu-icon:active { + background: var(--box-shadow-color); + border-radius: 50%; + box-shadow: 0 0 1px 8px var(--box-shadow-color); +} + +.chat-window-menu-span { + margin-left: 3px; + flex: 1; + justify-content: flex-start; + font-size: 10px; + font-weight: 700; + line-height: 14px; +} + +/* Chat window */ + +.chat-window { + display: flex; + flex-direction: column; + padding: 10rem 10rem 6rem 10rem; + height: calc(100vh - 100px); + overflow: scroll; + overflow-x: hidden; + background-color: var(--backround-chat-panel); + background-repeat: repeat; +} + +.chat-window::-webkit-scrollbar { + width: 0.7rem; + height: 3rem; +} + +.chat-window::-webkit-scrollbar-track { + background: var(--chat-window-beige); +} + +.chat-window::-webkit-scrollbar-thumb { + background: var(--chat-window-scrollbar-color); +} + +.sender, +.receiver { + border-radius: 0.5rem; + padding: 1.2rem 1rem; + margin-bottom: 0.3rem; + position: relative; +} + +.sender { + background: var(--sender-message-green); + align-self: flex-end; +} + +.receiver { + background: var(--background-color-message-receive); + align-self: flex-start; +} + +.sender-message, +.receiver-message { + font-size: 1.4rem; + margin-right: 3rem; +} + +.message-time, +.audio-message-length { + color: var(--text-gray); + font-size: 1rem; +} + +.message-status img { + margin-left: 3px; + vertical-align: bottom; +} + +.image-message { + width: 33rem; + height: 33rem; + position: relative; + padding: 0.5rem; + cursor: pointer; +} + +.image-message img { + width: 100%; + height: 100%; +} + +.image-message .receiver-message { + margin-right: 0; + display: flex; + justify-content: center; + align-items: center; +} + +.image-message .message-time { + position: absolute; + bottom: 1rem; + right: 2rem; + color: var(--white); +} + +.receiver-message-tail, +.sender-message-tail { + width: 0.8rem; + height: 1.3rem; + position: absolute; + top: 0; +} + +.receiver-message-tail { + left: -0.5rem; +} + +.sender-message-tail { + right: -0.5rem; +} + +.receiver-audio-message { + width: 33rem; +} + +.audio-message { + display: flex; + align-items: center; + flex-wrap: wrap; +} + +.audio-message-left { + cursor: pointer; +} + +.audio-message-center { + position: relative; + margin: 0 1.5rem; + flex: 1; +} + +/* Reset input range styles */ +input[type="range"]:focus { + outline: none; +} + +input[type="range"]::-webkit-slider-thumb { + -webkit-appearance: none; + background: #30b6b6; + cursor: pointer; + height: 1.5rem; + width: 1.5rem; + border-radius: 50%; +} + +input[type="range"] { + background: #d8d8d8; + width: 100%; + height: 0.5rem; + -webkit-appearance: none; + border-radius: 1rem; +} + +.audio-message-bar { + width: 75%; + background: #30b6f6; + position: absolute; + left: 0; + height: 0.5rem; + z-index: 1; +} + +.audio-message-center-top { + display: flex; +} + +.audio-message-center-bottom { + position: absolute; + top: 1.5rem; + width: 100%; +} + +.audio-message-bottom { + display: flex; + justify-content: space-between; +} + +.audio-message-right { + position: relative; +} + +.audio-message-contact-image { + width: 5rem; + height: 5rem; + border-radius: 50%; +} + +.audio-message-microphone { + width: 2rem; + position: absolute; + bottom: 0; + left: -1rem; +} + +/* Type message bar */ +.type-window-image { + width: 100%; + display: flex; + flex-direction: column; + padding: 1rem 2rem; + justify-content: column; + align-items: center; + z-index: 999; +} + +.type-window-image h1 { + font-size: 40px; + color: var(--chat-window-scrollbar-color); +} + +.type-window-image h2 { + font-size: 20px; + color: var(--chat-window-scrollbar-color); +} + +.type-window-image img { + width: 450px; + margin-top: 30px; +} + +/* Type message bar */ +.type-message-bar { + position: absolute; + bottom: 0; + left: 0; + width: 100%; + display: flex; + background: var(--type-message-bar); + padding: 1rem 2rem 1rem; + justify-content: space-between; + align-items: center; + z-index: 999; +} + +.type-message-bar img { + cursor: pointer; +} + +.type-message-bar-icons-upload { + display: flex; + flex-direction: column; + flex: 1; + position: absolute; + bottom: 50px; + overflow: hidden; + height: 120px; + margin-left: -40px; + z-index: 9999; +} + +.type-message-bar-icons-upload-btn { + padding: 10px 11px 10px; + background-color: var(--backgroup-icons-upload); + border-radius: 100px; + margin-bottom: 10px; +} + +.type-message-bar-icons-upload img { + width: 22px; +} + +.type-message-bar-left img:nth-of-type(2) { + margin: 0 2rem; +} + +.type-message-bar-center { + flex: 1; + margin: 0 5px; +} + +.type-message-bar-center input { + width: 100%; + outline: none; + border: none; + padding: 1rem 1rem 1rem 2rem; + flex: 1; + border-radius: 2rem; + font-family: "Segoe UI"; +} + +.type-message-bar-center input::-webkit-input-placeholder { + font-size: 1.5rem; +} + +.type-message-bar-right { + margin-left: 2rem; + justify-content: center; +} + +#myImg { + width: 100%; + border-radius: 5px; + transition: 0.3s; +} + +#myImg:hover { + opacity: 0.7; +} + +/** + * MODAL + */ +.modal { + display: none; + position: fixed; + z-index: 99999; + padding-top: 100px; + left: 0; + top: 0; + width: 100%; + height: 100%; + overflow: auto; + background-color: rgb(0, 0, 0); + background-color: rgba(0, 0, 0, 0.4); +} + +/* Modal Content */ +.modal-content { + position: relative; + border-radius: 10px; + background-color: #fefefe; + margin: auto; + padding: 0; + border: 1px solid #888; + width: 30%; + box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19); + -webkit-animation-name: animatetop; + -webkit-animation-duration: 0.4s; + animation-name: animatetop; + animation-duration: 0.4s +} + +.modal-content-body { + display: flex; + align-items: center; +} + +.modal-content-body-item { + flex-direction: column; +} + +.modal-content-body-itens { + flex-direction: column; + flex: 1; +} + +/* Add Animation */ +@-webkit-keyframes animatetop { + from { + top: -300px; + opacity: 0 + } + + to { + top: 0; + opacity: 1 + } +} + +@keyframes animatetop { + from { + top: -300px; + opacity: 0 + } + + to { + top: 0; + opacity: 1 + } +} + +/* The Close Button */ +.close { + color: #585858; + font-size: 28px; + font-weight: bold; +} + +.close:hover, +.close:focus { + color: #FFF; + text-decoration: none; + cursor: pointer; +} + +.modal-header { + display: flex; + align-items: center; + border-top-left-radius: 10px; + border-top-right-radius: 10px; + padding: 5px 16px; + background-color: var(--sidebar-header); + color: #585858; +} + +.modal-header-title { + flex: 1; +} + +.modal-body { + padding: 10px 16px; +} + +.modal-footer { + border-top: solid 1px var(--backgroup-icons-upload); + display: flex; + align-items: center; + flex-direction: row; + padding: 2px 16px; +} + +.modal-footer-content { + display: flex; + flex-direction: row; + margin: 10px; +} + +.modal-text { + font-size: 16px; +} + +.modal-list-radio { + font-size: 16px; +} + +/** + * FLEXBOX JUSTIFY CONTENT + */ +.flex-center { + justify-content: center; +} + +.flex-right { + justify-content: right; +} + +/** + * FLEXBOX SIZE + */ +.flex-1 { + flex: 1; +} + +/* + * BOTAO STYLE + */ +.btn-danger { + padding: 5px 15px 5px; + color: #FFF; + border: solid 1px transparent; + border-radius: 20px; + background-color: #f77070; +} + +.btn-danger:hover { + background-color: #ff0000; +} + +.btn-danger:active { + background-color: #fd6868; +} + +.btn-info { + padding: 5px 15px 5px; + color: #FFF; + border: solid 1px transparent; + border-radius: 20px; + background-color: #70aff7; +} + +.btn-info:hover { + background-color: #0076fd; +} + +.btn-info:active { + background-color: #378ff5; +} + +.btn-send { + padding: 7px 9px 7px; + color: #FFF; + border: solid 1px transparent; + border-radius: 20px; + background-color: #e5e5e5; +} + +.fz-10 { + font-size: 10px; +} + +.fz-14 { + font-size: 14px; +} + +.fz-18 { + font-size: 18px; +} + +/** + * STATUS + */ +.status-connect { + color: rgb(42, 201, 95); +} + +.status-desconnect { + color: rgb(241, 65, 65); +} + +.status-reconnect { + color: rgb(241, 165, 65); +} + +.cursor-pointer { + cursor: pointer; +} \ No newline at end of file diff --git a/public/images/audio-icon.svg b/public/images/audio-icon.svg new file mode 100644 index 0000000..de8944f --- /dev/null +++ b/public/images/audio-icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/images/camera-icon.svg b/public/images/camera-icon.svg new file mode 100644 index 0000000..519e432 --- /dev/null +++ b/public/images/camera-icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/images/clip.svg b/public/images/clip.svg new file mode 100644 index 0000000..ca3cdb5 --- /dev/null +++ b/public/images/clip.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/images/community_message.svg b/public/images/community_message.svg new file mode 100644 index 0000000..edd8a04 --- /dev/null +++ b/public/images/community_message.svg @@ -0,0 +1,363 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/cross-circle.svg b/public/images/cross-circle.svg new file mode 100644 index 0000000..17a3afe --- /dev/null +++ b/public/images/cross-circle.svg @@ -0,0 +1,2 @@ + + diff --git a/public/images/double-check-seen.svg b/public/images/double-check-seen.svg new file mode 100644 index 0000000..3cf4d2f --- /dev/null +++ b/public/images/double-check-seen.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/images/double-check-unseen.svg b/public/images/double-check-unseen.svg new file mode 100644 index 0000000..7d8d858 --- /dev/null +++ b/public/images/double-check-unseen.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/images/double-check.svg b/public/images/double-check.svg new file mode 100644 index 0000000..dc41b7f --- /dev/null +++ b/public/images/double-check.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/images/down-arrow.svg b/public/images/down-arrow.svg new file mode 100644 index 0000000..8450754 --- /dev/null +++ b/public/images/down-arrow.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/images/favicon.ico b/public/images/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..6ee060e1e548d32c93f4bb3c99eeb9ff3370fa01 GIT binary patch literal 32988 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 literal 0 HcmV?d00001 diff --git a/public/images/file.svg b/public/images/file.svg new file mode 100644 index 0000000..472baf5 --- /dev/null +++ b/public/images/file.svg @@ -0,0 +1,2 @@ + + diff --git a/public/images/gt-arrow.svg b/public/images/gt-arrow.svg new file mode 100644 index 0000000..f79a135 --- /dev/null +++ b/public/images/gt-arrow.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/images/icons.svg b/public/images/icons.svg new file mode 100644 index 0000000..7cc7d43 --- /dev/null +++ b/public/images/icons.svg @@ -0,0 +1 @@ + diff --git a/public/images/manage_chats.svg b/public/images/manage_chats.svg new file mode 100644 index 0000000..e7ed144 --- /dev/null +++ b/public/images/manage_chats.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/images/menu-icon.svg b/public/images/menu-icon.svg new file mode 100644 index 0000000..0e0e7d1 --- /dev/null +++ b/public/images/menu-icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/images/message-icon.svg b/public/images/message-icon.svg new file mode 100644 index 0000000..3303847 --- /dev/null +++ b/public/images/message-icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/images/message-tail-receiver.svg b/public/images/message-tail-receiver.svg new file mode 100644 index 0000000..33e73a7 --- /dev/null +++ b/public/images/message-tail-receiver.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/images/message-tail-sender.svg b/public/images/message-tail-sender.svg new file mode 100644 index 0000000..b28282b --- /dev/null +++ b/public/images/message-tail-sender.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/images/messenger.png b/public/images/messenger.png new file mode 100644 index 0000000000000000000000000000000000000000..17fd6ff2b90989648754599d02ee245475f54692 GIT binary patch literal 129504 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
|7n}!yEF394< z<~zd}(9n@c{T%F$3JqLX{g)2Sq#5r1(UN8|8{-%M*@TWKT-eaw#_a3b`6-sTk2VIY zpqWB>`h-a1%-sHaD!7K zo`@bEGQ7e3S{^GLF=;UOjV|bm!8=**2X&;QV46450MvJQa(bV>VnHyhC_!hN75oo4 zPm8}&`sgAewMToG5ujEY^3$F6$xMZe(+`HiGi!xZh*JwKO8}N~99*Dm7lun_8pZdvI2h;Y~Wm5Q8CBG#$&V32e)cmqBCkxySJYe<7HM6;>R>VC-DeZ;40>LpCWu*15sS z`k&ikGbMOgo|r#jC05jMl9qbTYojRAv+hig}0{OB_ zl|3~7?|dqbq4dl4dYAkcOXgt>!R)&(hfNivRP(|M;eVZ#l<*X!Gv|4f?ySL`(Nl7ytM5v13Gx$#e<*Utd`-KgMuS zW53In|9#!IhH=xjM%d+lmf;u+C0)nkS>I7Feg1z9{{J83|F>TM?_!cd-yFTj%*4dh zT1G1#d`Y~VXeX`{%EZjV%?!vYulT}t; \ No newline at end of file diff --git a/public/images/microphone.svg b/public/images/microphone.svg new file mode 100644 index 0000000..5068a60 --- /dev/null +++ b/public/images/microphone.svg @@ -0,0 +1,2 @@ + + diff --git a/public/images/notifications.svg b/public/images/notifications.svg new file mode 100644 index 0000000..b09e6ee --- /dev/null +++ b/public/images/notifications.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/images/paper-plane.svg b/public/images/paper-plane.svg new file mode 100644 index 0000000..f6246f3 --- /dev/null +++ b/public/images/paper-plane.svg @@ -0,0 +1,2 @@ + + diff --git a/public/images/pause.svg b/public/images/pause.svg new file mode 100644 index 0000000..86fad01 --- /dev/null +++ b/public/images/pause.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/images/picture.svg b/public/images/picture.svg new file mode 100644 index 0000000..2c1c2ec --- /dev/null +++ b/public/images/picture.svg @@ -0,0 +1,2 @@ + + diff --git a/public/images/placeholder-image.svg b/public/images/placeholder-image.svg new file mode 100644 index 0000000..0f9c09d --- /dev/null +++ b/public/images/placeholder-image.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/images/play-audio-icon.svg b/public/images/play-audio-icon.svg new file mode 100644 index 0000000..cdfb5c6 --- /dev/null +++ b/public/images/play-audio-icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/images/power.svg b/public/images/power.svg new file mode 100644 index 0000000..b6b08bd --- /dev/null +++ b/public/images/power.svg @@ -0,0 +1 @@ + diff --git a/public/images/redo.svg b/public/images/redo.svg new file mode 100644 index 0000000..4935836 --- /dev/null +++ b/public/images/redo.svg @@ -0,0 +1,2 @@ + + diff --git a/public/images/search-icon.svg b/public/images/search-icon.svg new file mode 100644 index 0000000..b82fe2f --- /dev/null +++ b/public/images/search-icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/images/single-check.svg b/public/images/single-check.svg new file mode 100644 index 0000000..cb3ed59 --- /dev/null +++ b/public/images/single-check.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/images/status.svg b/public/images/status.svg new file mode 100644 index 0000000..38feead --- /dev/null +++ b/public/images/status.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/images/stop.svg b/public/images/stop.svg new file mode 100644 index 0000000..f5fe016 --- /dev/null +++ b/public/images/stop.svg @@ -0,0 +1,70 @@ + + + +image/svg+xml + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/images/telegram.png b/public/images/telegram.png new file mode 100644 index 0000000000000000000000000000000000000000..3d6936cc0b6d2f1e52d75f15b185a4e27425a933 GIT binary patch literal 57029 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�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 CA6E4M literal 0 HcmV?d00001 diff --git a/public/images/trash.svg b/public/images/trash.svg new file mode 100644 index 0000000..bcb9ad7 --- /dev/null +++ b/public/images/trash.svg @@ -0,0 +1,2 @@ + + diff --git a/public/images/user.png b/public/images/user.png new file mode 100644 index 0000000000000000000000000000000000000000..2f2f296e0c1fb8b83a8342eb44a71196e12d7c91 GIT binary patch literal 29318 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 Gr~e0|x+XXP literal 0 HcmV?d00001 diff --git a/public/images/whatsapp.png b/public/images/whatsapp.png new file mode 100644 index 0000000000000000000000000000000000000000..2c634d7c0feabeb77c4c2e3c7bcc7c120e612ff3 GIT binary patch literal 29936 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ℑ09%E>+b zkIDD1-kGEj8#hbJ(pCx$^pXhbtw0zRv^#^`fFNQUpdw<4n?KR{iAF*uptZdKDBls3K_i2zF?)67ZOzldqO@X4|%D6c;s z+(j3QEi}dvL3w(=DpH_ja1k=DiNUFR5j4BdSYe%cI}nM%eWBU)hP8^Wr?b!RG)dS50dI zk>4WaLUHj4E|mJ6<*K>xb3CbA^s?No8&0}As+cLM^Vbry)Hl9jhzu+PaOup`;~6C& z#E@n%*W8oHetE{&8pe@Uy7DgRd*z?NQ)|JW@c{QEf_Oox%3>T;S8rG0u=AGF5+{v$ zC(1$e4(YRqjp-%e)^BFCZ2fm||F5X)j;FGJ|F>gP9I}!bWo4yO6q3EkEHfe@A*1Y( zEh=SYWbZ9|&q9(>$Vf&;$jUyy>-KzKuiqcf>v^8nan8BV=f3XG=epk4`~AKaNvz&5 zj4BV9*Hw6|n$5G#7?2^bhiF2uBwE_Z>YEE+ka%6wjaN`-&1(Gpu_!eE(_c_OE+6%) zt-n}1kuowP8I(mhcG)nr=krZoD*+@R7s?9_VAcOp=)T!I+O5*y3vcg(rxx0`zqa@( zQ&E@Tk#TOk&ysp8fcm?qzRAORM|| zT({)a_lW*gyZ6MHyVj$_a-tgsLK0cde!CE zvpL>rLaWM}QSD0skoCFqIm& z+a`B^C_24R(b!yyzj|w^nBfEgq~fZSQxEeT6!kdTm~D{csTr&#_l$C%mBT zAU%s|YFbxr{8C;Y_Uwn+_H#=eBr@Uw{yrQyzmlY-FjaxRRbm@j8gNjRZh`?fTkLdMW za+^zqJ%WE6=;6~0Y_FAK)C=kfQDF@nzQEA`1>EoPMDKw=9B&|Rfsn|yPIx8|hXokhf1aii(iWgZX}{@v(4UI?;Zw7ZIs6CT<8F`uk&a9xoYF}e3qCVv?+rx z5Bvh!fd3!|asWtbqer%gRFe$Z+ym4!tXx%;r0h&9W`8q{;kBAUA2n(6L<5< z#}oELjlI9vZcq(-K?V)Y_bW+;A}9GMJWrtTTe^)2U1Ru3T4l(ZHM7rApQ=?EDDRyO zqFZ#i#Hm3VGN5Z=b+yh2g7-1#l?Kb}fwb*l>_OyQX}~x1o$^;>tx1!Ko=FgFzGfg~ zlK)Bze7!J}=C7~*0ois465_yK$kp88_+UBu#?GVWtuASYm%_^n2W4fJOWK^(TNB7i zR7Kg%n7>WsH|w<*P5t%D`};BJ8rq$cKbxgQDKWZr;yC|y)CJAo*~@>)RL6P*58s8! zGCv$3kurSLe1&B2c93Pm8}})E)v+)j;X`jUr?z*r8F&cm{^V;j@zyAvYkh-Pb1XN*;So?!PB_ZTaVE`WaXd6+#uY)^iXd;STm z)Sg^ok#z#f<1a|_t65s+#FS3WFeBcX?-mlYeX<-F-^tgMsQl7I60Xqz6D+7^sfQfM z{4MbGovod^gR%;9?;^>hqN*m?rxxhtFWjZ!D_rzU1_p@249h_jeDI|igSpMb z5(Jy8FU$OHcLH#n{;B-_@?NS$0U4BuXJlpk^>=PNJT`x4;!Vup(az^LA;{`vunN_w zh@Q^q75OXsS9vY_sN}{c!f|`qe12dHRcCS( z^;#a5S`S;sUS@nW6ar~nc5RFd+)`U!vjhlo&3BV3X}&o zmZ!JmVvifp4Df^G+8>f@)v@tsGsH922kd?oubSfNGxObNRlMyamPD1SG1B2FEBG7y zaH-u&6RmIRSBJ{S!)hj9B=c@Ou6hckPWXcEoB9*VN)zZJQkgG>13DmdLE;oN2orNxoS)g z13<-qJQ07}8u~D?1dQtrPUFC#`zF9H%Pjfy7JtVJH-`SB0F-}UVe8+lB8>GMLA#p{ z(GZ}}wf4GzqZ`ZBZOIT5KS~WCpn+tV8&dQ54|(fZ-t`+-jkXWUJe#j|PJ+_ajzB29 zhO6fvFm$1$Wi4qQRoe$mS6J5YLve?Yg&5hAR4B4IgR)X=+{kJIwr?6frq>^H@OOH) z44m$i4hIG}D$}YSiEIzs3lBbWeqB2*Q(C?atqnK(`N^ExzRwDUaR~-O+qNcV65o_b zc<7bc12tWr?mw8fz>uep>jQ`Y_cO#Jzvtf^9#QOZN{8!n&>y}Y#V4~URcm0wRr)pG&Hg}A@zZ*pK+dLUc?blIez9kAxbuW=A(pR(PAfM^3Jz7W% zb)rM&JwBLl@k=5?9fd6UIue5MStIG!d30!o*9r&1D~2xDXQct{t9PuxS^GhQKetU; z;9CXx=IVyu?+Baj#yxfMaV0##2_*gZHL?jJ7>Y}D>sewQqYq?S2vJF8`k%fq(@%iMuj3>S<;(JVXh1?U zBLm)M*jAm4!!NjiXumd;8E4H*JQ7a3JeG3b>Lh9_rVDy2SBfL6T3wkZ(b2pTBO*cn zme=rED%Xu{8VYp?n^3<3WI1hxW5(X(Xs-VDLR&(6*IGa@uCmSa|XjRw0BXL-8&o3_HD~HsPAJ*_{QbyyB}% zVj4A^R4SgN`g{s0L$$dTHHZcw7zH5!ub=(FSfF^f?jW*c_%xj)DZOi>fBs>lYoiYc zHMj1Y7O=@IN#x*4o9D`jT4$teTPu~Vk+|UH#148L9%UumQ7wG{y z%xXB1PfMU-MOjv4`{!Hbo{9G7j4&qZsfA>stf37Yz`eZj%GtHuv4!n~uFZ-5$$qjW zugWJOXX0|7(=Z_#R^nZ=vw4d<%vU{^f!*<|@$YfXKc8SJj;v%GtBxCWJmCk;#^lMT zEFkVFJpnA!6iEL(dGzx_1jp8g?0AvukN(jkKL(8~8v3~$8G0D)_-e|Y@xy8k@aKID zL;>@Fs6va=K*Vd0znHW)E6+qxS+Cle`ymH$bNCdg4)$Z(i;RjJ3i&6$BpIB;WuWgn>Nme+JulU&)|&ja zL*G3%;56EnfU5Y$~ezWzF zUzcR=@JTy$1Za=zdV(b4p}9%D{mmX4nVGsgHzamsW%NxWgRt9nU>t zH=H+k4|LJ^!M%Us@j((n`+l|JNi*lXy_6LG-gIxQ90%f-&Mpn6cQDb4`J1F%!mKx> z%i159QdR`a)8Rpmm9S;mlR2670t3GrH?hqCvSiS#crq@l+KnB|$BY^w*HPO&wy|-c z0ph%mnInN5;<-oD*ZaluVB%}lLPkyuf zc#mOYyg4bI61~S*ygjhE+sF%>TYeT?G^f_7qBa!rfK1q#25EZB!B_lXH34l`FkL8G zjvsqT|62nu3Ou=p)&#b6O#g^VrOo5u0C?X%t^g0qr3`?ee{T^(NMLEqQeWUBS0Ptl zUEbm2)P^699}tBzzG`O{%82FoUwwn5k-aBLhAMM|l@K#lt6?_c+g~NTsleG=WsfB& zLrA{nwcJsqBHNRV03yqH#nO8v38`17?^azV0yNm_#9;HBL?dSgs2pyftR9f)t!CR< zJASZP#l3dkf!1nWblf*??K*jPRsFk9POe5`!>(*Gt{!!U7kc+RxPEn=e|-D^Z)#(j zzGP+16`E8rB*B$prQ5iex{KiWnfoxFOC?G2ixJ&0-C=0@eB1T>Y}JYhGJUz8J6{zZm>@;B>TPlS8O_I$JF zw0>r!pRgIp0)3j?O;dHi4^~*{X`_8Wa){EVfx^$;!sy;n#0-rNU*Q~mt&6PP6m^s_ zi|H@=NMxZW0S!Y2I>`{wgce*lq+z8$3X9yn%*ONAal72k*pp*@uT#qGVm+r$pMO;G zLsx74yPJ{ynaFqfxd-OU`&DcbOH( z-SV7>8PLoi&g*Vy0WE0ign{fU-=})euBl3&H>&y3Gyf!AyTtULQx{a4Ab>B(a1zoN>b|?54v)jbWd+m|7(BhU>veH1T-R;-Mn-d3(qP zl!w8_FLBmeZRN}e(N6Bk;Va`U#7V`2y)mP;FC-v+XwXE1ivWV_tiV-+ZskEs=uQIC z5De&XXHDp8N%@SY*QF^gph~yR6pETZI3Q@O{);c=U&aVqP%!0pvJ>K|G4vk4^638= z!1jC6`B~u(J=;@JvcxazySPGK30!5o?eEjVGdo}Ffbdu|Za8bJ5=sbIWMF@yVEcBi z2hi2%p}xy8FL_PE%|Me73e1R?)CIkdVke%Y&$1bh9B`or*Nc{Nwl!^+D|P!!Tl5|^ zXTohmVg;oakH|teO+K{Vk3ecmb4Ujx@REAlQY&k+joN;r#PVPKCnYW#krC&eb5FS;koJnoYPM1O)f zzZ}7!^Uzn8g=hnx4OFM6Z*+7SSo!HMXf@#e$pV#~VPxTKFN^nX%P26eQr1qpY12E? z02^-J_)PUgmgM8)RpK+Y9eghIEQHwEr4QfZwjvnuWMeN`=atSPa3)GM{(gztPCzSR zapg7OeVss4_vab6N7c0X0ysusfVC`?+?E&N1Z)7M`#zk0^P6rWSxha+E&WU~eQzs( z8*l{hlWM!09#*a<^fztZU-0&i^6pRNcX>9jcf$u20+<(@LlXk+0kBdEwlvh(MK!qh z&|@!z(t`1y0h287>DR8&o;Gm#PuZn%-nV8}y^)VMgzWR+sfY{)EyKb+uqIgkcI$wP zuW&3JXX_sRFP225;=KO6b$^tljR|Uq#N3oz@i6jG-SF|nql`>&h#b8rFJ$Oj|m@j+Tl z6WFE-37?q48(xqN$)Q$Q-|RY8Mz_GSOtW*H7glP#l{ghdm7Bn-H-0(aoXUVR?f2Ze z3w)x+)ZC0{F)|L$47amZUvnF+hmt^@k4$Oa=u1$U+iyxk;xz+EJySnJGFm4^MFuK0 zv|c`cUj2DM+s{nTtApjl_akJv?I*yz>>jw5?Y5`4z`hNvR(6PIsH{fOHOn@5B#GQzSR7Yif$>x;lCKN5SP$;EC!r^0B|&x zvSna<0(Y11H&mmvWWk7Lu>=>R0nlPI$GA{Pnmw&$J0|QNKJvYXU5K0!7U~I^P06`YhXJUf<5LEIW?fpqpq8HY@*XQcZ%LG; z^zWC0R1D<$u%}%Z-6zt0>(wH1FcN1Q{;}ITGS{IrGU&L;6011m#ikJQlhh~NT2+Y_ zRbhG`xp9D_SFHWfa(F;!+{nli8<&rjgP%UB?rS4@x95f8LBX}N@tZQ+T!>D?<=dks zs9yiGJ;`*U-r?pF!4Z8rVG)VEAYSA4-i#eoMhG;mgkRB2990fMB75GrutV&4&2%qO zf}aZP^9Q%WNEcfN*}KSn09w0{loWG?n3_@x-gE?38yvrN3(`=J8ch(lKd)FyQKG$F zGae3ml$mA~#r*=k`Kx~I=T_Mom&x6BXyk|QXk8a||GMZoDBK0^kHvBAlYa!BlKaRk zUH}@T6cPTXZ!iRw--`A zd9J!9=+VA4QO^j@?CY`qx)s9LC_x^LV)NVBsJ&j z3tcxOgQFKXLlT2A0lONo`vF*qO^BTNE9Nyvs}=0(cu{xZ;R9Xz*NFGWUsROwG=_L= zv0`D8i}W{Iu%b9QJ${#O`SNCe`bYMlSkRRByX$NQST5kw0TP<6!tcAafwkB-#(yRN z9P)0rUqq#GN{lYxJ>2p2!FG0}R?yYB6}0pKS7CO%2hRjbHPIeD_zkHsr}aW%Of&RI zY@5lOS?NreT3k+%JSxA6dBTsLzNmJF!X^kP8JZf~=Z2Y&C;$uuGrXvop6WP4r|jGB zRKVv8!ICd+3_VsO1b^tT#=kKqeW1|Dx^5x4N>lc00m?KW7FN(4t-+Sie8c)=X-=sdqt(3(`E3OIH`n(#AJ}e{s>jLdk#kum7lNKPf!C?zXzd?(qG`$B^OXtZ z0!P@btI(tDpXKmLj?2F|_6G|-2U&)dn5{MM`m)7<5ci>N$3O`qxG6 z?Ct}0zH_hdHQ!zc!>0c%v&zaMbYH4WathxaT8m-!GSlnxz&k8%e#zD@+RF%Mqb%(oxht+LmDp+5FFPhh!V z((&M$mtyPZD|RK_pD*x~ixRcHkj4G(2eu6MW+rw~p=Nk0ACCEcd1YuWq>UQOF^81= z3TW$gIzKykEAf}%lS7gzE0>sGUJ2KJpGAza%YN&CyO^=JDvu76?T-j_H-T&jR63qD zzl_=V?0BWYY5@8=#IoG5LN5w`9`^IyBY7s96a*9!%(IJTaGye#L|Ji_ZdKN-kynK4 zc~7ERD?k0e05c@jk2>4@X(F!<_LEdh+_@Bs;*S{K8XC z45nT3Sy7LG%#IJK5;Iz-FTww5V$8*EpMHDToX@;5Ve}dV9I&*TxYv7v$Ukj>F zhnD1ofYT+-ioqr401Eaeu{i-S8A+uK$3<=yNUmk{e_?GYHlCg5(YaK;)IXDpIuXHX z05k#(@REg&4hpbUM58sJ=PttrFS`a_rFhu?94PY^y1y3sHCaKd%zQhA=rgXZ9PcIWjHui;g{axplg=>2Y zKUS;*qN!OH0zn=hl(oBaGPwld%+M(O^ihBJ95`*R)j|f4IiFEa7E%kV-EMMQ^2X71 z{p0LXqM`kf!?=1KXQ*(8hl2Fy?vEGN^3-uY$u}@NarMo@-E?%9@T9yKFAPGZ+niSO z(q!)%JmR;^dhaAvbuyUWp>nJpSxgom-kSyOMvtM`w#QI9jZ}+L;`3KNQ?nZ3!g8(e z5%aB@Z@B86KP}TEul4M)S`s%C{TWe1 zc1o`|lcJx>%LuWsx}|e{T@;(0iF$8eFlEv-n!ZAUWAWQsg5PEwYxr0pce--$8iRQ_ zArbq`jST@q9i_yyw1Wl&@p-41CqdP8_xRP*2|jMlYI4oei%b)y!SG(gV#}LdfBt(d z8k=yJ@z1~8oojbov+>;q>XkdTL=pyB&tVl&Tk_cVgYj(g^-%`JL1Mv`43?0;8NVwn z#cCZ8t*wTfMtzoLrMtw#pKK7H(1pwtzPG_ATKzWahHS~;osgip#^X$4F9PHSM3|3^ z1;6dTE8eYdzyHQ4DV^dcd0;&LnsQl#YQ$dxGiJ7D$L7v+G28CfSEvNlcKf5|_0Bk= zUt3j`a>uudPKx~4v)Ze(Fnh5=4wjJ!sPLvPmPhAF4ql*s-@>@FzLGsjbV%gUm10QUXyAd*MW?Y&xxK zPK`%LQV1<gZ&#@W-G+evbknE|rTdgU$VfzF$ zB|34@UdW(Gt?=Q4!M0&hnc?-#%7Exb zn2Z1+;M3*}6ABvr#faYcz-fq(D9Fbi3Lto*%iu8Xt0q0y&Xlu%!_{`zwwm&qRUOddo|W2R!OU+WgqV`7j8>1gcsbQ8oAM)O&r zC+Uk+dPC&kc;<-0)60nX@y#?Izyx>CTND~9GRvrgh55%HiiFDT{bzk#$t~c1&cC9* zJ#U%+2^L1w5P8?}PLXg@k;m&~lPVgbV6INk#t-hRjhPgw^6>giSmI=4T0kKiHTDm$ zh@-(aCcLg*-ncF7ejcNwf3!3}zNPGtS7oZ{47g*No#h5d7N2m}%nUz$iUw~@Hf z)yDLE>@p$Osm3;$p#fqkb)PH>W+kltq(TI1?RNu`LKlx1n~>wz2&lhj=2k5qM^708 zX(Zw{QWwIvUDKl^hDf-_3wMY;${`(g@Ywh#A%J*H&Hm-HxyQRC@K(C+*70nFH^@Y3 zkhnlxVw|3GVtX)<)&&(|f`+v^;>P4raN0b-g43t5x3{M+lj5F*$L+68n?E-1vd`H3 z&nn*N)MK&`@p+56MrG#q-!T88%TsyaJP+9sIa?_D(-*WOFtej0aeN!u{sb5SM0Ee=S0zAyJ2sIo*_MH4in zp-))Dc{cjEWyh^XtlqpSMY}R|&zLeL>Sy*5AFs+WR?Bi8&B1P+Y|E zwqOi)qj{fDs3TlDg(a%8x0ibdFqC~5Z}Q~9-ny{o`*;aFp;&ZzoWH2iuE#%(BNT_> zBd7jqlu`7;go(;C!`dWk%laL$IBK`viQf!h1nfCYg(P<0dFxexUB8T!98Vs3^FnI6 zu=@47WEMrs=WudF^0E3#_Ge3d9DY|Ax^~~MUAUaHK1_d}o+%an;srV4%?`seGSrCM zL=eC1U+@=-hRD(Bvg)n$u%V)|8Ag_~jP5aag(r9?R7-6S$q}*vW$ke|h8tH#)cl?`5kK zJ`4sbzD?%fs@9Rqn(Z&N3X6yT>0=M>`^txZk{`61Zo$-~xtJdqw@jAdpB@{c=UMQC zs^XM8lZtrZOp!xFYd0byGgM2hThYT{-A?No>5y zjm_#ysJbWUjFpRop-g`HKT2*BM9(AdUPRb5V0S98Y2^#)8jq2<^f}! zCeXQOL1ud}YfC&!(o^)(pxMb7@htko<@XMNAv$fmsBR)IbwT8K z9Z#}uB7)a?KZyuNnt+ve;4U%Dqe?hT#k!G5U4qEN==&qa)Ws4v#6^nU!ayrQ7-;1J zqmrmHTCU+^2H2_KCYK;ugzWQOmd(Zaw-k98!(h4%`Xdy&4att8()9PJ3 zLHdI*1Pfoekc7?%6G(US`kem82A;)VzyP;CYtz2tRy8Ea9a^WS#Hj(}#)2sD-DKUa z3--m8&Ivg)Sb7*DLaO%#87vAEgR7L3W*Z-#56k!w2l+UlRGw!$Dnbz+UEVt!BpWlU0rKbwp7uQ7SQ87 zO8Z_MCHY6>;elNDli8!$Q#Cc(|B47_R+0YK@Il6p6_8clDEqqO-1USx9C!iv?ng4YngVU-|OYXZYw zKfA6AE5s9#>k_v%o>gP60 z{2A<1=`KRG*XzQ`ZJh_C@OSpTdsi;YRZKg=WIGbWE)IMZ4i>zUJJLqn*B688ER%O( zoLMY;4-8MzdcHxXU`RM6jr<&pi}fWLycN1iFhkG(s58a`ODO`URP0X`BcY*a8k4tV z*)hsfN(g?oh_EE5#tGAqfOQ>P*Mh6oSWdML() zC<3EjsN_+U`>7Ho5GgPR3!A|0C5&Ds^v|#O+i#t_X~-Q=C535iK1GWEh8VNNUZSs5 zeRW&G6VLmTm2Ut#ig4v)FNsLAgs#+S;@~GF*L=B7F}@{kI?rBA7R5&^{^`Lkx2 z;Ar&{9Y^IUg(Hy(Q!+Z#Kd9|8eCGP6HzchTm#tWE5NJ2Sz% zc3%ZldAA2xRBmqi%Gl$L$YWqWrNnk)lK=(5BD*F*3JKO7q2a;0Uw{1P<41j`;GnhB z8_Y2Bkz+H6wL>;RTfJ0K)>S9C5P9`Hv=FO+`9dmnopYK!MZ?PO6e%s{4Z@4Z6eyFU z{a8xDK}zYV6v?ul>5Z$$I|;A3Zzws^@#N8>Ury%Ej_2RddoP0SwviJGW6qLkq#+K= z-WSQR>_`iqH9qzP8x2NRK?cJ`gO$V`3RWP+xame6lEIrTq$#9QB5X_uBQkO#_>R;t zrc_rd$2YoyC4@2de#XZPU6~@o+aFsu!xzSI_`Sjp6y^lJ-hxS{h@z~TOrf-?-~Ry! C?&(_q literal 0 HcmV?d00001 diff --git a/public/index.html b/public/index.html new file mode 100644 index 0000000..9eb8a0d --- /dev/null +++ b/public/index.html @@ -0,0 +1,159 @@ + + + + + + + Whatsapp + + + + + +
+ +
+
+ +
+ +
+
+
+ +
+ + WhatsApp +
+
+ +
+
+ + Transf. +
+ +
+ + Finalizar +
+
+
+
+
+

Bem-vindo

+

Seu canal de atendimento de mensagens!

+ +
+ +
+ + + + + + +
+ +
+
+ + + + +
+ +
+ +
+
+ +
+
+ +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/public/js/cronometro.js b/public/js/cronometro.js new file mode 100644 index 0000000..82a69fa --- /dev/null +++ b/public/js/cronometro.js @@ -0,0 +1,54 @@ +let cron +let segundos = 0; +let minutos = 0; + +$(function(){ + $('#voicerecorder').on('click', function(){ + start() + }) + $('.modal-content-body').on('click', '#stoprecorder', function(){ + pause() + }) +}) + +const addZero = (time) => { + if(time < 10){ + time = "0" + time + } + return time +} + +function start() { + segundos = 0 + minutos = 0 + pause(); + cron = setInterval(() => { + timer(); + }, 990); +} + +function pause() { + clearInterval(cron); +} + +function timer() { + segundos++; + if (segundos == 60) { + minutos++; + segundos = 0; + } + $('#minutes').text(addZero(minutos)) + $('#seconds').text(addZero(segundos)) +} + +function converdata(timestamp, horario_server = false){ + if(horario_server){ + timestamp = timestamp * 1000 + } + + let date = new Date(timestamp); + let hours = date.getHours(); + let minutes = date.getMinutes(); + let formattedTime = addZero(hours) + ':' + addZero(minutes) + return formattedTime +} \ No newline at end of file diff --git a/public/js/jquery-3.6.0.min.js b/public/js/jquery-3.6.0.min.js new file mode 100644 index 0000000..200b54e --- /dev/null +++ b/public/js/jquery-3.6.0.min.js @@ -0,0 +1,2 @@ +/*! jQuery v3.6.0 | (c) OpenJS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType&&"function"!=typeof e.item},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.6.0",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e&&e.namespaceURI,n=e&&(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},j=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||D,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,D=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="",y.option=!!ce.lastChild;var ge={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n",""]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function je(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function De(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function qe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Le(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var _t,zt=[],Ut=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=zt.pop()||S.expando+"_"+wt.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Ut.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Ut.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Ut,"$1"+r):!1!==e.jsonp&&(e.url+=(Tt.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,zt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((_t=E.implementation.createHTMLDocument("").body).innerHTML="
",2===_t.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=Fe(y.pixelPosition,function(e,t){if(t)return t=We(e,n),Pe.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0 { + $(".chat-window").animate({scrollTop: 9999 }, 1); +} + +const modalStart = () => { + $('#footer-content-left').empty() + $('#footer-content-right').empty() + $('.modal-content-body').empty() + $('.modal-header-title').empty() +} + +const startChannelMessage = () => { + $('#typemessagebar').hide() + $('#headerbuttonsagent').hide() + $("#headermediaagent").hide() + $('#chatwindowagent').css({ overflow: 'hidden' }) + $('#uploadfiles').hide() +} + +const startNotification = () => { + $('#typemessagebar').show(); + $('#headerbuttonsagent').show() + $("#headermediaagent").show() + $('#welcometomessage').hide() + $('#chatwindowagent').css({ overflow: 'scroll' }) +} + +const startSendImage = () => { + modalStart() + $("#uploadimage").on('change', function(){ + const file = new FileReader(); + file.readAsDataURL(this.files[0]); + const imgName = this.files[0].name + + file.onload = function(e) { + $('#myImg').remove() + $('#footername').remove() + $('#footersend').remove() + + $('.modal-content-body').append(``) + $('#footer-content-left').append(``) + $('#footer-content-right').append(``) + } + $('#modalselect').show() + }) +} + +function recorderVoice () { + $('#modalselect').show() + modalStart() + + $('.modal-content-body').append(` + `) + + $('#footer-content-right').append(``) + + navigator.mediaDevices.getUserMedia({audio: true}).then( + stream => { + mediaRecorder = new MediaRecorder(stream) + let chunks = [] + + mediaRecorder.ondataavailable = data => { + chunks.push(data.data) + } + + mediaRecorder.onstop = () => { + const blob = new Blob(chunks, {type : 'audio/ogg; code=opus'}) + const reader = new window.FileReader() + reader.readAsDataURL(blob) + reader.onloadend = () => { + const audio = document.createElement('audio') + audio.src = reader.result + audio.controls = true + $('#footer-content-left').append(audio) + } + } + mediaRecorder.start() + }, err => { + // add msg de error + } + ) +} + +$(function(){ + localStorage.setItem('my_uniqueid', 1040) + + /** + * VOICE RECORDER + */ + $('.modal-content-body').on('click', '#stoprecorder',() => { + $('#msgRecorder').text('Paramos de gravar sua voz!') + mediaRecorder.stop() + }) + $('#voicerecorder').on('click', () => { + recorderVoice() + }) + /** FIM VOICE RECORDER */ + + /** EVENTOS DE CLICK NO BODY */ + $("body").mouseup(function(){ + $('#uploadfiles').fadeOut('slow') + }); + + $("#myBtn").on('click', () => modalStart()) + + /** ENVIO DE IMAGEM */ + startSendImage() + + /** INICIA COM O HEADER DO CONTATO VAZIO */ + startChannelMessage() + + /** INICIA O CHAT NO FINAL DA CONVERSA */ + $('.chats').on('click', function(){ + scrollDown() + }) + + /** ENVIA AS MSG PELO ENTER */ + $('#fieldsendmessage').bind('keyup', function(ev){ + if(ev.keyCode == 13){ + sendMessage() + } + }) + + $('.type-message-bar-right').on('click',function(){ + sendMessage() + }) + + $('#imgclip').on('click', function(){ + if($('#uploadfiles').is(':hidden')){ + $('#uploadfiles').fadeIn('slow') + } else { + $('#uploadfiles').fadeOut('slow') + } + }) +}) + +const selectNotification = (id) => { + const dataStorage = JSON.parse(localStorage.getItem('keep_msg')) + const dataContact = dataStorage.data.filter(e => { + if(id.trim() == e.event?.contact.number || (e.de == localStorage.getItem('my_uniqueid') && e.para == id.trim())){ + return true + } + }) + + if(dataContact){ + localStorage.removeItem('session_window') + localStorage.setItem('session_window', dataContact[0].event.contact.number) + + $('.chat-window-contact-name').text(dataContact[0].event.contact.name) + $('.chat-window-contact-status').text('WhatsApp') + + /** REMOVE AS MSG NA E CONSTRIO A TELA NOVAMENTE (EVITAR DUPLICAR) */ + $('.chat-window .sender').remove() + $('.chat-window .receiver').remove() + + dataContact.forEach(e => { + console.log(e) + const datesend = e.event?.mensagem.datetime ? converdata(e.event?.mensagem.datetime, true) : 'algumas horas'; + if(e.event?.mensagem.type == 'text'){ + $('.chat-window').append(`
${e.event.mensagem.content}${datesend}
`) + } + + if(e.de == localStorage.getItem('my_uniqueid')){ + const datereceived = e.datetime ? converdata(e.datetime) : 'algumas horas'; + $('.chat-window').append(`
${e.msg}${datereceived}
`) + } + }) + + startNotification() + } +} + +const sendMessage = () => { + const sendNumber = localStorage.getItem('session_window') + const myUniqueid = localStorage.getItem('my_uniqueid') + let sendText = $('#fieldsendmessage').val() + + if(!sendText){ + return + } + + let dataStorage = JSON.parse(localStorage.getItem('keep_msg')) + if (!dataStorage) { + dataStorage = { + data: [] + } + } + dataStorage.data.push({ para: sendNumber, de: myUniqueid, msg: sendText, type: 'text', datetime: Date.now() }) + /** REMOVE TODOS OS ITENS ANTES PARA NÃO DUPLICAR */ + localStorage.removeItem('keep_msg'); + /** ADD LOCALSTORAGE */ + localStorage.setItem('keep_msg', JSON.stringify(dataStorage)) + + const dataSend = { + "event": { + "type": "mensagem", + "contact": { + "name": "Lucas Awade", + "number": sendNumber, + "matricula": myUniqueid + }, + "mensagem": { + "dst": sendNumber, + "idAtendimento": "43524352345", + "type": "text", + "media": "whatsapp", + "content": sendText, + "status": "sended" + } + } + } + + $.ajax({ + url: `http://${server_api}/api/enviaMsg`, + type: "POST", + data: JSON.stringify(dataSend), + success: function(res){ + console.log(res) + }, + error: function(res){ + $('.chat-window').append(`
MENSAGEM NÃO FOI ENVIADA!
`) + } + }); + + $('.chat-window').append(`
${sendText}${converdata(Date.now())}
`) + scrollDown() + + /** LIMPA O CAMPO DE ENVIO DE MENSAGEM */ + $('#fieldsendmessage').val("") +} + +const viewMessage = (ev) => { + const sessionOpen = localStorage.getItem('session_window') + const datesend = ev.event?.mensagem.datetime ? converdata(ev.event?.mensagem.datetime, true) : 'algumas horas'; + if(ev.event?.contact.number == sessionOpen && ev.event?.mensagem.type == 'text'){ + $('.chat-window').append(`
${ev.event.mensagem.content}${datesend}
`) + } + scrollDown() +} + +const receiveNotification = (data) => { + let contact = JSON.parse(localStorage.getItem('obj_contact')) + if (!contact) { + contact = { + data: [] + } + } + + /** VERIFICA SE JÁ EXIST O NUMERO ENVIADO */ + let validate = contact.data.filter(e => { + if (e.numero == data.event?.contact.number) { + return true + } + }); + + /** VALIDA O NUMERO, VERIFICA SE O TEM ALGMA MSG INICIAL, SE JÁ TEVE UM NUMERO NA VERIFICACAO */ + if (data.event?.contact.number && (contact?.data.length == 0 || validate.length == 0)) { + const datesend = converdata(data.event?.mensagem.datetime) + const chatList = + `
+
+ +
+
+
+ ${data.event?.contact.name} + ${datesend} +
+
+
+ WhatsApp +
+
+ 1 + +
+
+
+
` + + /** CRIA O OBJETO PARA ADD NO LOCALSTORAGE */ + let objt = { + numero: data.event?.contact.number, + chatHTML: chatList + } + /** ADD OBJETO NO ARRAY DO LOCALSTORAGE */ + contact.data.push(objt) + /** REMOVE TODOS OS ITENS ANTES PARA NÃO DUPLICAR */ + localStorage.removeItem('obj_contact'); + /** ADD LOCALSTORAGE */ + localStorage.setItem('obj_contact', JSON.stringify(contact)) + } + + /** REMOVE OS ITENS DO HTML E ADD NOVAMENTE PARA N DUPLICAR AS NOTIFICATIONS */ + contact.data.forEach(e => { + $('#' + e.numero).remove() + $('#chats').append(e.chatHTML) + }); +} + +const keepMensage = (ev) => { + let msg = JSON.parse(localStorage.getItem('keep_msg')) + if(!msg){ + msg = { data: [] } + } + + if(ev.event?.contact && ev.event?.mensagem.content){ + msg.data.push(ev) + localStorage.removeItem('keep_msg'); + localStorage.setItem('keep_msg', JSON.stringify(msg)) + } +} + +ws.addEventListener("open", () => { + // localStorage.removeItem('obj_contact'); + // return; + ws.addEventListener("message", e => { + const data = JSON.parse(e?.data) + console.log(data) + if(data){ + keepMensage(data) + } + /** RECEBE AS PRIMEIRAS MENSAGENS */ + receiveNotification(data) + + if($("#welcometomessage").is(':hidden') == false){ + localStorage.setItem('session_window', null) + } + + /** ATUALIZACAO DA SESSAO CORRENTE */ + viewMessage(data) + }) +}) \ No newline at end of file From bbc60ec3ae992f976aa4bf31f6b6a71e01d96c7d Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Mon, 29 Nov 2021 16:31:53 -0400 Subject: [PATCH 015/144] update semana 26 --- app/Controllers/AgentController.php | 23 +-- app/Controllers/BilheteController.php | 10 +- app/Controllers/ClientController.php | 9 +- app/Core/Connect.php | 22 +-- app/Core/CoreMedia.php | 261 +++++++++++++++----------- app/Core/Media.php | 174 ----------------- app/Core/Model.php | 4 +- app/Interfaces/IApiMedia.php | 5 +- app/Middleware/ApiAgente.php | 44 ++++- app/Middleware/Middleware.php | 20 +- app/Models/Agent.php | 6 +- app/Models/EventQueue.php | 4 +- app/Providers/Positus.php | 71 ++++--- app/Providers/WebHeader.php | 1 - bd | 4 +- config/event.php | 12 +- config/whatsapp.php | 2 +- service/MonitoraAgente.php | 6 +- 18 files changed, 293 insertions(+), 385 deletions(-) delete mode 100644 app/Core/Media.php diff --git a/app/Controllers/AgentController.php b/app/Controllers/AgentController.php index 7dc7654..d1a519c 100644 --- a/app/Controllers/AgentController.php +++ b/app/Controllers/AgentController.php @@ -10,6 +10,7 @@ use app\Models\Pause; use app\Models\Bilhete; use app\Models\EventQueue; use app\Controllers\ClassificacaoController; +use app\Models\Supervisor; use Exception; /** @@ -20,7 +21,7 @@ use Exception; class AgentController extends Controller { - private $agent; + private Supervisor $agent; private $queue; private $ramal; private $pause; @@ -29,7 +30,7 @@ class AgentController extends Controller public function __construct() { - $this->agent = new Agent(); + $this->agent = new Supervisor(); $this->queue = new Queue(); $this->ramal = new Ramal(); $this->pause = new Pause(); @@ -37,12 +38,11 @@ class AgentController extends Controller $this->eventqueue = new EventQueue(); } - public function openService($ramal, $origemDest) + public function openService($ramal, $origemDest, $uniqueid) { 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; @@ -79,7 +79,7 @@ class AgentController extends Controller 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; + $motivo = $atendimento->phone == 'CLIENT' ? CONF_EVENT_TIMERMINO_CLIENTE : CONF_EVENT_TIMERMINO_AGENTE; } $this->eventqueue->addEventQueue($uniqueid, $agent->dac, $agent->matricula, $motivo, $media); /* @@ -116,12 +116,12 @@ class AgentController extends Controller return false; } - public function login($ramal, $login, $fila, $media) + public function login($fila, $matricula) { try { $this->agent->begin(); $queue = $this->queue->findQueueByName(strtoupper($fila)); - $agent = $this->agent->findByAgent(strtolower($login)); + $agent = $this->agent->findByAgent(strtolower($matricula)); if ($queue->midiafila == 'N') { throw new Exception('Fila não relacionada como WhatsApp!'); @@ -135,11 +135,8 @@ class AgentController extends Controller throw new Exception('Usuário não encontrado!'); } - if (!$this->ramal->findRamal($ramal)) { - throw new Exception('Telefone não encontrado!'); - } - if ($this->agent->findByNumber($ramal)) { + if ($this->agent->findByNumber($matricula)) { throw new Exception('Agente já autenticado!'); } @@ -149,11 +146,11 @@ class AgentController extends Controller $classificacao = new ClassificacaoController(); $chamadaSemClassificacao = $classificacao->agentClassificacaoPending($agent->matricula, $fila); - if (!$this->agent->addAgent($agent->matricula, $ramal, $login, $queue->nome, $media, ($chamadaSemClassificacao ? 0 : 1))) { + if (!$this->agent->addAgent($agent->matricula, $fila, ($chamadaSemClassificacao ? 0 : 1))) { throw new Exception('Não foi possével inserir o agente!'); } - if (!$this->agent->addEventoLoginAgent($agent->matricula, $ramal, $queue->id)) { + if (!$this->agent->addEventoLoginAgent($agent->matricula, $queue->id)) { throw new Exception('Não foi possível inserir as informações de autenticação do login!'); } $this->agent->commit(); diff --git a/app/Controllers/BilheteController.php b/app/Controllers/BilheteController.php index 923d394..b7e9a63 100644 --- a/app/Controllers/BilheteController.php +++ b/app/Controllers/BilheteController.php @@ -62,7 +62,7 @@ class BilheteController extends Controller $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->eventqueue->updateEventQueue($uniqueid, $queue, $agent, CONF_EVENT_TIMERMINO_AGENTE, $tempoEspera, $newuniqueid); $this->bilhete->commit(); } catch (Exception $ex) { $this->bilhete->rollback(); @@ -76,13 +76,13 @@ class BilheteController extends Controller { try { $this->bilhete->begin(); - $bilhetesForaHorario = $this->bilhete->findBilheteBySrc($ramal, CONF_EVENT_WHATSAPP_ESPERA); + $bilhetesForaHorario = $this->bilhete->findBilheteBySrc($ramal, CONF_EVENT_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->updateBilheteForaHorario($bilhetesForaHorario->uniqueid, $ramal, '0', CONF_EVENT_ABANDONADA); + $this->eventqueue->updateEventQueue($bilhetesForaHorario->uniqueid, $bilhetesForaHorario->fila, $bilhetesForaHorario->agente, CONF_EVENT_ABANDONADA); $this->bilhete->commit(); return true; @@ -178,4 +178,4 @@ class BilheteController extends Controller } return false; } -} \ No newline at end of file +} diff --git a/app/Controllers/ClientController.php b/app/Controllers/ClientController.php index ff1a2e6..41ed1b1 100644 --- a/app/Controllers/ClientController.php +++ b/app/Controllers/ClientController.php @@ -4,6 +4,7 @@ namespace app\Controllers; use app\Core\Controller; use app\Controllers\BilheteController; +use app\Models\Atendimento; /** * Description of ClientController @@ -15,14 +16,14 @@ class ClientController extends Controller public function getClientQueueData($number) { - $bilheteController = new BilheteController(); - $atendimentoFila = $bilheteController->bilheteForaHorario(CONF_EVENT_WHATSAPP_ESPERA); + $atendimento = new Atendimento(); + $atendimentoFila = $atendimento->getAtendimentoByEvento(); $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 +36,7 @@ class ClientController extends Controller foreach ($queueClientData as $clientes) { $queuePosition += 1; - if ($clientes->src == $number) { + if ($clientes->cliente_id == $number) { break; } } diff --git a/app/Core/Connect.php b/app/Core/Connect.php index 1e71a80..143914a 100644 --- a/app/Core/Connect.php +++ b/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=" . CONF_DB_HOST . - ";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, diff --git a/app/Core/CoreMedia.php b/app/Core/CoreMedia.php index f47a681..b67db69 100644 --- a/app/Core/CoreMedia.php +++ b/app/Core/CoreMedia.php @@ -3,14 +3,17 @@ 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\Interfaces\IApiMedia; +use app\Models\Agent; +use app\Models\Atendimento; +use app\Models\Evento; use app\Models\ListaNegraPalavras; use app\Models\Ramal; +use app\Models\Supervisor; use websocket\WsInterface; /** @@ -20,28 +23,32 @@ use websocket\WsInterface; */ class CoreMedia { - private $api; - private $crypt; + private IApiMedia $api; private $file; - private $host; private $request; private $commands; - private $media; private $queue; - private $agente; + private AgentController $agente; private $palavroes; private $ws; + private $atendimento; + private Supervisor $supervisor; + private Evento $eventos; + private $host; + private $testeee; 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 Supervisor(); + $this->eventos = new Evento(); } /** @@ -52,29 +59,14 @@ class CoreMedia public function inicia($data, IApiMedia $api) { /** Validate $data */ + $this->build($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; + if (!$this->api->baixarMidia()) { + $this->api->enviarMsg($this->api->getPhone(), utf8_encode("Não foi possivel entregar o aquivo para o agente. Envie novamente!")); } - $this->ws->enviaMsg($api->convertToWebsocket($this->request, '1022', time())); - /** - * VERIFICA SE N?O MENSAGEM DO POSITUS DE CONFIRMA??O - */ - if (!$this->api->getIsValidMessage()) { - return null; - } - /** - * VERIFICA SE ? UM COMANDO - */ + // 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())); @@ -111,28 +103,50 @@ class CoreMedia } 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; - } + $this->ws->enviaMsg( + $this->api->convertToWebsocket( + $this->retornaConteudo(), + '1040', // $atendiment->matricula, + time(), + $this->api->getType(), + $this->api->getProfile(), + $this->api->getPhone(), + time(), + $this->api->getId(), + $this->api->getMimetype() //$atendiment->uniqueid + ) + ); + //verifica se tem atendimento em aberto, se tiver ja manda msg para o agente via ws + $atendiment = $this->supervisor->findAtendimento($this->api->getPhone()); + if ($atendiment) { + $this->ws->enviaMsg($this->api->convertToWebsocket( + $this->retornaConteudo(), + $atendiment->matricula, + $atendiment->uniqueid, + $this->api->getType(), + $this->api->getProfile(), + $this->api->getPhone(), + time(), + $this->api->getId(), + $this->api->getMimetype() + )); + return null; } - - /** - * VALIDA O ATENDIMENTO - */ - $this->validaAtendimento($this->api, $filas['QUEUE'], $data, $this->api->getPhone(), $this->api->channel); + //verifica se tem atendimento em espera, se tiver ja mostra a sua posição na fila + $atende = $this->atendimento->getAtendimentoByID($this->api->getPhone()); + if ($atende) { + $this->mostraPosiscaoNaFila(); + return null; + } + //verifica se o cliente escolheu uma fila caso não escolheu manda as filas + $filas = $this->queue->listAllQueueWhatsApp($this->api->getMessage()); + if ($filas['LIST']) { + $this->api->enviarMsg($this->api->getPhone(), utf8_encode($filas['LIST'])); + return null; + } + // cria a atendimento + $this->criaAtendimento($filas['QUEUE']); } - /** * Set variables class * @param array $data @@ -140,20 +154,8 @@ 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->request = $data; + $this->validaPalavroes(); } public function validaPalavroes() @@ -180,60 +182,55 @@ class CoreMedia } return; } - public function validaAtendimento($api, $fila, $data, $numero, $media) + public function criaAtendimento(string $fila) { - $response = $this->media->selecionaAtendimento($numero, $fila, $api->getMessage(), $media); - if (!$response['STATUS']) { - $api->enviarMsg($numero, utf8_encode($response['MESSAGE'])); + + $uniqueid = $this->atendimento->createAtendimento(null, $this->api->getPhone(), 'E', $this->api->getchannel()); + 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($this->api->getchannel(), $fila); + if (empty($agentes)) { + $this->api->enviarMsg( + $this->api->getPhone(), + utf8_encode('Não temos nenhum atendente disponível no momento, iremos lhe atender assim que um atendente estiver disponível!') + ); return null; } - 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; + $agentModel = new Agent(); + $agentModel->findAgentByQueue($fila); + $agent = $agentModel->findAgentByQueue($fila, 'LIVRE', 'tempo_livre', 'DESC'); + + if (empty($agent)) { + $this->api->enviarMsg( + $this->api->getPhone(), + utf8_encode('Nossos atendentes estão ocupados, por favor aguarde que iremos lhe atender!') + ); + $this->mostraPosiscaoNaFila(); + return null; } + return null; - switch ($api->getType()) { - case 'text': - $this->ws->enviaMsg($api->getMessage()); - $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; - 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; - 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; + $retServe = $this->agente->openService($agent[0]->ramal, $this->api->getPhone(), $uniqueid); + if (empty($retServe)) { + $this->api->enviarMsg( + $this->api->getPhone(), + utf8_encode('Erro ao gerar atendimento!') + ); + return null; } + $protocol = $this->bilheteController->generateProtocol($uniqueid); + $this->bilheteController->tempoEsperaSupervisor($uniqueid); + $this->api->enviarMsg( + $this->api->getPhone(), + utf8_encode("Atendimento iniciado!\n") + ); + $this->api->enviarMsg( + $this->api->getPhone(), + utf8_encode("Número do protocolo do atendimento é $protocol\n") + ); + return null; } private function enviaLista($listas, $api, $numero, $nome, $pre = '') @@ -247,6 +244,15 @@ class CoreMedia ); } + public function mostraPosiscaoNaFila() + { + $clientfila = $this->queue->clientQueueVerify($this->api->getPhone()); + if ($clientfila['MESSAGE']) { + $this->api->enviarMsg($this->api->getPhone(), utf8_encode($clientfila['MESSAGE'])); + return null; + } + } + public function listaFilas() { $ramal = new Ramal(); @@ -272,4 +278,43 @@ class CoreMedia } return $listaPausas; } -} \ No newline at end of file + + public function retornaConteudo($atendiment = null) + { + 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 $this->api->getMessage(); + case 'image': + //$this->api->enviaImagem($response['DATA']['ramal'], $this->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 $this->api->getLinkDownload($this->host); + 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 $this->api->getLinkDownload($this->host); + 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 $this->api->getLinkDownload($this->host); + 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 $this->api->getLinkDownload($this->host); + 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 $this->api->getLinkDownload($this->host); + 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; + } + } +} diff --git a/app/Core/Media.php b/app/Core/Media.php deleted file mode 100644 index 1ceb055..0000000 --- a/app/Core/Media.php +++ /dev/null @@ -1,174 +0,0 @@ -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); - } -} \ No newline at end of file diff --git a/app/Core/Model.php b/app/Core/Model.php index ac5d3e0..0156ca0 100644 --- a/app/Core/Model.php +++ b/app/Core/Model.php @@ -84,9 +84,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) { diff --git a/app/Interfaces/IApiMedia.php b/app/Interfaces/IApiMedia.php index 915da99..b9a0d76 100644 --- a/app/Interfaces/IApiMedia.php +++ b/app/Interfaces/IApiMedia.php @@ -4,6 +4,7 @@ namespace app\Interfaces; interface IApiMedia { + function getchannel(); function enviarMsg($whatsapp, $mensagem); function enviarMsgIterativaLista($whatsapp, $mensagem, $nomeButton, $sections); function enviarMsgIterativaBotao($whatsapp, $mensagem, $buttons); @@ -14,7 +15,7 @@ interface IApiMedia function enviaSticker($whatsapp, $link); function enviaVideo($whatsapp, $link, $titulo = null); function enviaAudio($whatsapp, $link); - function baixarMidia($name); + function baixarMidia(); function setStorage($storage); function getProfile(); function getPhone(); @@ -36,5 +37,5 @@ interface IApiMedia function response($result); public function getLinkDownload($host); public function retornaTituloDocument($msg); - public function convertToWebsocket($msg, $matricula, $atendimento); + public function convertToWebsocket($content, $matricula = '', $idAtendimento, $type, $name, $number, $data, $idProvedor, $mimetype); } \ No newline at end of file diff --git a/app/Middleware/ApiAgente.php b/app/Middleware/ApiAgente.php index cb3a614..d2faef5 100644 --- a/app/Middleware/ApiAgente.php +++ b/app/Middleware/ApiAgente.php @@ -5,9 +5,7 @@ namespace app\Middleware; use app\Controllers\AgentController; use app\Controllers\ClassificacaoController; use app\Controllers\QueueController; -use app\Core\Media; use app\Models\Message; -use app\Models\Ramal; use app\Providers\Positus; class ApiAgente @@ -39,6 +37,9 @@ class ApiAgente case 'enviaMsg': $this->enviaMsg($request); break; + case 'getArquivo': + $this->getArquivo($request); + break; default: echo json_encode(['status' => '404']); break; @@ -48,10 +49,8 @@ class ApiAgente function login($request) { $ret = $this->agentController->login( - $request['ramal'], $request['login'], - $request['fila'], - 'WHATSAPP' + $request['fila'] ); echo json_encode(['status' => $ret]); return $ret; @@ -117,7 +116,6 @@ class ApiAgente echo json_encode(['status' => "false"]); return null; } - public function enviaMsg($request) { try { @@ -125,13 +123,26 @@ class ApiAgente $contact = $request['event']['contact']; $provider = new Positus(); $modelMensagem = new Message(); - $retuno = $provider->enviarMsg($mensagem['dst'], $mensagem['content']); + if ($mensagem['type'] == 'text') { + $retuno = $provider->enviarMsg($mensagem['dst'], $mensagem['content']); + } else { + $anmeArquivo = __DIR__ . "/../../storage/files/" . $mensagem['id_provedor']; + $texto = base64_decode($mensagem['content']); + file_put_contents($anmeArquivo, $texto); + $retuno = $provider->enviarMedia( + $mensagem['dst'], + 'http://voip.simplesip.com.br:8090/link/' . $mensagem['id_provedor'] . '/' . $mensagem['mimetype'], + $mensagem['id_provedor'], + $mensagem['type'] + ); + } + $modelMensagem->addMessage( $mensagem['idAtendimento'], $contact['matricula'], $mensagem['dst'], $mensagem['type'], - $mensagem['content'], + $mensagem['type'] == 'text' ? $mensagem['content'] : $mensagem['id_provedor'], $contact['name'], $mensagem['media'], $mensagem['status'] @@ -143,4 +154,19 @@ class ApiAgente return null; } } -} \ No newline at end of file + + public function envia() + { + } + + function getArquivo($request) + { + try { + $file = file_get_contents(__DIR__ . "/../../storage/files/" . $request['file']); + echo json_encode(['file' => base64_encode($file)]); + } catch (\Throwable $th) { + echo json_encode(['status' => false, 'message' => '' . $th->getMessage()]); + } + return null; + } +} diff --git a/app/Middleware/Middleware.php b/app/Middleware/Middleware.php index 6204cf0..7d86320 100644 --- a/app/Middleware/Middleware.php +++ b/app/Middleware/Middleware.php @@ -46,7 +46,7 @@ class Middleware extends Http } /** - * Redirect request to Media Social + * Redirect request to media Social * @return null */ private function router() @@ -54,26 +54,24 @@ class Middleware extends Http $coremedia = new CoreMedia(); $apiAgente = new ApiAgente(); $this->baseUri(); - $data = [ - "REQUEST" => $this->request, - "HOST" => $this->getLink(), - "DOWNLOAD" => $this->download() - ]; + // $ws = new WsInterface(); + // $ws->enviaMsg(json_encode($this->request)); + switch (strtolower($this->param()[1])) { case 'whatsapp': - $file = $coremedia->inicia($data, new Positus()); - if ($file) { - $this->download($file); - } + $coremedia->inicia($this->request, new Positus()); echo json_encode(['success' => true]); return null; case 'telegram': - $file = $coremedia->inicia($data, new ApiTelegram()); + $coremedia->inicia($this->request, new ApiTelegram()); echo json_encode(['success' => $this->request]); return null; case 'api': $apiAgente->router($this->param()[2], $this->request); return null; + case 'link': + $this->header->fileTransfer($this->param()[2], "/var/www/html/storage/files/" . $this->param()[2], base64_decode($this->param()[3])); + exit(0); default: $this->header->redirect(); } diff --git a/app/Models/Agent.php b/app/Models/Agent.php index 4819e7e..493e021 100644 --- a/app/Models/Agent.php +++ b/app/Models/Agent.php @@ -28,7 +28,7 @@ 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 = []; @@ -88,8 +88,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) { diff --git a/app/Models/EventQueue.php b/app/Models/EventQueue.php index 812839f..19cf067 100644 --- a/app/Models/EventQueue.php +++ b/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]); } -} \ No newline at end of file +} diff --git a/app/Providers/Positus.php b/app/Providers/Positus.php index 00286b4..7d3c7e8 100644 --- a/app/Providers/Positus.php +++ b/app/Providers/Positus.php @@ -20,39 +20,62 @@ class Positus implements IApiMedia private $request; private $params = array(); private $hook; - private $storage = "/var/www/html/aplicativo/audio/"; + private $storage = __DIR__ . "/../../storage/files/"; public $channel = CONF_WHATSAPP_CHANNEL; public $timeout_client_resposta = CONF_WHATSAPP_TIMEOUT_CLIENT_RESPOSTA; ######################################################################## ## RECURSOS DA API ## ######################################################################## - public function convertToWebsocket($msg, $matricula = '', $atendimento) + public function getchannel() { - if ($msg['contacts']) { + return CONF_WHATSAPP_CHANNEL; + } + public function convertToWebsocket($content, $matricula = '', $uniqueid, $type, $name, $number, $data, $idProvedor, $mimetype) + { + if ($number) { $mensagem = []; $mensagem["event"] = [ "type" => "mensagem", "contact" => [ - "name" => $msg['contacts'][0]['profile']['name'], - "number" => $msg['contacts'][0]['wa_id'], + "name" => $name, + "number" => $number, "matricula" => '' ], "mensagem" => [ - "type" => $msg['messages'][0]['type'], - "content" => $msg['messages'][0]['text']['body'], - "id" => "425435243", + "type" => $type, + "content" => $content, + "id_provedor" => $idProvedor, "dst" => $matricula, - "idAtendimento" => $atendimento, + "uniqueid" => $uniqueid, "media" => $this->channel, - "datetime" => $msg['messages'][0]['timestamp'], - "status" => "sent" + "datetime" => $data, + "status" => "sent", + 'mimetype' => $mimetype ] ]; return json_encode($mensagem); } - return ""; + return null; + } + + function enviarMedia($whatsapp, $link, $titulo = null, $type) + { + $this->debug = debug_backtrace(); + if ($this->getArgs(func_get_args())) { + $this->params = array( + "to" => "+$whatsapp", + "type" => "$type", + "$type" => array( + "link" => "$link" + ) + ); + $this->requestType("POST"); + $this->setMetodo('messages'); + return $this->exec(); + } + return false; } function enviarMsg($whatsapp, $mensagem) @@ -280,16 +303,20 @@ class Positus implements IApiMedia return false; } - function baixarMidia($name) + function baixarMidia() { + if (in_array($this->getType(), ['location', 'contacts', 'text'])) { + return true; + } + $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; + file_put_contents($pathfile, $this->exec()); + if (file_exists($pathfile)) { + return true; } } return false; @@ -337,7 +364,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; } @@ -349,6 +376,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; @@ -360,8 +390,7 @@ class Positus implements IApiMedia */ public function getIsValidMessage() { - $message = $this->hook['messages']; - return ($message ? $message : false); + return $this->hook['messages']; } /** @@ -552,4 +581,4 @@ class Positus implements IApiMedia $titulo = substr($titulo, 0, strrpos($titulo, ".")); return $titulo; } -} \ No newline at end of file +} diff --git a/app/Providers/WebHeader.php b/app/Providers/WebHeader.php index 9b7220f..9794b87 100644 --- a/app/Providers/WebHeader.php +++ b/app/Providers/WebHeader.php @@ -178,7 +178,6 @@ class WebHeader ob_end_clean(); ob_start(); readfile($file); - unlink($file); ob_flush(); } diff --git a/bd b/bd index 6912bf8..021c7c0 100644 --- a/bd +++ b/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" \ No newline at end of file diff --git a/config/event.php b/config/event.php index f88b36d..34b37fa 100644 --- a/config/event.php +++ b/config/event.php @@ -10,11 +10,11 @@ */ // 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_WHATSAPP_OCUPADO", 'MD_OCUPADO'); \ No newline at end of file diff --git a/config/whatsapp.php b/config/whatsapp.php index fdf9567..bb01d6f 100644 --- a/config/whatsapp.php +++ b/config/whatsapp.php @@ -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'); /* |-------------------------------------------------------------------------- diff --git a/service/MonitoraAgente.php b/service/MonitoraAgente.php index c6d7ff1..6bdbfbb 100644 --- a/service/MonitoraAgente.php +++ b/service/MonitoraAgente.php @@ -98,14 +98,14 @@ class MonitoraAgente continue; } - $atendimento = $this->bilhete->bilheteForaHorario(CONF_EVENT_WHATSAPP_ESPERA, $item['FILA'], null, $item['SALA1']); + $atendimento = $this->bilhete->bilheteForaHorario(CONF_EVENT_ESPERA, $item['FILA'], null, $item['SALA1']); if ($atendimento) { /** * VALIDA O ATENDIMENTO */ $response = $this->media->validaAtendimento($positus, $atendimento[0]->fila, null, $atendimento[0]->src, $item['SALA1']); $this->bilhete->atenderBilheteForaHorario($atendimento[0]->uniqueid, $atendimento[0]->src, $atendimento[0]->fila, $item['MATRICULA'], $response['DATA']['uniqueid']); - $atendimento = $this->bilhete->bilheteForaHorario(CONF_EVENT_WHATSAPP_ESPERA, $item['FILA'], null, $item['SALA1']); + $atendimento = $this->bilhete->bilheteForaHorario(CONF_EVENT_ESPERA, $item['FILA'], null, $item['SALA1']); foreach ($atendimento as $cliente) { $mensagemFila = 'Você está na posição ' . $this->media2->getClientQueueData($cliente->src, $atendimento)['QUEUE_POSITION'] . '. Aguarde ser atendido!'; $positus->enviarMsg($cliente->src, utf8_encode($mensagemFila)); @@ -114,7 +114,7 @@ class MonitoraAgente } if ($item['STATUS'] == CONF_AGENT_STATUS_OCUPADO) { - if ($this->message->timeoutTalk($item['UNIQUEID'], $item['RAMAL']) == 'FINISH' && $this->agente->closeService($item['ORIGEM_DESTINO'], CONF_EVENT_WHATSAPP_TIMEOUT_CLIENTE, $item['SALA1'])) { + if ($this->message->timeoutTalk($item['UNIQUEID'], $item['RAMAL']) == 'FINISH' && $this->agente->closeService($item['ORIGEM_DESTINO'], CONF_EVENT_TIMEOUT_CLIENTE, $item['SALA1'])) { $positus->enviarMsg($item['ORIGEM_DESTINO'], utf8_encode($this->mensagem['TIMEOUT_CLIENT_INATIVIDADE'])); $positus->enviarMsg($item['ORIGEM_DESTINO'], utf8_encode($this->mensagem['TALK_FINISHED'])); $positus->enviarMsg($item['RAMAL'], utf8_encode($this->mensagem['TALK_FINISHED'])); From 518278c4406911a5e1fdfe33f934c9ce2f97a464 Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Mon, 29 Nov 2021 16:39:55 -0400 Subject: [PATCH 016/144] add novas models --- app/Models/Atendimento.php | 60 ++++++++++ app/Models/Evento.php | 34 ++++++ app/Models/Supervisor.php | 227 +++++++++++++++++++++++++++++++++++++ 3 files changed, 321 insertions(+) create mode 100644 app/Models/Atendimento.php create mode 100644 app/Models/Evento.php create mode 100644 app/Models/Supervisor.php diff --git a/app/Models/Atendimento.php b/app/Models/Atendimento.php new file mode 100644 index 0000000..1fd1e8b --- /dev/null +++ b/app/Models/Atendimento.php @@ -0,0 +1,60 @@ +query = "SELECT ma.* FROM {$this->atendimento} ma + INNER JOIN {$this->evento} me + ON ma.uniqueid = me.uniqueid + WHERE ma.cliente_id = :cliente_id + AND me.evento = :evento"; + return $this->read($this->query, ['cliente_id' => $cliente_id, 'evento' => $evento])->fetch(); + } + + public function getAtendimentoByEvento($evento = 'EMESPERA') + { + $this->query = "SELECT ma.* FROM {$this->atendimento} ma + INNER JOIN {$this->evento} me + ON ma.uniqueid = me.uniqueid + WHERE me.evento = :evento"; + return $this->read($this->query, ['evento' => $evento])->fetchAll(); + } + public function createAtendimento($matricula, $cliente_id, $direcao, $context) + { + $unique = uniqid('', true); + $this->query = "INSERT INTO {$this->atendimento} ( + matricula, + cliente_id, + direcao, + uniqueid, + context) + VALUES( + :matricula, + :cliente_id, + :direcao, + :uniqueid, + :context);"; + + $data['uniqueid'] = $unique; + $data['matricula'] = $matricula; + $data['cliente_id'] = $cliente_id; + $data['direcao'] = $direcao; + $data['context'] = $context; + + $return = $this->create($this->query, $data); + if ($return) { + return $unique; + } + + return $return; + } +} \ No newline at end of file diff --git a/app/Models/Evento.php b/app/Models/Evento.php new file mode 100644 index 0000000..fc34023 --- /dev/null +++ b/app/Models/Evento.php @@ -0,0 +1,34 @@ +query = "INSERT INTO {$this->evento} (uniqueid, + evento, + data_evento, + data_reg, + fila ) + VALUES(:uniqueid, + :evento, + :data_evento, + :data_reg, + :fila );"; + + $data['uniqueid'] = $uniqueid; + $data['evento'] = $evento; + $data['data_evento'] = $data_evento; + $data['data_reg'] = $data_reg; + $data['fila'] = $fila; + + $return = $this->create($this->query, $data); + return $return; + } +} \ No newline at end of file diff --git a/app/Models/Supervisor.php b/app/Models/Supervisor.php new file mode 100644 index 0000000..d52f1dc --- /dev/null +++ b/app/Models/Supervisor.php @@ -0,0 +1,227 @@ +query = "SELECT ma.* FROM {$this->supervisor} ms + INNER JOIN {$this->atendimento} ma + ON ma.uniqueid = ms.uniqueid + WHERE ms.cliente_id = :cliente_id"; + return $this->read($this->query, ['cliente_id' => $cliente_id])->fetch(); + } + + public function findByNumber($matricula) + { + $this->query = "SELECT * " + . "FROM " . self::SUPERVISOR_AGENTE . " WHERE (matricula = :matricula)"; + return $this->read($this->query, ['matricula' => $matricula])->fetch(); + } + + 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()); + return []; + } + } + + 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 findAgentByQueue($queue, $status = 'LIVRE', $orderBy = null, $orderByType = 'ASC') + { + $data = []; + $this->query = "SELECT ramal, b.matricula, b.nome, b.apelido, penalidade, (logado - duracao) as tempo_livre, + (SELECT count(*) AS atendimentos + FROM " . self::BILHETE . " ab + INNER JOIN " . self::EVENTOS_DACS . " bc ON ab.uniqueid = bc.uid2 + WHERE bc.fila = a.dac + AND bc.agente = a.matricula + AND bc.evento IN(:evento) + AND ab.data_bilhete = 'now') + FROM " . self::SUPERVISOR_AGENTE . " a + INNER JOIN " . self::USUARIOS . " b ON a.matricula = b.matricula + WHERE upper(a.dac) = upper(:queue) + AND a.status = upper(:status) "; + + $data['queue'] = $queue; + $data['status'] = $status; + $data['evento'] = implode(',', [ + CONF_EVENT_TIMERMINO_CLIENTE, + CONF_EVENT_TIMERMINO_AGENTE + ]); + + if ($orderBy && $orderByType) { + $this->query .= " ORDER BY $orderBy $orderByType;"; + } + + return $this->read($this->query, $data)->fetchAll(); + } + + ######################################## + ### SUPERVISOR AGENTE ### + ######################################## + + public function addAgent($matricula, $queue, $chamada_classificado = 1) + { + $this->query = "INSERT INTO " . self::SUPERVISOR_AGENTE . " + (ramal, matricula, nome, tempo_login, modo_atendimento, dac, status, duracao, logado, status_time, disponivel_atendimento, sala_1, chamada_classificado) + VALUES(:ramal, :matricula, :login, :tempo_login, :modo_atendimento, :queue, :status, :duracao, :logado, :status_time, :disponivel_atendimento, :media, :chamada_classificado);"; + + return $this->create($this->query, [ + 'matricula' => $matricula, + 'tempo_login' => 'now()', + 'queue' => $queue, + 'status' => 'LIVRE', + 'duracao' => 'now()', + 'logado' => 'now()', + 'status_time' => 'now()', + 'disponivel_atendimento' => 1, + 'chamada_classificado' => $chamada_classificado + ]); + } + + public function updateAgent($matricula, $ramal, $status = 'LIVRE', $origemDestino = null, $motivoPausa = null, $duracao = null, $uniqueid = null, $chamada_classificado = 1) + { + $data = []; + $this->query = "UPDATE " . self::SUPERVISOR_AGENTE . " SET logado = :logado, origem_destino = :origem_destino, status = :status, motivo_pausa = :motivo_pausa, uniqueid = :uniqueid, chamada_classificado = :chamada_classificado"; + if ($duracao) { + $this->query .= ", duracao = :duracao "; + } + $this->query .= " WHERE matricula = :matricula AND ramal = :ramal;"; + + $data['matricula'] = $matricula; + $data['ramal'] = $ramal; + $data['logado'] = 'now()'; + $data['duracao'] = 'now()'; + $data['origem_destino'] = $origemDestino; + $data['status'] = $status; + $data['motivo_pausa'] = $motivoPausa; + $data['uniqueid'] = $uniqueid; + $data['chamada_classificado'] = ($chamada_classificado ? '1' : '0'); + 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 deleteAgent($matricula, $ramal) + { + $data = []; + $this->query = "DELETE FROM " . self::SUPERVISOR_AGENTE . " WHERE matricula = :matricula AND ramal = :ramal;"; + $data['matricula'] = $matricula; + $data['ramal'] = $ramal; + 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) + { + $data = []; + $this->query = "INSERT INTO " . self::EVENTO_AGENTE . "(matricula, login, logoff, id_dac, flag) VALUES(:matricula, :login, :logoff, :id_dac, :flag) "; + $data['matricula'] = $matricula; + $data['login'] = 'now()'; + $data['logoff'] = 'now()'; + $data['id_dac'] = $idDac; + $data['flag'] = $flag; + 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); + } +} \ No newline at end of file From fbba7ca3107453f44f9b82217cdf4187ec484b40 Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Tue, 30 Nov 2021 10:34:10 -0400 Subject: [PATCH 017/144] add metodo na api de logoff --- app/Controllers/AgentController.php | 34 ++++++++++----------- app/Middleware/ApiAgente.php | 47 ++++++++++++++++++++++++----- app/Models/Supervisor.php | 37 +++++++++++++++-------- 3 files changed, 81 insertions(+), 37 deletions(-) diff --git a/app/Controllers/AgentController.php b/app/Controllers/AgentController.php index d1a519c..8de0574 100644 --- a/app/Controllers/AgentController.php +++ b/app/Controllers/AgentController.php @@ -124,34 +124,34 @@ class AgentController extends Controller $agent = $this->agent->findByAgent(strtolower($matricula)); if ($queue->midiafila == 'N') { - throw new Exception('Fila não relacionada como WhatsApp!'); + return 'Fila não relacionada como WhatsApp!'; } if (!$queue) { - throw new Exception('Fila não encontrada ou não relacionada como WhatsApp!'); + return 'Fila não encontrada ou não relacionada como WhatsApp!'; } if (!$agent) { - throw new Exception('Usuário não encontrado!'); + return 'Usuário não encontrado!'; } if ($this->agent->findByNumber($matricula)) { - throw new Exception('Agente já autenticado!'); + return 'Agente já autenticado!'; } - /** * VERIFICA CLASSIFICACAO ATENDIMENTO */ - $classificacao = new ClassificacaoController(); - $chamadaSemClassificacao = $classificacao->agentClassificacaoPending($agent->matricula, $fila); + // $classificacao = new ClassificacaoController(); + // $chamadaSemClassificacao = $classificacao->agentClassificacaoPending($agent->matricula, $fila); - if (!$this->agent->addAgent($agent->matricula, $fila, ($chamadaSemClassificacao ? 0 : 1))) { - throw new Exception('Não foi possével inserir o agente!'); + if (!$this->agent->addAgent($agent->matricula, $fila)) { + return 'Não foi possével inserir o agente!'; } - if (!$this->agent->addEventoLoginAgent($agent->matricula, $queue->id)) { - throw new Exception('Não foi possível inserir as informações de autenticação do login!'); + + 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 true; @@ -164,16 +164,16 @@ class AgentController extends Controller return false; } - public function logoff($ramal) + public function logoff($matricula) { try { $this->agent->begin(); - $agent = $this->agent->findAgentByRamal($ramal); + $agent = $this->agent->findAgentByMatricula($matricula); if (!$agent) { throw new Exception('Telefone não encontrado!'); } - $queue = $this->queue->findQueueByName($agent->dac); + $queue = $this->queue->findQueueByName($agent->fila); if (!$queue) { throw new Exception('Agente não conectado!'); } @@ -187,8 +187,8 @@ class AgentController extends Controller throw new Exception(''); } - $this->agent->updateEventoLogoffAgent($agent->matricula, $ramal, $queue->id); - $this->agent->deleteAgent($agent->matricula, $ramal); + $this->agent->updateEventoLogoffAgent($agent->matricula, '0', $queue->id); + $this->agent->deleteAgent($agent->matricula); $this->agent->commit(); return true; } catch (Exception $ex) { @@ -497,4 +497,4 @@ class AgentController extends Controller return false; } } -} \ No newline at end of file +} diff --git a/app/Middleware/ApiAgente.php b/app/Middleware/ApiAgente.php index d2faef5..8217a01 100644 --- a/app/Middleware/ApiAgente.php +++ b/app/Middleware/ApiAgente.php @@ -40,6 +40,9 @@ class ApiAgente case 'getArquivo': $this->getArquivo($request); break; + case 'logoff': + $this->logoff($request); + break; default: echo json_encode(['status' => '404']); break; @@ -48,12 +51,43 @@ class ApiAgente function login($request) { - $ret = $this->agentController->login( - $request['login'], - $request['fila'] - ); - echo json_encode(['status' => $ret]); - return $ret; + try { + $ret = $this->agentController->login( + $request['fila'], + $request['matricula'] + ); + + echo json_encode([ + 'status' => $ret, + "mensagem" => $ret ? 'sucesso' : 'falha' + ]); + } catch (\Throwable $th) { + echo json_encode([ + 'status' => $ret, + "mensagem" => $th->getMessage() + ]); + } + return null; + } + + function logoff($request) + { + try { + $ret = $this->agentController->logoff( + $request['matricula'] + ); + + echo json_encode([ + 'status' => $ret, + "mensagem" => $ret ? 'sucesso' : 'falha' + ]); + } catch (\Throwable $th) { + echo json_encode([ + 'status' => $ret, + "mensagem" => $th->getMessage() + ]); + } + return null; } public function listaFilas() @@ -136,7 +170,6 @@ class ApiAgente $mensagem['type'] ); } - $modelMensagem->addMessage( $mensagem['idAtendimento'], $contact['matricula'], diff --git a/app/Models/Supervisor.php b/app/Models/Supervisor.php index d52f1dc..2b25bde 100644 --- a/app/Models/Supervisor.php +++ b/app/Models/Supervisor.php @@ -107,21 +107,32 @@ class Supervisor extends Model ### SUPERVISOR AGENTE ### ######################################## - public function addAgent($matricula, $queue, $chamada_classificado = 1) + public function addAgent($matricula, $fila, $chamada_classificado = 1) { $this->query = "INSERT INTO " . self::SUPERVISOR_AGENTE . " - (ramal, matricula, nome, tempo_login, modo_atendimento, dac, status, duracao, logado, status_time, disponivel_atendimento, sala_1, chamada_classificado) - VALUES(:ramal, :matricula, :login, :tempo_login, :modo_atendimento, :queue, :status, :duracao, :logado, :status_time, :disponivel_atendimento, :media, :chamada_classificado);"; + ( + matricula, + tempo_login, + fila, + status, + duracao, + chamada_classificado + ) + VALUES( + :matricula, + :tempo_login, + :fila, + :status, + :duracao, + :chamada_classificado + );"; return $this->create($this->query, [ 'matricula' => $matricula, 'tempo_login' => 'now()', - 'queue' => $queue, + 'fila' => $fila, 'status' => 'LIVRE', 'duracao' => 'now()', - 'logado' => 'now()', - 'status_time' => 'now()', - 'disponivel_atendimento' => 1, 'chamada_classificado' => $chamada_classificado ]); } @@ -173,12 +184,11 @@ class Supervisor extends Model return $this->update($this->query, $data); } - public function deleteAgent($matricula, $ramal) + public function deleteAgent($matricula) { $data = []; - $this->query = "DELETE FROM " . self::SUPERVISOR_AGENTE . " WHERE matricula = :matricula AND ramal = :ramal;"; + $this->query = "DELETE FROM " . self::SUPERVISOR_AGENTE . " WHERE matricula = :matricula"; $data['matricula'] = $matricula; - $data['ramal'] = $ramal; return $this->delete($this->query, $data); } @@ -198,15 +208,16 @@ class Supervisor extends Model ### EVENTOS ### ######################################## - public function addEventoLoginAgent($matricula, $idDac, $flag = 1) + public function addEventoLoginAgent($matricula, $idDac, $flag = 1, $ramal) { $data = []; - $this->query = "INSERT INTO " . self::EVENTO_AGENTE . "(matricula, login, logoff, id_dac, flag) VALUES(:matricula, :login, :logoff, :id_dac, :flag) "; + $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); } @@ -224,4 +235,4 @@ class Supervisor extends Model $data['ramal'] = $ramal; return $this->update($this->query, $data); } -} \ No newline at end of file +} From 2936c302d3ff703d1d1f4924ea979136f340ca78 Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Tue, 30 Nov 2021 14:56:02 -0400 Subject: [PATCH 018/144] add metodo listaagentesdisponivel --- app/Core/CoreMedia.php | 2 +- app/Middleware/ApiAgente.php | 28 ++++++++++++++++++++++++++++ app/Models/Supervisor.php | 15 +++++++++++++++ 3 files changed, 44 insertions(+), 1 deletion(-) diff --git a/app/Core/CoreMedia.php b/app/Core/CoreMedia.php index b67db69..5aebd39 100644 --- a/app/Core/CoreMedia.php +++ b/app/Core/CoreMedia.php @@ -198,7 +198,7 @@ class CoreMedia return null; } - $agentModel = new Agent(); + $agentModel = new Supervisor(); $agentModel->findAgentByQueue($fila); $agent = $agentModel->findAgentByQueue($fila, 'LIVRE', 'tempo_livre', 'DESC'); diff --git a/app/Middleware/ApiAgente.php b/app/Middleware/ApiAgente.php index 8217a01..4085a62 100644 --- a/app/Middleware/ApiAgente.php +++ b/app/Middleware/ApiAgente.php @@ -5,18 +5,22 @@ namespace app\Middleware; use app\Controllers\AgentController; use app\Controllers\ClassificacaoController; use app\Controllers\QueueController; +use app\Models\Agent; use app\Models\Message; +use app\Models\Supervisor; use app\Providers\Positus; class ApiAgente { public AgentController $agentController; public ClassificacaoController $classificacao; + public Supervisor $supervisor; function __construct() { $this->queue = new QueueController(); $this->classificacao = new ClassificacaoController(); $this->agentController = new AgentController(); + $this->supervisor = new Supervisor(); } function router($rota, $request) @@ -43,6 +47,9 @@ class ApiAgente case 'logoff': $this->logoff($request); break; + case 'listaAgentesDisponivel': + $this->listaAgentesDisponivel(); + break; default: echo json_encode(['status' => '404']); break; @@ -90,6 +97,27 @@ class ApiAgente return null; } + function listaAgentesDisponivel() + { + try { + + $ret = $this->supervisor->listaAgentesDisponivel(); + $agentes = []; + foreach ($ret as $key => $value) { + if ($value->countAtendimentos < 3) { + array_push($agentes, $value); + } + } + echo json_encode($agentes); + } catch (\Throwable $th) { + echo json_encode([ + 'status' => $ret, + "mensagem" => $th->getMessage() + ]); + } + return null; + } + public function listaFilas() { $queue = new QueueController(); diff --git a/app/Models/Supervisor.php b/app/Models/Supervisor.php index 2b25bde..2958f27 100644 --- a/app/Models/Supervisor.php +++ b/app/Models/Supervisor.php @@ -16,6 +16,21 @@ class Supervisor extends Model private $supervisor = 'md_supervisor'; private $atendimento = 'md_atendimento'; + public function listaAgentesDisponivel() + { + $this->query = " SELECT *, + (SELECT count(*) + FROM md_atendimento maa + INNER JOIN md_evento me + ON me.uniqueid = maa.uniqueid + WHERE me.evento = 'EMESPERA' + AND maa.matricula = ms.matricula + ) AS countAtendimentos + FROM md_supervisor ms + "; + return $this->read($this->query)->fetchAll(); + } + public function findAtendimento($cliente_id) { $this->query = "SELECT ma.* FROM {$this->supervisor} ms From 917f3b0cccceee9089f3a1e877d68ab034dd67bc Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Mon, 6 Dec 2021 15:13:56 -0400 Subject: [PATCH 019/144] add metodo finalizar, ajustes na fila e atendimento --- app/Controllers/AgentController.php | 2 +- app/Controllers/ClientController.php | 4 +- app/Controllers/QueueController.php | 4 +- app/Core/CoreMedia.php | 84 ++++++++------- app/Middleware/ApiAgente.php | 72 +++++++++---- app/Models/Agent.php | 1 - app/Models/Atendimento.php | 58 ++++++++-- app/Models/Evento.php | 2 +- app/Models/Supervisor.php | 69 +++++------- app/Providers/Positus.php | 156 +++++++-------------------- service/MonitoraAgente.php | 113 ++++--------------- websocket/Servidorsocket.php | 9 +- websocket/WsInterface.php | 19 +--- 13 files changed, 241 insertions(+), 352 deletions(-) diff --git a/app/Controllers/AgentController.php b/app/Controllers/AgentController.php index 8de0574..2e258fc 100644 --- a/app/Controllers/AgentController.php +++ b/app/Controllers/AgentController.php @@ -497,4 +497,4 @@ class AgentController extends Controller return false; } } -} +} \ No newline at end of file diff --git a/app/Controllers/ClientController.php b/app/Controllers/ClientController.php index 41ed1b1..d8d6f42 100644 --- a/app/Controllers/ClientController.php +++ b/app/Controllers/ClientController.php @@ -14,10 +14,10 @@ use app\Models\Atendimento; class ClientController extends Controller { - public function getClientQueueData($number) + public function getClientQueueData($number, $fila) { $atendimento = new Atendimento(); - $atendimentoFila = $atendimento->getAtendimentoByEvento(); + $atendimentoFila = $atendimento->getAtendimentoByEvento($fila); $clientInQueue = false; $queuePosition = 0; $clientQueue = ''; diff --git a/app/Controllers/QueueController.php b/app/Controllers/QueueController.php index b9d17b8..2f550e9 100644 --- a/app/Controllers/QueueController.php +++ b/app/Controllers/QueueController.php @@ -76,11 +76,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']; diff --git a/app/Core/CoreMedia.php b/app/Core/CoreMedia.php index 5aebd39..65c1a9d 100644 --- a/app/Core/CoreMedia.php +++ b/app/Core/CoreMedia.php @@ -7,6 +7,7 @@ use app\Models\Message; use app\Providers\Crypt; use app\Controllers\QueueController; use app\Controllers\AgentController; +use app\Controllers\BilheteController; use app\Interfaces\IApiMedia; use app\Models\Agent; use app\Models\Atendimento; @@ -35,7 +36,7 @@ class CoreMedia private Supervisor $supervisor; private Evento $eventos; private $host; - private $testeee; + private $bilheteController; public function __construct() { @@ -49,8 +50,12 @@ class CoreMedia $this->palavroes = new ListaNegraPalavras(); $this->supervisor = new Supervisor(); $this->eventos = new Evento(); + $this->bilheteController = new BilheteController; + } + public function setApi($api) + { + $this->api = $api; } - /** * Send the file to the active contact * @@ -106,7 +111,7 @@ class CoreMedia $this->ws->enviaMsg( $this->api->convertToWebsocket( $this->retornaConteudo(), - '1040', // $atendiment->matricula, + '1020', // $atendiment->matricula, time(), $this->api->getType(), $this->api->getProfile(), @@ -117,25 +122,27 @@ class CoreMedia ) ); //verifica se tem atendimento em aberto, se tiver ja manda msg para o agente via ws - $atendiment = $this->supervisor->findAtendimento($this->api->getPhone()); + $atendiment = $this->atendimento->findAtenEmAberto($this->api->getPhone()); if ($atendiment) { - $this->ws->enviaMsg($this->api->convertToWebsocket( - $this->retornaConteudo(), - $atendiment->matricula, - $atendiment->uniqueid, - $this->api->getType(), - $this->api->getProfile(), - $this->api->getPhone(), - time(), - $this->api->getId(), - $this->api->getMimetype() - )); + $this->ws->enviaMsg( + $this->api->convertToWebsocket( + $this->retornaConteudo(), + $atendiment->matricula, + $atendiment->uniqueid, + $this->api->getType(), + $this->api->getProfile(), + $this->api->getPhone(), + time(), + $this->api->getId(), + $this->api->getMimetype() // + ) + ); return null; } //verifica se tem atendimento em espera, se tiver ja mostra a sua posição na fila $atende = $this->atendimento->getAtendimentoByID($this->api->getPhone()); if ($atende) { - $this->mostraPosiscaoNaFila(); + $this->mostraPosiscaoNaFila($this->api->getPhone(), $atende->fila); return null; } //verifica se o cliente escolheu uma fila caso não escolheu manda as filas @@ -145,7 +152,7 @@ class CoreMedia return null; } // cria a atendimento - $this->criaAtendimento($filas['QUEUE']); + $this->criaAtendimento($filas['QUEUE'], $this->api->getPhone()); } /** * Set variables class @@ -182,52 +189,49 @@ class CoreMedia } return; } - public function criaAtendimento(string $fila) + public function criaAtendimento(string $fila, $numero, $uniqueid = null) { - - $uniqueid = $this->atendimento->createAtendimento(null, $this->api->getPhone(), 'E', $this->api->getchannel()); - if ($uniqueid) { - $this->eventos->createEvento($uniqueid, 'EMESPERA', date('Y-m-d H:i:s'), date('Y-m-d H:i:s'), $fila); + if (empty($uniqueid)) { + $uniqueid = $this->atendimento->createAtendimento(null, $numero, 'E', $this->api->getchannel()); + 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($this->api->getchannel(), $fila); + $agentes = $this->agente->infoAgentes($fila); if (empty($agentes)) { $this->api->enviarMsg( - $this->api->getPhone(), + $numero, utf8_encode('Não temos nenhum atendente disponível no momento, iremos lhe atender assim que um atendente estiver disponível!') ); return null; } - $agentModel = new Supervisor(); - $agentModel->findAgentByQueue($fila); $agent = $agentModel->findAgentByQueue($fila, 'LIVRE', 'tempo_livre', 'DESC'); - if (empty($agent)) { $this->api->enviarMsg( - $this->api->getPhone(), + $numero, utf8_encode('Nossos atendentes estão ocupados, por favor aguarde que iremos lhe atender!') ); - $this->mostraPosiscaoNaFila(); + $this->mostraPosiscaoNaFila($numero, $fila); return null; } - return null; - - $retServe = $this->agente->openService($agent[0]->ramal, $this->api->getPhone(), $uniqueid); + $retServe = $this->atendimento->updAtendimento($uniqueid, $agent[0]->matricula); if (empty($retServe)) { $this->api->enviarMsg( - $this->api->getPhone(), + $numero, utf8_encode('Erro ao gerar atendimento!') ); return null; } + $this->eventos->createEvento($uniqueid, 'START', date('Y-m-d H:i:s'), date('Y-m-d H:i:s'), $fila); $protocol = $this->bilheteController->generateProtocol($uniqueid); - $this->bilheteController->tempoEsperaSupervisor($uniqueid); + //$this->bilheteController->tempoEsperaSupervisor($uniqueid); $this->api->enviarMsg( - $this->api->getPhone(), + $numero, utf8_encode("Atendimento iniciado!\n") ); $this->api->enviarMsg( - $this->api->getPhone(), + $numero, utf8_encode("Número do protocolo do atendimento é $protocol\n") ); return null; @@ -244,11 +248,11 @@ class CoreMedia ); } - public function mostraPosiscaoNaFila() + public function mostraPosiscaoNaFila($numero, $fila) { - $clientfila = $this->queue->clientQueueVerify($this->api->getPhone()); + $clientfila = $this->queue->clientQueueVerify($numero, $fila); if ($clientfila['MESSAGE']) { - $this->api->enviarMsg($this->api->getPhone(), utf8_encode($clientfila['MESSAGE'])); + $this->api->enviarMsg($numero, utf8_encode($clientfila['MESSAGE'])); return null; } } @@ -317,4 +321,4 @@ class CoreMedia return null; } } -} +} \ No newline at end of file diff --git a/app/Middleware/ApiAgente.php b/app/Middleware/ApiAgente.php index 4085a62..b2c0863 100644 --- a/app/Middleware/ApiAgente.php +++ b/app/Middleware/ApiAgente.php @@ -6,6 +6,8 @@ use app\Controllers\AgentController; use app\Controllers\ClassificacaoController; use app\Controllers\QueueController; use app\Models\Agent; +use app\Models\Atendimento; +use app\Models\Evento; use app\Models\Message; use app\Models\Supervisor; use app\Providers\Positus; @@ -15,12 +17,16 @@ class ApiAgente public AgentController $agentController; public ClassificacaoController $classificacao; public Supervisor $supervisor; + public Atendimento $atendimento; + public Evento $eventos; function __construct() { $this->queue = new QueueController(); $this->classificacao = new ClassificacaoController(); $this->agentController = new AgentController(); $this->supervisor = new Supervisor(); + $this->atendimento = new Atendimento(); + $this->eventos = new Evento(); } function router($rota, $request) @@ -50,6 +56,9 @@ class ApiAgente case 'listaAgentesDisponivel': $this->listaAgentesDisponivel(); break; + case 'finalizaAtendimento': + $this->finalizaAtendimento($request); + break; default: echo json_encode(['status' => '404']); break; @@ -65,14 +74,11 @@ class ApiAgente ); echo json_encode([ - 'status' => $ret, - "mensagem" => $ret ? 'sucesso' : 'falha' + 'status' => $ret ? 'sucesso' : 'falha', + "mensagem" => utf8_encode($ret) ]); } catch (\Throwable $th) { - echo json_encode([ - 'status' => $ret, - "mensagem" => $th->getMessage() - ]); + $this->retorno($ret, $th->getMessage()); } return null; } @@ -83,16 +89,12 @@ class ApiAgente $ret = $this->agentController->logoff( $request['matricula'] ); - echo json_encode([ - 'status' => $ret, - "mensagem" => $ret ? 'sucesso' : 'falha' + 'status' => $ret ? 'sucesso' : 'falha', + "mensagem" => utf8_encode($ret) ]); } catch (\Throwable $th) { - echo json_encode([ - 'status' => $ret, - "mensagem" => $th->getMessage() - ]); + $this->retorno($ret, $th->getMessage()); } return null; } @@ -110,10 +112,24 @@ class ApiAgente } echo json_encode($agentes); } catch (\Throwable $th) { - echo json_encode([ - 'status' => $ret, - "mensagem" => $th->getMessage() - ]); + $this->retorno($ret, $th->getMessage()); + } + return null; + } + + function finalizaAtendimento($request) + { + try { + $ret = $this->eventos->createEvento( + $request['uniqueid'], + CONF_EVENT_TIMERMINO_AGENTE, + date('Y-m-d H:i:s'), + date('Y-m-d H:i:s'), + $request['fila'] + ); + $this->retorno($ret, $ret); + } catch (\Throwable $th) { + $this->retorno($ret, $th->getMessage()); } return null; } @@ -193,13 +209,15 @@ class ApiAgente file_put_contents($anmeArquivo, $texto); $retuno = $provider->enviarMedia( $mensagem['dst'], - 'http://voip.simplesip.com.br:8090/link/' . $mensagem['id_provedor'] . '/' . $mensagem['mimetype'], - $mensagem['id_provedor'], - $mensagem['type'] + 'http://voip.simplesip.com.br:8090/link/' . + $mensagem['id_provedor'] . '/' . + base64_encode($mensagem['mimetype']), + $mensagem['type'], + $mensagem['file_name'] ); } $modelMensagem->addMessage( - $mensagem['idAtendimento'], + $mensagem['id_atendimento'], $contact['matricula'], $mensagem['dst'], $mensagem['type'], @@ -211,7 +229,7 @@ class ApiAgente echo json_encode(['status' => $retuno]); return null; } catch (\Throwable $th) { - echo json_encode(['status' => false, 'message' => '' . $th->getMessage()]); + $this->retorno($retuno, $th->getMessage()); return null; } } @@ -230,4 +248,12 @@ class ApiAgente } return null; } -} + + function retorno($status, $mensagem) + { + echo json_encode([ + 'status' => $status, + "mensagem" => utf8_encode($mensagem) + ]); + } +} \ No newline at end of file diff --git a/app/Models/Agent.php b/app/Models/Agent.php index 493e021..d441a3d 100644 --- a/app/Models/Agent.php +++ b/app/Models/Agent.php @@ -46,7 +46,6 @@ class Agent extends Model } } catch (\Throwable $th) { Connect::setInstance(null); - logger('testeOff')->info(gettype(Connect::getInstance()), debug_backtrace()); return []; } } diff --git a/app/Models/Atendimento.php b/app/Models/Atendimento.php index 1fd1e8b..c2ce8de 100644 --- a/app/Models/Atendimento.php +++ b/app/Models/Atendimento.php @@ -12,22 +12,60 @@ class Atendimento extends Model public function getAtendimentoByID($cliente_id, $evento = 'EMESPERA') { - $this->query = "SELECT ma.* FROM {$this->atendimento} ma + $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 + ON ma.uniqueid = me.uniqueid + WHERE ma.cliente_id = :cliente_id + AND (SELECT count(*) + FROM md_evento m2 + WHERE ma.uniqueid = m2.uniqueid + AND m2.evento IN ('COMPLETE_AGENT','ABANDON', 'TIMEOUT_CALLER','TIMEOUT_AGENT') + ) = 0 AND me.evento = :evento"; return $this->read($this->query, ['cliente_id' => $cliente_id, 'evento' => $evento])->fetch(); } - public function getAtendimentoByEvento($evento = 'EMESPERA') + 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 me.evento = :evento"; - return $this->read($this->query, ['evento' => $evento])->fetchAll(); + WHERE me.evento = :evento + AND (SELECT count(*) + FROM md_evento m2 + WHERE ma.uniqueid = m2.uniqueid + AND m2.evento IN ('COMPLETE_AGENT','ABANDON', 'TIMEOUT_CALLER','TIMEOUT_AGENT') + ) = 0 + AND me.fila = :fila"; + return $this->read($this->query, ['evento' => $evento, 'fila' => $fila])->fetchAll(); + } + + public function findAtenEmAberto($cliente_id) + { + $this->query = "SELECT ma.* FROM {$this->atendimento} ma + LEFT JOIN {$this->evento} me + ON ma.uniqueid = me.uniqueid + WHERE ma.cliente_id = :cliente_id + AND (SELECT count(*) + FROM md_evento m2 + WHERE ma.uniqueid = m2.uniqueid + AND m2.evento IN ('COMPLETE_AGENT','ABANDON', 'TIMEOUT_CALLER','TIMEOUT_AGENT') + ) = 0 + AND ma.matricula notnull "; + return $this->read($this->query, ['cliente_id' => $cliente_id])->fetch(); } + + public function getAtendFila($fila, $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 me.evento = :evento + AND me.fila = :fila + AND ma.matricula IS NULL"; + return $this->read($this->query, ['evento' => $evento, 'fila' => $fila])->fetchAll(); + } + public function createAtendimento($matricula, $cliente_id, $direcao, $context) { $unique = uniqid('', true); @@ -57,4 +95,12 @@ class Atendimento extends Model 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); + } } \ No newline at end of file diff --git a/app/Models/Evento.php b/app/Models/Evento.php index fc34023..d89e3d8 100644 --- a/app/Models/Evento.php +++ b/app/Models/Evento.php @@ -9,7 +9,7 @@ class Evento extends Model { private $evento = 'md_evento'; - public function createEvento($uniqueid, $evento, $data_evento, $data_reg, string $fila) + public function createEvento($uniqueid, $evento, $data_evento, $data_reg, $fila = null) { $this->query = "INSERT INTO {$this->evento} (uniqueid, evento, diff --git a/app/Models/Supervisor.php b/app/Models/Supervisor.php index 2958f27..cb191b8 100644 --- a/app/Models/Supervisor.php +++ b/app/Models/Supervisor.php @@ -31,15 +31,6 @@ class Supervisor extends Model return $this->read($this->query)->fetchAll(); } - public function findAtendimento($cliente_id) - { - $this->query = "SELECT ma.* FROM {$this->supervisor} ms - INNER JOIN {$this->atendimento} ma - ON ma.uniqueid = ms.uniqueid - WHERE ms.cliente_id = :cliente_id"; - return $this->read($this->query, ['cliente_id' => $cliente_id])->fetch(); - } - public function findByNumber($matricula) { $this->query = "SELECT * " @@ -47,22 +38,16 @@ class Supervisor extends Model return $this->read($this->query, ['matricula' => $matricula])->fetch(); } - public function findAllAgentes($media = ['TELEGRAM', 'whatsapp'], $queue = null) + public function findAllAgentes($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 "; + $this->query .= " AND fila = :queue "; $data['queue'] = $queue; } - $ret = $this->read($this->query, $data); - if ($ret) { - return $ret->fetchAll(); - } else { - return []; - } + return $this->read($this->query, $data)->fetchAll(); } catch (\Throwable $th) { Connect::setInstance(null); logger('testeOff')->info(gettype(Connect::getInstance()), debug_backtrace()); @@ -88,33 +73,21 @@ class Supervisor extends Model return $this->read($this->query, ['matricula' => $matricula])->fetch(); } - public function findAgentByQueue($queue, $status = 'LIVRE', $orderBy = null, $orderByType = 'ASC') + public function findAgentByQueue($queue) { $data = []; - $this->query = "SELECT ramal, b.matricula, b.nome, b.apelido, penalidade, (logado - duracao) as tempo_livre, - (SELECT count(*) AS atendimentos - FROM " . self::BILHETE . " ab - INNER JOIN " . self::EVENTOS_DACS . " bc ON ab.uniqueid = bc.uid2 - WHERE bc.fila = a.dac - AND bc.agente = a.matricula - AND bc.evento IN(:evento) - AND ab.data_bilhete = 'now') - FROM " . self::SUPERVISOR_AGENTE . " a - INNER JOIN " . self::USUARIOS . " b ON a.matricula = b.matricula - WHERE upper(a.dac) = upper(:queue) - AND a.status = upper(:status) "; + $this->query = "SELECT * + FROM md_supervisor ms + WHERE (SELECT count(*) + FROM md_atendimento maa + INNER JOIN md_evento me + ON me.uniqueid = maa.uniqueid + WHERE me.evento = 'EMESPERA' + AND maa.matricula = ms.matricula + ) < 3 + AND ms.fila = :queue"; $data['queue'] = $queue; - $data['status'] = $status; - $data['evento'] = implode(',', [ - CONF_EVENT_TIMERMINO_CLIENTE, - CONF_EVENT_TIMERMINO_AGENTE - ]); - - if ($orderBy && $orderByType) { - $this->query .= " ORDER BY $orderBy $orderByType;"; - } - return $this->read($this->query, $data)->fetchAll(); } @@ -152,8 +125,16 @@ class Supervisor extends Model ]); } - public function updateAgent($matricula, $ramal, $status = 'LIVRE', $origemDestino = null, $motivoPausa = null, $duracao = null, $uniqueid = null, $chamada_classificado = 1) - { + public function updateAgent( + $matricula, + $ramal, + $status = 'LIVRE', + $origemDestino = null, + $motivoPausa = null, + $duracao = null, + $uniqueid = null, + $chamada_classificado = 1 + ) { $data = []; $this->query = "UPDATE " . self::SUPERVISOR_AGENTE . " SET logado = :logado, origem_destino = :origem_destino, status = :status, motivo_pausa = :motivo_pausa, uniqueid = :uniqueid, chamada_classificado = :chamada_classificado"; if ($duracao) { @@ -250,4 +231,4 @@ class Supervisor extends Model $data['ramal'] = $ramal; return $this->update($this->query, $data); } -} +} \ No newline at end of file diff --git a/app/Providers/Positus.php b/app/Providers/Positus.php index 7d3c7e8..b6e7390 100644 --- a/app/Providers/Positus.php +++ b/app/Providers/Positus.php @@ -7,14 +7,9 @@ use app\Providers\RequestURL; class Positus implements IApiMedia { - private $token; private $url; private $metodo; - - ######################################################################## - ## VARIAVEIS DA CLASSE ## - ######################################################################## private $query; private $requestType; private $request; @@ -24,9 +19,13 @@ class Positus implements IApiMedia public $channel = CONF_WHATSAPP_CHANNEL; public $timeout_client_resposta = CONF_WHATSAPP_TIMEOUT_CLIENT_RESPOSTA; - ######################################################################## - ## RECURSOS DA API ## - ######################################################################## + function __construct() + { + $this->token = CONF_WHATSAPP_AUTH_TOKEN; + $this->url = CONF_WHATSAPP_AUTH_URL; + $this->request = new RequestURL(); + } + public function getchannel() { return CONF_WHATSAPP_CHANNEL; @@ -59,23 +58,38 @@ class Positus implements IApiMedia } return null; } - - function enviarMedia($whatsapp, $link, $titulo = null, $type) + function enviaImagem($whatsapp, $link, $titulo = null) { - $this->debug = debug_backtrace(); - if ($this->getArgs(func_get_args())) { - $this->params = array( - "to" => "+$whatsapp", - "type" => "$type", - "$type" => array( - "link" => "$link" - ) - ); - $this->requestType("POST"); - $this->setMetodo('messages'); - return $this->exec(); + } + function enviaSticker($whatsapp, $link) + { + } + function enviaVideo($whatsapp, $link, $titulo = null) + { + } + function enviaAudio($whatsapp, $link) + { + } + function enviaDocumento($whatsapp, $link, $titulo = null) + { + } + function enviarMedia($whatsapp, $link, $type, $titulo = null) + { + $tipos = []; + $tipos['link'] = $link; + if (!empty($titulo)) { + $tipos['caption'] = $titulo; } - return false; + $this->debug = debug_backtrace(); + $this->params = [ + "to" => "+$whatsapp", + "type" => "$type", + "$type" => $tipos + ]; + $this->requestType("POST"); + $this->setMetodo('messages'); + return $this->exec(); + //} } function enviarMsg($whatsapp, $mensagem) @@ -112,7 +126,6 @@ class Positus implements IApiMedia ) ) ); - logger('positus')->info(print_r($this->params, true)); $this->requestType("POST"); $this->setMetodo('messages'); $ret = $this->exec(); @@ -161,7 +174,6 @@ class Positus implements IApiMedia ) ) ); - logger('positus')->info(print_r($this->params, true)); $this->requestType("POST"); $this->setMetodo('messages'); $ret = $this->exec(); @@ -223,86 +235,6 @@ 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) - { - $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(); - } - return false; - } - function baixarMidia() { if (in_array($this->getType(), ['location', 'contacts', 'text'])) { @@ -414,7 +346,6 @@ class Positus implements IApiMedia $message = $this->hook['messages'][0]['text']['body']; return ($message ? $message : false); } - /** * Returns the name of the contact * @return string|boolean @@ -459,17 +390,6 @@ class Positus implements IApiMedia $this->hook = $hook; } - ######################################################################## - ## FUNCOES DO SISTEMA ## - ######################################################################## - - function __construct() - { - $this->token = CONF_WHATSAPP_AUTH_TOKEN; - $this->url = CONF_WHATSAPP_AUTH_URL; - $this->request = new RequestURL(); - } - function setMetodo($metodo) { $this->metodo = $metodo; @@ -581,4 +501,4 @@ class Positus implements IApiMedia $titulo = substr($titulo, 0, strrpos($titulo, ".")); return $titulo; } -} +} \ No newline at end of file diff --git a/service/MonitoraAgente.php b/service/MonitoraAgente.php index 6bdbfbb..af33e9d 100644 --- a/service/MonitoraAgente.php +++ b/service/MonitoraAgente.php @@ -14,39 +14,30 @@ use app\Core\Connect; use app\Core\CoreMedia; use app\Providers\Positus; use app\Core\Media; +use app\Models\Agent; +use app\Models\Atendimento; use app\Models\Message; use app\Models\NotificaMedia; +use app\Models\Supervisor; use app\Providers\ApiTelegram; use Exception; use Ratchet\ConnectionInterface; class MonitoraAgente { - public $agente; - public $bilhete; - public MessageController $message; - public $media; - public $media2; - public $notificaMedia; - public $mensages; - public $classificacao; - public $supervisor; + public Supervisor $supervisor; + public Atendimento $atendimento; public $timeout_agent_alert; public $timeout_agent_desconexao; public $timeout_agent_classificacao; public $mensagem; + public $coremedia; function __construct() { - $this->agente = new AgentController(); - $this->bilhete = new BilheteController(); - $this->message = new MessageController(); - $this->media = new CoreMedia(); - $this->media2 = new Media(); - $this->notificaMedia = new NotificaMedia(); - $this->mensages = new Message(); - $this->classificacao = new ClassificacaoController(); - $this->supervisor = new SupervisorQueueController(); + $this->supervisor = new Supervisor(); + $this->atendimento = new Atendimento(); + $this->coremedia = new CoreMedia(); $this->timeout_agent_alert = (CONF_WHATSAPP_TIMEOUT_AGENT_AVISO / 60); $this->timeout_agent_desconexao = (CONF_WHATSAPP_TIMEOUT_AGENT_DESCONEXAO / 60); $this->timeout_agent_classificacao = (CONF_WHATSAPP_TIMEOUT_AGENT_CLASSIFICACAO / 60); @@ -62,84 +53,22 @@ class MonitoraAgente } - function monitora(ConnectionInterface $conn) + function monitora() { try { - $agentesLista = $this->agente->inactiveAgents(); + $this->coremedia->setApi(new Positus()); + $agentesLista = $this->supervisor->listaAgentesDisponivel(); foreach ($agentesLista as $item) { - $positus = $this->retornaApiMedia($item['SALA1']); - //verifica os timeout do agente - $this->timeoutAgente($item, $conn); + logger('bug')->info(var_export($item, true), debug_backtrace()); + $atendimentos = $this->atendimento->getAtendFila($item->fila); - if ($item['STATUS'] == CONF_AGENT_STATUS_LIVRE) { - $chamadaSemClassificacao = $this->classificacao->agentClassificacaoPending($item['MATRICULA'], $item['FILA']); - if ($chamadaSemClassificacao) { //BLOQUEAR ATENDIMENTO SEM CLASSIFICACAO - if ($item['FREETIME'] % $this->timeout_agent_classificacao == 0) { - $classificacaoList = $this->classificacao->agentClassificacaoList($item['FILA']); - - // $retorno = $this->notificaMedia->verificaNotifica( - // $chamadaSemClassificacao, - // $item['RAMAL'], - // utf8_encode(CONF_NAME_REPONSE . " : " . $classificacaoList) - // ); - // if ($retorno->quant == 0) { - // $this->notificaMedia->addNotifica( - // $chamadaSemClassificacao, - // $item['RAMAL'], - // utf8_encode(CONF_NAME_REPONSE . " : " . $classificacaoList) - // ); - $conn->send($this->enviaActions(CONF_NAME_REPONSE . " : " . $classificacaoList, 'classificacao')); - // $positus->enviarMsg( - // $item['RAMAL'], - // utf8_encode(CONF_NAME_REPONSE . " : " . $classificacaoList) - // ); - // } - } - continue; - } - - $atendimento = $this->bilhete->bilheteForaHorario(CONF_EVENT_ESPERA, $item['FILA'], null, $item['SALA1']); - if ($atendimento) { - /** - * VALIDA O ATENDIMENTO - */ - $response = $this->media->validaAtendimento($positus, $atendimento[0]->fila, null, $atendimento[0]->src, $item['SALA1']); - $this->bilhete->atenderBilheteForaHorario($atendimento[0]->uniqueid, $atendimento[0]->src, $atendimento[0]->fila, $item['MATRICULA'], $response['DATA']['uniqueid']); - $atendimento = $this->bilhete->bilheteForaHorario(CONF_EVENT_ESPERA, $item['FILA'], null, $item['SALA1']); - foreach ($atendimento as $cliente) { - $mensagemFila = 'Você está na posição ' . $this->media2->getClientQueueData($cliente->src, $atendimento)['QUEUE_POSITION'] . '. Aguarde ser atendido!'; - $positus->enviarMsg($cliente->src, utf8_encode($mensagemFila)); - } - } - } - - if ($item['STATUS'] == CONF_AGENT_STATUS_OCUPADO) { - if ($this->message->timeoutTalk($item['UNIQUEID'], $item['RAMAL']) == 'FINISH' && $this->agente->closeService($item['ORIGEM_DESTINO'], CONF_EVENT_TIMEOUT_CLIENTE, $item['SALA1'])) { - $positus->enviarMsg($item['ORIGEM_DESTINO'], utf8_encode($this->mensagem['TIMEOUT_CLIENT_INATIVIDADE'])); - $positus->enviarMsg($item['ORIGEM_DESTINO'], utf8_encode($this->mensagem['TALK_FINISHED'])); - $positus->enviarMsg($item['RAMAL'], utf8_encode($this->mensagem['TALK_FINISHED'])); - } - if ($this->message->timeoutTalk($item['UNIQUEID'], $item['RAMAL']) == 'ALERT') { - $msg = $this->mensages->findLastMessage($item['UNIQUEID']); - $retorno = $this->notificaMedia->verificaNotifica( - $item['UNIQUEID'], - $item['ORIGEM_DESTINO'], - utf8_encode($this->mensagem['TALK_ALERT_FINISH']) . $msg->id - ); - if ($retorno->quant == 0) { - $this->notificaMedia->addNotifica( - $item['UNIQUEID'], - $item['ORIGEM_DESTINO'], - utf8_encode($this->mensagem['TALK_ALERT_FINISH']) . $msg->id - ); - $positus->enviarMsg( - $item['ORIGEM_DESTINO'], - utf8_encode($this->mensagem['TALK_ALERT_FINISH']) - ); - } - } + if (count($atendimentos) > 0) { + $this->coremedia->criaAtendimento( + $atendimentos[0]->fila, + $atendimentos[0]->cliente_id, + $atendimentos[0]->uniqueid + ); } - $this->supervisor->calcTimeAwait(); } } catch (Exception $ex) { Connect::setInstance(null); @@ -193,4 +122,4 @@ class MonitoraAgente logger('monitora')->info($th->getMessage(), debug_backtrace()); } } -} +} \ No newline at end of file diff --git a/websocket/Servidorsocket.php b/websocket/Servidorsocket.php index c7d6914..1ae1057 100644 --- a/websocket/Servidorsocket.php +++ b/websocket/Servidorsocket.php @@ -50,14 +50,9 @@ class Servidorsocket implements MessageComponentInterface $this->provider->enviarMsg($mensagem->para, $mensagem->msg); } else { try { + $monitora = new MonitoraAgente(); + $monitora->monitora(); foreach ($this->clientes as $key => $value) { - if ($mensagem->event->type == 'mensagem') { - # code... - } - if ($value['ramal'] == 8081) { - $monitora = new MonitoraAgente(); - $monitora->monitora($value['conection']); - } $value['conection']->send($msg); } } catch (\Throwable $th) { diff --git a/websocket/WsInterface.php b/websocket/WsInterface.php index 23337da..331d6ea 100644 --- a/websocket/WsInterface.php +++ b/websocket/WsInterface.php @@ -8,24 +8,13 @@ class WsInterface { private $client; - function __construct() - { - $this->client = new Client("ws://192.168.115.65:8090/ws"); - } - function enviaMsg($msg) { if ($msg) { - if ($this->client->isConnected()) { - $this->client->text($msg); - $this->client->close(); - return null; - } else { - $this->client = new Client("ws://192.168.115.65:8090/ws"); - $this->client->text($msg); - $this->client->close(); - return null; - } + $this->client = new Client("ws://192.168.115.65:8090/ws"); + $this->client->text($msg); + $this->client->close(); + return null; } } } \ No newline at end of file From d836135b99408b46829963288229ae35ed9e0746 Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Mon, 6 Dec 2021 15:59:38 -0400 Subject: [PATCH 020/144] add titulo do documento --- app/Interfaces/IApiMedia.php | 2 +- app/Providers/Positus.php | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/app/Interfaces/IApiMedia.php b/app/Interfaces/IApiMedia.php index b9a0d76..1696cc6 100644 --- a/app/Interfaces/IApiMedia.php +++ b/app/Interfaces/IApiMedia.php @@ -36,6 +36,6 @@ interface IApiMedia function exec(); function response($result); public function getLinkDownload($host); - public function retornaTituloDocument($msg); + public function retornaTituloDocument(); public function convertToWebsocket($content, $matricula = '', $idAtendimento, $type, $name, $number, $data, $idProvedor, $mimetype); } \ No newline at end of file diff --git a/app/Providers/Positus.php b/app/Providers/Positus.php index b6e7390..5ce8bf0 100644 --- a/app/Providers/Positus.php +++ b/app/Providers/Positus.php @@ -48,6 +48,7 @@ class Positus implements IApiMedia "dst" => $matricula, "uniqueid" => $uniqueid, "media" => $this->channel, + "file_name" => $this->retornaTituloDocument(), "datetime" => $data, "status" => "sent", 'mimetype' => $mimetype @@ -495,10 +496,11 @@ class Positus implements IApiMedia return $host . "whatsapp/download/{$this->getId()}/{$this->getMimetype()}"; } - public function retornaTituloDocument($msg) + public function retornaTituloDocument() { - $titulo = $msg['REQUEST']['messages'][0]['document']['filename']; - $titulo = substr($titulo, 0, strrpos($titulo, ".")); - return $titulo; + if ($this->hook['messages'][0]['document']) { + return $this->hook['messages'][0]['document']['filename']; + } + return null; } } \ No newline at end of file From e75b2db5cf8fb148541efc8b781d4274e8d2b02a Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Mon, 6 Dec 2021 17:25:53 -0400 Subject: [PATCH 021/144] add metodo de listarmensagem e listar atendimento --- app/Core/CoreMedia.php | 19 +++++++++--- app/Middleware/ApiAgente.php | 59 ++++++++++++++++++++++++++++++++++++ app/Models/Atendimento.php | 8 +++++ app/Models/Message.php | 4 +-- 4 files changed, 84 insertions(+), 6 deletions(-) diff --git a/app/Core/CoreMedia.php b/app/Core/CoreMedia.php index 65c1a9d..b6bff8c 100644 --- a/app/Core/CoreMedia.php +++ b/app/Core/CoreMedia.php @@ -9,7 +9,6 @@ use app\Controllers\QueueController; use app\Controllers\AgentController; use app\Controllers\BilheteController; use app\Interfaces\IApiMedia; -use app\Models\Agent; use app\Models\Atendimento; use app\Models\Evento; use app\Models\ListaNegraPalavras; @@ -37,6 +36,7 @@ class CoreMedia private Evento $eventos; private $host; private $bilheteController; + private $message; public function __construct() { @@ -68,6 +68,7 @@ class CoreMedia $this->api = $api; $this->api->setHook($this->request); + if (!$this->api->baixarMidia()) { $this->api->enviarMsg($this->api->getPhone(), utf8_encode("Não foi possivel entregar o aquivo para o agente. Envie novamente!")); } @@ -124,6 +125,16 @@ class CoreMedia //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->message->addMessage( + $atendiment->uniqueid, + $this->api->getPhone(), + $atendiment->matricula, + $this->api->getType(), + $this->retornaConteudo(), + $this->api->getProfile(), + $this->api->getchannel(), + "sended" + ); $this->ws->enviaMsg( $this->api->convertToWebsocket( $this->retornaConteudo(), @@ -205,8 +216,8 @@ class CoreMedia ); return null; } - $agentModel = new Supervisor(); - $agent = $agentModel->findAgentByQueue($fila, 'LIVRE', 'tempo_livre', 'DESC'); + + $agent = $this->supervisor->findAgentByQueue($fila, 'LIVRE', 'tempo_livre', 'DESC'); if (empty($agent)) { $this->api->enviarMsg( $numero, @@ -223,9 +234,9 @@ class CoreMedia ); return null; } + //cria o evento de inicio de atendimento e criar o protocolo $this->eventos->createEvento($uniqueid, 'START', date('Y-m-d H:i:s'), date('Y-m-d H:i:s'), $fila); $protocol = $this->bilheteController->generateProtocol($uniqueid); - //$this->bilheteController->tempoEsperaSupervisor($uniqueid); $this->api->enviarMsg( $numero, utf8_encode("Atendimento iniciado!\n") diff --git a/app/Middleware/ApiAgente.php b/app/Middleware/ApiAgente.php index b2c0863..907f267 100644 --- a/app/Middleware/ApiAgente.php +++ b/app/Middleware/ApiAgente.php @@ -59,6 +59,12 @@ class ApiAgente case 'finalizaAtendimento': $this->finalizaAtendimento($request); break; + case 'listaAtendimentoAgent': + $this->listaAtendimentoAgent($request); + break; + case 'listaMensagem': + $this->listaMensagem($request); + break; default: echo json_encode(['status' => '404']); break; @@ -256,4 +262,57 @@ class ApiAgente "mensagem" => utf8_encode($mensagem) ]); } + + function listaAtendimentoAgent($request) + { + if ($request['matricula']) { + $retunr = $this->atendimento->findAtendAgent($request['matricula']); + if ($retunr) { + echo json_encode($retunr); + } else { + echo $this->retorno($retunr, $retunr); + } + } else { + echo $this->retorno(false, "O parametro matricula é obrigatorio"); + } + } + + function listaMensagem($request) + { + if ($request['uniqueid']) { + $messageModel = new Message(); + $retunr = $messageModel->findMessageByUniqueid($request['uniqueid']); + if ($retunr) { + $mensagenss = []; + foreach ($retunr as $key => $value) { + $mensagem = []; + $mensagem["event"] = [ + "type" => "mensagem", + "contact" => [ + "name" => $value->profile_name, + "number" => $value->src, + ], + "mensagem" => [ + "type" => $value->type, + "content" => $value->content, + "id_provedor" => $value->idProvedor, + "dst" => $value->dst, + "uniqueid" => $value->uniqueid, + "media" => $value->media, + "file_name" => $value->file_name, + "datetime" => $value->msg_date, + "status" => $value->status, + 'mimetype' => $value->mimetype + ] + ]; + array_push($mensagenss, $mensagem); + } + echo json_encode($mensagenss); + } else { + echo $this->retorno($retunr, $retunr); + } + } else { + echo $this->retorno(false, "O parametro uniqueid é obrigatorio"); + } + } } \ No newline at end of file diff --git a/app/Models/Atendimento.php b/app/Models/Atendimento.php index c2ce8de..652b3b3 100644 --- a/app/Models/Atendimento.php +++ b/app/Models/Atendimento.php @@ -40,6 +40,14 @@ class Atendimento extends Model return $this->read($this->query, ['evento' => $evento, 'fila' => $fila])->fetchAll(); } + public function findAtendAgent($matricula) + { + $this->query = "SELECT ma.* FROM {$this->atendimento} ma + WHERE ma.matricula = :matricula + LIMIT 10"; + return $this->read($this->query, ['matricula' => $matricula])->fetchAll(); + } + public function findAtenEmAberto($cliente_id) { $this->query = "SELECT ma.* FROM {$this->atendimento} ma diff --git a/app/Models/Message.php b/app/Models/Message.php index 8a15c6b..414b60c 100644 --- a/app/Models/Message.php +++ b/app/Models/Message.php @@ -32,8 +32,8 @@ class Message extends Model 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"; + return $this->read($this->query, ['uniqueid' => $uniqueid])->fetchAll(); } public function findMessageByNumber($number) From c966881312e947fbdfdb440427873577e462e0e0 Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Tue, 7 Dec 2021 15:52:32 -0400 Subject: [PATCH 022/144] add metodo listapausas, entrarPausa e sairPausa --- app/Controllers/AgentController.php | 21 ++--- app/Core/CoreMedia.php | 13 --- app/Middleware/ApiAgente.php | 128 ++++++++++++++++++++-------- app/Models/Parametros.php | 31 ++++--- app/Models/Pause.php | 6 +- app/Models/Queue.php | 2 +- app/Models/Supervisor.php | 15 ++++ service/MonitoraAgente.php | 1 - 8 files changed, 139 insertions(+), 78 deletions(-) diff --git a/app/Controllers/AgentController.php b/app/Controllers/AgentController.php index 2e258fc..df61d0f 100644 --- a/app/Controllers/AgentController.php +++ b/app/Controllers/AgentController.php @@ -199,13 +199,13 @@ class AgentController extends Controller 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) { @@ -224,11 +224,11 @@ class AgentController extends Controller 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!'); } @@ -238,16 +238,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!'); } @@ -259,8 +260,7 @@ class AgentController extends Controller if ($agent->status != CONF_AGENT_STATUS_PAUSA) { throw new Exception('Agente não está em pausa!'); } - - $this->agent->updateAgent($agent->matricula, $ramal, CONF_AGENT_STATUS_LIVRE, null, null, 1); + $this->agent->updateAgent($agent->matricula, CONF_AGENT_STATUS_LIVRE); $this->pause->updateEventoOutPause($agent->matricula, $queue->id); $this->agent->commit(); return true; @@ -268,6 +268,7 @@ class AgentController extends Controller $this->agent->rollback(); $this->message($ex->getMessage()); logger()->error($ex->getMessage()); + return $ex->getMessage(); } return false; } diff --git a/app/Core/CoreMedia.php b/app/Core/CoreMedia.php index b6bff8c..b70a366 100644 --- a/app/Core/CoreMedia.php +++ b/app/Core/CoreMedia.php @@ -109,19 +109,6 @@ class CoreMedia } return null; } - $this->ws->enviaMsg( - $this->api->convertToWebsocket( - $this->retornaConteudo(), - '1020', // $atendiment->matricula, - time(), - $this->api->getType(), - $this->api->getProfile(), - $this->api->getPhone(), - time(), - $this->api->getId(), - $this->api->getMimetype() //$atendiment->uniqueid - ) - ); //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) { diff --git a/app/Middleware/ApiAgente.php b/app/Middleware/ApiAgente.php index 907f267..0e3ac8b 100644 --- a/app/Middleware/ApiAgente.php +++ b/app/Middleware/ApiAgente.php @@ -9,6 +9,8 @@ use app\Models\Agent; use app\Models\Atendimento; use app\Models\Evento; use app\Models\Message; +use app\Models\Parametros; +use app\Models\Pause; use app\Models\Supervisor; use app\Providers\Positus; @@ -19,6 +21,8 @@ class ApiAgente public Supervisor $supervisor; public Atendimento $atendimento; public Evento $eventos; + public Parametros $parametros; + public Pause $pausasModel; function __construct() { $this->queue = new QueueController(); @@ -27,6 +31,8 @@ class ApiAgente $this->supervisor = new Supervisor(); $this->atendimento = new Atendimento(); $this->eventos = new Evento(); + $this->pausasModel = new Pause(); + $this->parametros = new Parametros(); } function router($rota, $request) @@ -65,6 +71,15 @@ class ApiAgente case 'listaMensagem': $this->listaMensagem($request); break; + case 'listaPausasAgente': + $this->listaPausasAgente($request); + break; + case 'saiPausa': + $this->saiPausa($request); + break; + case 'entrarPausa': + $this->entrarPausa($request); + break; default: echo json_encode(['status' => '404']); break; @@ -144,13 +159,7 @@ class ApiAgente { $queue = new QueueController(); $search = $queue->listaAllFilas(); - $lista = []; - foreach ($search as $key => $value) { - array_push($lista, [ - 'title' => strtolower($value->nome), - ]); - } - echo json_encode($lista); + echo json_encode($search); return null; } @@ -272,47 +281,94 @@ class ApiAgente } else { echo $this->retorno($retunr, $retunr); } + return null; } else { echo $this->retorno(false, "O parametro matricula é obrigatorio"); + return null; } + return null; } function listaMensagem($request) { - if ($request['uniqueid']) { - $messageModel = new Message(); - $retunr = $messageModel->findMessageByUniqueid($request['uniqueid']); - if ($retunr) { - $mensagenss = []; - foreach ($retunr as $key => $value) { - $mensagem = []; - $mensagem["event"] = [ - "type" => "mensagem", - "contact" => [ - "name" => $value->profile_name, - "number" => $value->src, - ], - "mensagem" => [ - "type" => $value->type, - "content" => $value->content, - "id_provedor" => $value->idProvedor, - "dst" => $value->dst, - "uniqueid" => $value->uniqueid, - "media" => $value->media, - "file_name" => $value->file_name, - "datetime" => $value->msg_date, - "status" => $value->status, - 'mimetype' => $value->mimetype - ] - ]; - array_push($mensagenss, $mensagem); + try { + if ($request['uniqueid']) { + $messageModel = new Message(); + $retunr = $messageModel->findMessageByUniqueid($request['uniqueid']); + if ($retunr) { + $mensagenss = []; + foreach ($retunr as $key => $value) { + $mensagem = []; + $mensagem["event"] = [ + "type" => "mensagem", + "contact" => [ + "name" => $value->profile_name, + "number" => $value->src, + ], + "mensagem" => [ + "type" => $value->type, + "content" => utf8_encode($value->content), + "id_provedor" => $value->idProvedor, + "dst" => $value->dst, + "uniqueid" => $value->uniqueid, + "media" => $value->media, + "file_name" => $value->file_name, + "datetime" => $value->msg_date, + "status" => $value->status, + 'mimetype' => $value->mimetype + ] + ]; + array_push($mensagenss, $mensagem); + } + echo json_encode($mensagenss); + return null; + } else { + echo $this->retorno($retunr, $retunr); + return null; } - echo json_encode($mensagenss); + } else { + echo $this->retorno(false, "O parametro uniqueid é obrigatorio"); + return null; + } + } catch (\Throwable $th) { + $this->retorno(false, $th->getMessage()); + return null; + } + } + + function listaPausasAgente($request) + { + if ($request['matricula']) { + $param = $this->parametros->findProtocolByParams(); + if ($param->prm_pausa_grupo) { + $retunr = $this->pausasModel->findPauseByGroupUser($request['matricula']); + } + if (empty($retunr)) { + $retunr = $this->pausasModel->findAllPause(); + } + + if ($retunr) { + echo json_encode($retunr); } else { echo $this->retorno($retunr, $retunr); } + return null; } else { - echo $this->retorno(false, "O parametro uniqueid é obrigatorio"); + echo $this->retorno(false, "O parametro matricula é obrigatorio"); + return null; } + return null; + } + + function entrarPausa($request) + { + $retunr = $this->agentController->enterPause($request['matricula'], $request['pausa']); + echo $this->retorno($retunr ? true : false, $retunr); + } + + function saiPausa($request) + { + $retunr = $this->agentController->exitPause($request['matricula']); + echo $this->retorno($retunr ? true : false, $retunr); } } \ No newline at end of file diff --git a/app/Models/Parametros.php b/app/Models/Parametros.php index 742d1ff..1fa7053 100644 --- a/app/Models/Parametros.php +++ b/app/Models/Parametros.php @@ -1,19 +1,22 @@ query = "SELECT prm_agente_proto, prm_use_proto_parceiro FROM " . self::TABLE . " WHERE id = :id;"; - return $this->read($this->query, ['id' => 1])->fetch(); - } - } \ No newline at end of file + const TABLE = 'pbx_parametros'; + + public function findProtocolByParams() + { + $this->query = "SELECT prm_agente_proto, prm_use_proto_parceiro, prm_pausa_grupo FROM " . self::TABLE . " WHERE id = :id;"; + return $this->read($this->query, ['id' => 1])->fetch(); + } +} \ No newline at end of file diff --git a/app/Models/Pause.php b/app/Models/Pause.php index b8d890c..5c58b45 100644 --- a/app/Models/Pause.php +++ b/app/Models/Pause.php @@ -45,7 +45,7 @@ class Pause extends Model { $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 +56,14 @@ 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 "; if ($active) { - $this->query .= " AND flag = :flag"; + $this->query .= " AND d.flag = :flag"; $data['flag'] = 1; } $data['matricula'] = $matricula; diff --git a/app/Models/Queue.php b/app/Models/Queue.php index 76a39c1..427c3e6 100644 --- a/app/Models/Queue.php +++ b/app/Models/Queue.php @@ -19,7 +19,7 @@ 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'; diff --git a/app/Models/Supervisor.php b/app/Models/Supervisor.php index cb191b8..5e89e56 100644 --- a/app/Models/Supervisor.php +++ b/app/Models/Supervisor.php @@ -126,6 +126,21 @@ class Supervisor extends Model } public function updateAgent( + $matricula, + $status = 'LIVRE', + $motivo_pausa = null + ) { + $this->query = "UPDATE {$this->supervisor} SET status = :status, motivo_pausa = :motivo_pausa"; + $this->query .= " WHERE matricula = :matricula"; + $data = []; + $data['status'] = $status; + $data['motivo_pausa'] = $motivo_pausa; + $data['matricula'] = $matricula; + + return $this->update($this->query, $data); + } + + public function updateAgent2( $matricula, $ramal, $status = 'LIVRE', diff --git a/service/MonitoraAgente.php b/service/MonitoraAgente.php index af33e9d..88f5661 100644 --- a/service/MonitoraAgente.php +++ b/service/MonitoraAgente.php @@ -61,7 +61,6 @@ class MonitoraAgente foreach ($agentesLista as $item) { logger('bug')->info(var_export($item, true), debug_backtrace()); $atendimentos = $this->atendimento->getAtendFila($item->fila); - if (count($atendimentos) > 0) { $this->coremedia->criaAtendimento( $atendimentos[0]->fila, From 67162fc38a82fe0c4e70502587841b49e038ae42 Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Tue, 7 Dec 2021 19:13:13 -0400 Subject: [PATCH 023/144] add metodo transferir atendimento --- app/Controllers/AgentController.php | 30 ++++++++++++++++++++--------- app/Core/CoreMedia.php | 2 +- app/Middleware/ApiAgente.php | 16 +++++++++++++++ app/Models/Atendimento.php | 11 ++++++++++- config/event.php | 1 + 5 files changed, 49 insertions(+), 11 deletions(-) diff --git a/app/Controllers/AgentController.php b/app/Controllers/AgentController.php index df61d0f..c44dc0d 100644 --- a/app/Controllers/AgentController.php +++ b/app/Controllers/AgentController.php @@ -10,6 +10,8 @@ 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\Supervisor; use Exception; @@ -273,30 +275,40 @@ class AgentController extends Controller 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); + $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 + ); $this->agent->commit(); - return [$ramal, $ramalTransfer]; + return true; } catch (Exception $ex) { $this->agent->rollback(); $this->message($ex->getMessage()); logger()->error($ex->getMessage()); + return $ex->getMessage(); } return false; } diff --git a/app/Core/CoreMedia.php b/app/Core/CoreMedia.php index b70a366..ac3677b 100644 --- a/app/Core/CoreMedia.php +++ b/app/Core/CoreMedia.php @@ -138,7 +138,7 @@ class CoreMedia return null; } //verifica se tem atendimento em espera, se tiver ja mostra a sua posição na fila - $atende = $this->atendimento->getAtendimentoByID($this->api->getPhone()); + $atende = $this->atendimento->getAtendimentoByCliente($this->api->getPhone()); if ($atende) { $this->mostraPosiscaoNaFila($this->api->getPhone(), $atende->fila); return null; diff --git a/app/Middleware/ApiAgente.php b/app/Middleware/ApiAgente.php index 0e3ac8b..d20a65d 100644 --- a/app/Middleware/ApiAgente.php +++ b/app/Middleware/ApiAgente.php @@ -80,6 +80,9 @@ class ApiAgente case 'entrarPausa': $this->entrarPausa($request); break; + case 'transfAtendimento': + $this->transfAtendimento($request); + break; default: echo json_encode(['status' => '404']); break; @@ -364,11 +367,24 @@ class ApiAgente { $retunr = $this->agentController->enterPause($request['matricula'], $request['pausa']); echo $this->retorno($retunr ? true : false, $retunr); + return null; } function saiPausa($request) { $retunr = $this->agentController->exitPause($request['matricula']); echo $this->retorno($retunr ? true : false, $retunr); + return null; + } + + function transfAtendimento($request) + { + $retunr = $this->agentController->transfer( + $request['matOrigem'], + $request['matDestino'], + $request['uniqueid'] + ); + echo $this->retorno($retunr ? true : false, $retunr); + return null; } } \ No newline at end of file diff --git a/app/Models/Atendimento.php b/app/Models/Atendimento.php index 652b3b3..53a6bb4 100644 --- a/app/Models/Atendimento.php +++ b/app/Models/Atendimento.php @@ -10,7 +10,7 @@ class Atendimento extends Model private $evento = 'md_evento'; private $atendimento = 'md_atendimento'; - public function getAtendimentoByID($cliente_id, $evento = 'EMESPERA') + public function getAtendimentoByCliente($cliente_id, $evento = 'EMESPERA') { $this->query = "SELECT ma.*, me.fila as fila FROM {$this->atendimento} ma INNER JOIN {$this->evento} me @@ -25,6 +25,15 @@ class Atendimento extends Model return $this->read($this->query, ['cliente_id' => $cliente_id, 'evento' => $evento])->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 diff --git a/config/event.php b/config/event.php index 34b37fa..26b1f23 100644 --- a/config/event.php +++ b/config/event.php @@ -17,4 +17,5 @@ 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_WHATSAPP_OCUPADO", 'MD_OCUPADO'); \ No newline at end of file From f6e31a556bc02a013d322fabb825c4dac17e31bb Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Mon, 13 Dec 2021 15:06:55 -0400 Subject: [PATCH 024/144] =?UTF-8?q?add=20valida=C3=A7=C3=B5es=20em=20final?= =?UTF-8?q?izar=20atendimento?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Controllers/AgentController.php | 5 +- app/Core/CoreMedia.php | 9 ++- app/Middleware/ApiAgente.php | 108 ++++++++++++++++++++-------- app/Models/Atendimento.php | 7 +- app/Models/Evento.php | 19 +++-- app/Models/Message.php | 22 ++++-- app/Models/Supervisor.php | 8 ++- 7 files changed, 128 insertions(+), 50 deletions(-) diff --git a/app/Controllers/AgentController.php b/app/Controllers/AgentController.php index c44dc0d..279251e 100644 --- a/app/Controllers/AgentController.php +++ b/app/Controllers/AgentController.php @@ -300,7 +300,8 @@ class AgentController extends Controller CONF_EVENT_TRANSFER, date('Y-m-d H:i:s'), date('Y-m-d H:i:s'), - $atendAtual->fila + $atendAtual->fila, + $agent->matricula ); $this->agent->commit(); return true; @@ -510,4 +511,4 @@ class AgentController extends Controller return false; } } -} \ No newline at end of file +} diff --git a/app/Core/CoreMedia.php b/app/Core/CoreMedia.php index ac3677b..18dba5b 100644 --- a/app/Core/CoreMedia.php +++ b/app/Core/CoreMedia.php @@ -120,7 +120,10 @@ class CoreMedia $this->retornaConteudo(), $this->api->getProfile(), $this->api->getchannel(), - "sended" + "sended", + $this->api->getMimetype(), + $this->api->retornaTituloDocument(), + $this->api->getId() ); $this->ws->enviaMsg( $this->api->convertToWebsocket( @@ -204,7 +207,7 @@ class CoreMedia return null; } - $agent = $this->supervisor->findAgentByQueue($fila, 'LIVRE', 'tempo_livre', 'DESC'); + $agent = $this->supervisor->findAgentByQueue($fila, 'LIVRE'); if (empty($agent)) { $this->api->enviarMsg( $numero, @@ -319,4 +322,4 @@ class CoreMedia return null; } } -} \ No newline at end of file +} diff --git a/app/Middleware/ApiAgente.php b/app/Middleware/ApiAgente.php index d20a65d..425c607 100644 --- a/app/Middleware/ApiAgente.php +++ b/app/Middleware/ApiAgente.php @@ -11,6 +11,7 @@ use app\Models\Evento; use app\Models\Message; use app\Models\Parametros; use app\Models\Pause; +use app\Models\Queue; use app\Models\Supervisor; use app\Providers\Positus; @@ -38,10 +39,10 @@ class ApiAgente function router($rota, $request) { switch ($rota) { - case 'login': + case 'entrar': $this->login($request); break; - case 'listaFilas': + case 'listarFilas': $this->listaFilas($request); break; case 'refreshAgent': @@ -50,39 +51,42 @@ class ApiAgente case 'classificarAtendimento': $this->classificarAtendimento($request); break; - case 'enviaMsg': + case 'enviarMensagem': $this->enviaMsg($request); break; case 'getArquivo': $this->getArquivo($request); break; - case 'logoff': + case 'sair': $this->logoff($request); break; - case 'listaAgentesDisponivel': + case 'listarAgentesDisponivel': $this->listaAgentesDisponivel(); break; - case 'finalizaAtendimento': + case 'finalizarAtendimento': $this->finalizaAtendimento($request); break; - case 'listaAtendimentoAgent': + case 'listarAtendimentoAgente': $this->listaAtendimentoAgent($request); break; - case 'listaMensagem': + case 'listarMensagem': $this->listaMensagem($request); break; - case 'listaPausasAgente': + case 'listarPausasAgente': $this->listaPausasAgente($request); break; - case 'saiPausa': + case 'sairPausa': $this->saiPausa($request); break; case 'entrarPausa': $this->entrarPausa($request); break; - case 'transfAtendimento': + case 'transferirAtendimento': $this->transfAtendimento($request); break; + case 'marcarMensagemVista': + $this->markMessegeRead($request); + break; default: echo json_encode(['status' => '404']); break; @@ -92,8 +96,10 @@ class ApiAgente function login($request) { try { + $filaModel = new Queue(); + $fila = $filaModel->findQueueById($request['fila']); $ret = $this->agentController->login( - $request['fila'], + $fila->nome, $request['matricula'] ); @@ -144,16 +150,40 @@ class ApiAgente function finalizaAtendimento($request) { try { + $agente = $this->atendimento->findAtendAgent($request['matricula']); + //verifica se existe agente + if (empty($agente)) { + $this->retorno("Agente não encontrado"); + return; + } + $atendimento = $this->atendimento->findAtendId($request['uniqueid']); + //verifica se existe atendimento + if (empty($atendimento)) { + $this->retorno("Atendimento não encontrado"); + return; + } + $event = $this->eventos->findEventFinish($request['uniqueid']); + //verifica se o atendimento ja foi finalizado + if (!empty($event)) { + $this->retorno("Atendimento já 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'), - $request['fila'] + $atendimento->fila, + $request['matricula'] + ); + $this->retorno( + $ret ? "Finalizado com sucesso" : "Erro", + $ret ? $ret : null, + $ret ? $ret : null ); - $this->retorno($ret, $ret); } catch (\Throwable $th) { - $this->retorno($ret, $th->getMessage()); + + $this->retorno($th->getMessage()); } return null; } @@ -235,14 +265,17 @@ class ApiAgente ); } $modelMensagem->addMessage( - $mensagem['id_atendimento'], - $contact['matricula'], + $mensagem['uniqueid'], + $contact['number'], $mensagem['dst'], $mensagem['type'], $mensagem['type'] == 'text' ? $mensagem['content'] : $mensagem['id_provedor'], $contact['name'], $mensagem['media'], - $mensagem['status'] + $mensagem['status'], + $mensagem['mimetype'], + $mensagem['file_name'], + $mensagem['id_provedor'] ); echo json_encode(['status' => $retuno]); return null; @@ -267,12 +300,22 @@ class ApiAgente return null; } - function retorno($status, $mensagem) + function retorno($mensagem, $status = null, $id = null) { - echo json_encode([ - 'status' => $status, - "mensagem" => utf8_encode($mensagem) - ]); + //{ "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($id)) { + $data['data'] = [['id' => $id]]; + } + + echo json_encode($data); } function listaAtendimentoAgent($request) @@ -305,17 +348,17 @@ class ApiAgente $mensagem["event"] = [ "type" => "mensagem", "contact" => [ - "name" => $value->profile_name, + "name" => utf8_encode($value->profile_name), "number" => $value->src, ], "mensagem" => [ "type" => $value->type, "content" => utf8_encode($value->content), - "id_provedor" => $value->idProvedor, + "id_provedor" => $value->id_provedor, "dst" => $value->dst, "uniqueid" => $value->uniqueid, "media" => $value->media, - "file_name" => $value->file_name, + "file_name" => utf8_encode($value->file_name), "datetime" => $value->msg_date, "status" => $value->status, 'mimetype' => $value->mimetype @@ -380,11 +423,18 @@ class ApiAgente function transfAtendimento($request) { $retunr = $this->agentController->transfer( - $request['matOrigem'], - $request['matDestino'], + $request['matricula_origem'], + $request['matricula_destino'], $request['uniqueid'] ); echo $this->retorno($retunr ? true : false, $retunr); return null; } -} \ No newline at end of file + function markMessegeRead($request) + { + $modelMensagem = new Message(); + $retunr = $modelMensagem->markMessege($request['uniqueid'], 'read'); + echo $this->retorno($retunr ? true : false, $retunr); + return null; + } +} diff --git a/app/Models/Atendimento.php b/app/Models/Atendimento.php index 53a6bb4..d07034e 100644 --- a/app/Models/Atendimento.php +++ b/app/Models/Atendimento.php @@ -51,9 +51,10 @@ class Atendimento extends Model public function findAtendAgent($matricula) { - $this->query = "SELECT ma.* FROM {$this->atendimento} ma - WHERE ma.matricula = :matricula - LIMIT 10"; + $this->query = "SELECT ma.*, (SELECT profile_name FROM md_message mm WHERE uniqueid = ma.uniqueid AND src = ma.cliente_id LIMIT 1 ) AS profile_name + FROM {$this->atendimento} ma + WHERE ma.matricula = :matricula + LIMIT 10"; return $this->read($this->query, ['matricula' => $matricula])->fetchAll(); } diff --git a/app/Models/Evento.php b/app/Models/Evento.php index d89e3d8..28e7253 100644 --- a/app/Models/Evento.php +++ b/app/Models/Evento.php @@ -9,26 +9,37 @@ class Evento extends Model { private $evento = 'md_evento'; - public function createEvento($uniqueid, $evento, $data_evento, $data_reg, $fila = null) + 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 ) + fila, + matricula ) VALUES(:uniqueid, :evento, :data_evento, :data_reg, - :fila );"; + :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; } -} \ No newline at end of file + + 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(); + } +} diff --git a/app/Models/Message.php b/app/Models/Message.php index 414b60c..16445d3 100644 --- a/app/Models/Message.php +++ b/app/Models/Message.php @@ -14,12 +14,12 @@ class Message extends Model const MESSAGE = "md_message"; - public function addMessage($idAtendimento, $src, $dst, $tipo, $conteudo, $profile_name, $media, $status) + public function addMessage($uniqueid, $src, $dst, $tipo, $conteudo, $profile_name, $media, $status, $mimetype, $file_name, $id_provedor) { - $this->query = "INSERT INTO " . self::MESSAGE . " (uniqueid, src, dst, type, content, profile_name, media, status) - VALUES(:uniqueid, :src, :dst, :type, :content, :profile_name, :media, :status);"; + $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' => $idAtendimento, + 'uniqueid' => $uniqueid, 'src' => $src, 'dst' => $dst, 'type' => $tipo, @@ -27,12 +27,15 @@ class Message extends Model 'profile_name' => utf8_decode($profile_name), 'media' => $media, 'status' => $status, + 'id_provedor' => $id_provedor, + 'file_name' => $file_name, + 'mimetype' => $mimetype ]); } public function findMessageByUniqueid($uniqueid) { - $this->query = "SELECT * FROM " . self::MESSAGE . " WHERE uniqueid = :uniqueid"; + $this->query = "SELECT * FROM " . self::MESSAGE . " WHERE uniqueid = :uniqueid ORDER BY id"; return $this->read($this->query, ['uniqueid' => $uniqueid])->fetchAll(); } @@ -48,4 +51,11 @@ class Message extends Model $this->query .= " ORDER BY msg_date DESC LIMIT 1"; return $this->read($this->query, ['uniqueid' => $uniqueid])->fetch(); } -} \ No newline at end of file + 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); + } +} diff --git a/app/Models/Supervisor.php b/app/Models/Supervisor.php index 5e89e56..6bf1905 100644 --- a/app/Models/Supervisor.php +++ b/app/Models/Supervisor.php @@ -73,7 +73,7 @@ class Supervisor extends Model return $this->read($this->query, ['matricula' => $matricula])->fetch(); } - public function findAgentByQueue($queue) + public function findAgentByQueue($queue, $status) { $data = []; $this->query = "SELECT * @@ -85,9 +85,11 @@ class Supervisor extends Model WHERE me.evento = 'EMESPERA' AND maa.matricula = ms.matricula ) < 3 - AND ms.fila = :queue"; + AND ms.fila = :queue + AND ms.status = :status"; $data['queue'] = $queue; + $data['status'] = $status; return $this->read($this->query, $data)->fetchAll(); } @@ -246,4 +248,4 @@ class Supervisor extends Model $data['ramal'] = $ramal; return $this->update($this->query, $data); } -} \ No newline at end of file +} From 2e28c7dc6e5f65ef186f9b4df36ab0a85b9e37c4 Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Tue, 14 Dec 2021 10:20:52 -0400 Subject: [PATCH 025/144] =?UTF-8?q?re-fatora=C3=A7=C3=A3o=20dos=20retornos?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Controllers/AgentController.php | 10 +- app/Core/CoreMedia.php | 6 +- app/Middleware/ApiAgente.php | 307 +++++++++++++++++----------- app/Models/Atendimento.php | 24 ++- app/Models/Pause.php | 8 +- app/Models/Supervisor.php | 45 +++- app/Providers/Positus.php | 17 +- app/Providers/RequestURL.php | 12 +- 8 files changed, 275 insertions(+), 154 deletions(-) diff --git a/app/Controllers/AgentController.php b/app/Controllers/AgentController.php index 279251e..430b831 100644 --- a/app/Controllers/AgentController.php +++ b/app/Controllers/AgentController.php @@ -59,7 +59,7 @@ class AgentController extends Controller { try { $this->agent->begin(); - $atendimento = $this->agent->findByNumber($telefone); + $atendimento = $this->agent->findByMatricula($telefone); $agent = $this->agent->findAgentByRamal($atendimento->ramal); if (!$atendimento || !$agent->origem_destino) { throw new Exception('Você precisa estar em um atendimento para finalizar!'); @@ -105,7 +105,7 @@ class AgentController extends Controller public function inService($telefone) { try { - $atendimento = $this->agent->findByNumber($telefone); + $atendimento = $this->agent->findByMatricula($telefone); if (!$atendimento && $this->ramal->findRamal($telefone)) { return false; } else { @@ -138,7 +138,7 @@ class AgentController extends Controller } - if ($this->agent->findByNumber($matricula)) { + if ($this->agent->findByMatricula($matricula)) { return 'Agente já autenticado!'; } /** @@ -147,7 +147,7 @@ class AgentController extends Controller // $classificacao = new ClassificacaoController(); // $chamadaSemClassificacao = $classificacao->agentClassificacaoPending($agent->matricula, $fila); - if (!$this->agent->addAgent($agent->matricula, $fila)) { + if (!$this->agent->addAgent($agent->matricula, $fila, $agent->nome)) { return 'Não foi possével inserir o agente!'; } @@ -511,4 +511,4 @@ class AgentController extends Controller return false; } } -} +} \ No newline at end of file diff --git a/app/Core/CoreMedia.php b/app/Core/CoreMedia.php index 18dba5b..8477545 100644 --- a/app/Core/CoreMedia.php +++ b/app/Core/CoreMedia.php @@ -68,7 +68,11 @@ class CoreMedia $this->api = $api; $this->api->setHook($this->request); - + $msg = $this->api->getIsValidMessage(); + logger('deburguer')->info(var_export($msg, true)); + if (empty($msg)) { + return; + } if (!$this->api->baixarMidia()) { $this->api->enviarMsg($this->api->getPhone(), utf8_encode("Não foi possivel entregar o aquivo para o agente. Envie novamente!")); } diff --git a/app/Middleware/ApiAgente.php b/app/Middleware/ApiAgente.php index 425c607..59ec209 100644 --- a/app/Middleware/ApiAgente.php +++ b/app/Middleware/ApiAgente.php @@ -54,9 +54,6 @@ class ApiAgente case 'enviarMensagem': $this->enviaMsg($request); break; - case 'getArquivo': - $this->getArquivo($request); - break; case 'sair': $this->logoff($request); break; @@ -87,6 +84,9 @@ class ApiAgente case 'marcarMensagemVista': $this->markMessegeRead($request); break; + case 'statusAgente': + $this->statusAgente($request); + break; default: echo json_encode(['status' => '404']); break; @@ -96,19 +96,33 @@ class ApiAgente function login($request) { try { + $agente = $this->supervisor->findByAgent($request['matricula']); + //verifica se existe agente + if (empty($agente)) { + $this->retorno("Agente não encontrado"); + return; + } + $filaModel = new Queue(); - $fila = $filaModel->findQueueById($request['fila']); + $fila = $filaModel->findQueueById($request['id_fila']); + if (empty($fila)) { + $this->retorno("Fila não encontrada"); + return; + } $ret = $this->agentController->login( $fila->nome, $request['matricula'] ); - - echo json_encode([ - 'status' => $ret ? 'sucesso' : 'falha', - "mensagem" => utf8_encode($ret) - ]); + if (is_string($ret)) { + $this->retorno($ret); + return; + } + $this->retorno( + $ret ? "Logado com sucesso" : "Erro", + $ret ? $ret : null, + ); } catch (\Throwable $th) { - $this->retorno($ret, $th->getMessage()); + $this->retorno($th->getMessage()); } return null; } @@ -116,15 +130,30 @@ class ApiAgente function logoff($request) { try { + $agente = $this->supervisor->findAgentByMatricula($request['matricula']); + //verifica se existe agente + $atends = $this->atendimento->getAtendimentoAbertoByAgente($request['matricula']); + if (!empty($atends)) { + $this->retorno("Atendimentos em aberto, finalize os atendimentos para fazer logoff'"); + return; + } + if (empty($agente)) { + $this->retorno("Agente não encontrado"); + return; + } $ret = $this->agentController->logoff( $request['matricula'] ); - echo json_encode([ - 'status' => $ret ? 'sucesso' : 'falha', - "mensagem" => utf8_encode($ret) - ]); + if (is_string($ret)) { + $this->retorno($ret); + return; + } + $this->retorno( + $ret ? "Deslogado com sucesso" : "Erro", + $ret ? $ret : null, + ); } catch (\Throwable $th) { - $this->retorno($ret, $th->getMessage()); + $this->retorno($th->getMessage()); } return null; } @@ -142,7 +171,7 @@ class ApiAgente } echo json_encode($agentes); } catch (\Throwable $th) { - $this->retorno($ret, $th->getMessage()); + $this->retorno($th->getMessage()); } return null; } @@ -150,7 +179,7 @@ class ApiAgente function finalizaAtendimento($request) { try { - $agente = $this->atendimento->findAtendAgent($request['matricula']); + $agente = $this->supervisor->findAgentByMatricula($request['matricula']); //verifica se existe agente if (empty($agente)) { $this->retorno("Agente não encontrado"); @@ -179,7 +208,7 @@ class ApiAgente $this->retorno( $ret ? "Finalizado com sucesso" : "Erro", $ret ? $ret : null, - $ret ? $ret : null + $ret ? ["id" => $ret] : null ); } catch (\Throwable $th) { @@ -242,6 +271,7 @@ class ApiAgente echo json_encode(['status' => "false"]); return null; } + public function enviaMsg($request) { try { @@ -250,6 +280,7 @@ class ApiAgente $provider = new Positus(); $modelMensagem = new Message(); if ($mensagem['type'] == 'text') { + logger('enviaMsg')->info(print_r($mensagem, true)); $retuno = $provider->enviarMsg($mensagem['dst'], $mensagem['content']); } else { $anmeArquivo = __DIR__ . "/../../storage/files/" . $mensagem['id_provedor']; @@ -277,30 +308,20 @@ class ApiAgente $mensagem['file_name'], $mensagem['id_provedor'] ); - echo json_encode(['status' => $retuno]); - return null; - } catch (\Throwable $th) { - $this->retorno($retuno, $th->getMessage()); - return null; - } - } - - public function envia() - { - } + $this->retorno( + $retuno ? "Sucesso" : "Erro", + $retuno ? $retuno : null, + $retuno ? $retuno : null, + ); - function getArquivo($request) - { - try { - $file = file_get_contents(__DIR__ . "/../../storage/files/" . $request['file']); - echo json_encode(['file' => base64_encode($file)]); + return; } catch (\Throwable $th) { - echo json_encode(['status' => false, 'message' => '' . $th->getMessage()]); + $this->retorno($th->getMessage()); + return; } - return null; } - function retorno($mensagem, $status = null, $id = null) + function retorno($mensagem, $status = null, $dados = null) { //{ "status": "success", "message": "register created!", "data": [ "id": 20 ] } $data = []; @@ -311,8 +332,8 @@ class ApiAgente } else { $data['status'] = "error"; } - if (!empty($id)) { - $data['data'] = [['id' => $id]]; + if (!empty($dados)) { + $data['data'] = $dados; } echo json_encode($data); @@ -320,103 +341,126 @@ class ApiAgente function listaAtendimentoAgent($request) { - if ($request['matricula']) { - $retunr = $this->atendimento->findAtendAgent($request['matricula']); - if ($retunr) { - echo json_encode($retunr); - } else { - echo $this->retorno($retunr, $retunr); - } - return null; - } else { - echo $this->retorno(false, "O parametro matricula é obrigatorio"); - return null; + $agente = $this->supervisor->findAgentByMatricula($request['matricula']); + //verifica se existe agente + if (empty($agente)) { + $this->retorno("Agente não encontrado"); + return; } + + $ret = $this->atendimento->findAtendAgent($request['matricula']); + $this->retorno( + $ret ? "Sucesso" : "Erro", + $ret ? $ret : null, + $ret ? $ret : null + ); return null; } function listaMensagem($request) { try { - if ($request['uniqueid']) { - $messageModel = new Message(); - $retunr = $messageModel->findMessageByUniqueid($request['uniqueid']); - if ($retunr) { - $mensagenss = []; - foreach ($retunr as $key => $value) { - $mensagem = []; - $mensagem["event"] = [ - "type" => "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); - } - echo json_encode($mensagenss); - return null; - } else { - echo $this->retorno($retunr, $retunr); - return null; - } - } else { - echo $this->retorno(false, "O parametro uniqueid é obrigatorio"); - return null; + $atend = $this->atendimento->findAtendId($request['uniqueid']); + //verifica se existe agente + if (empty($atend)) { + $this->retorno("Nã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" => "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); } + $this->retorno( + $mensagenss ? "Sucesso" : "Erro", + $mensagenss ? $mensagenss : null, + $mensagenss ? $mensagenss : null + ); + return; } catch (\Throwable $th) { - $this->retorno(false, $th->getMessage()); - return null; + $this->retorno($th->getMessage()); + return; } } function listaPausasAgente($request) { - if ($request['matricula']) { - $param = $this->parametros->findProtocolByParams(); - if ($param->prm_pausa_grupo) { - $retunr = $this->pausasModel->findPauseByGroupUser($request['matricula']); - } - if (empty($retunr)) { - $retunr = $this->pausasModel->findAllPause(); - } - - if ($retunr) { - echo json_encode($retunr); - } else { - echo $this->retorno($retunr, $retunr); - } - return null; - } else { - echo $this->retorno(false, "O parametro matricula é obrigatorio"); - return null; + $agente = $this->supervisor->findAgentByMatricula($request['matricula']); + //verifica se existe agente + if (empty($agente)) { + $this->retorno("Agente não encontrado"); + return; } - return null; + $param = $this->parametros->findProtocolByParams(); + if ($param->prm_pausa_grupo) { + $retunr = $this->pausasModel->findPauseByGroupUser($request['matricula']); + } + if (empty($retunr)) { + $retunr = $this->pausasModel->findAllPause(); + } + $this->retorno( + $retunr ? "Sucesso" : "Erro", + $retunr ? $retunr : null, + $retunr ? $retunr : null + ); + return; } function entrarPausa($request) { - $retunr = $this->agentController->enterPause($request['matricula'], $request['pausa']); - echo $this->retorno($retunr ? true : false, $retunr); + $agente = $this->supervisor->findAgentByMatricula($request['matricula']); + //verifica se existe agente + if (empty($agente)) { + $this->retorno("Agente não encontrado"); + return; + } + $pausa = $this->pausasModel->findPauseById($request['id_pausa']); + if (empty($pausa)) { + $this->retorno("Pausa não encontrado"); + return; + } + $ret = $this->agentController->enterPause($request['matricula'], $pausa->motivo); + $this->retorno( + $ret ? "Agente em 'pausa'" : "Erro", + $ret ? $ret : null, + ); return null; } function saiPausa($request) { - $retunr = $this->agentController->exitPause($request['matricula']); - echo $this->retorno($retunr ? true : false, $retunr); + $agente = $this->supervisor->findAgentByMatricula($request['matricula']); + //verifica se existe agente + if (empty($agente)) { + $this->retorno("Agente não encontrado"); + return; + } + $ret = $this->agentController->exitPause($request['matricula']); + $this->retorno( + $ret ? "Agente 'livre'" : "Erro", + $ret ? $ret : null, + ); return null; } @@ -427,14 +471,45 @@ class ApiAgente $request['matricula_destino'], $request['uniqueid'] ); - echo $this->retorno($retunr ? true : false, $retunr); + $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ão existe atendimento para esse uniqueid"); + return; + } $modelMensagem = new Message(); - $retunr = $modelMensagem->markMessege($request['uniqueid'], 'read'); - echo $this->retorno($retunr ? true : false, $retunr); + $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ão encontrado"); + return; + } + $ret = $this->supervisor->statusAgente($request['matricula']); + $this->retorno( + $ret ? "Sucesso" : "Erro", + $ret ? $ret : null, + $ret ? $ret : null + ); + } catch (\Throwable $th) { + $this->retorno($th->getMessage()); + } + } +} \ No newline at end of file diff --git a/app/Models/Atendimento.php b/app/Models/Atendimento.php index d07034e..0c05040 100644 --- a/app/Models/Atendimento.php +++ b/app/Models/Atendimento.php @@ -51,7 +51,13 @@ class Atendimento extends Model public function findAtendAgent($matricula) { - $this->query = "SELECT ma.*, (SELECT profile_name FROM md_message mm WHERE uniqueid = ma.uniqueid AND src = ma.cliente_id LIMIT 1 ) AS profile_name + $this->query = "SELECT ma.*, + (SELECT profile_name FROM md_message mm WHERE uniqueid = ma.uniqueid AND src = ma.cliente_id LIMIT 1 ) AS profile_name, + (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 WHERE ma.matricula = :matricula LIMIT 10"; @@ -61,18 +67,20 @@ class Atendimento extends Model public function findAtenEmAberto($cliente_id) { $this->query = "SELECT ma.* FROM {$this->atendimento} ma - LEFT JOIN {$this->evento} me - ON ma.uniqueid = me.uniqueid WHERE ma.cliente_id = :cliente_id - AND (SELECT count(*) - FROM md_evento m2 - WHERE ma.uniqueid = m2.uniqueid - AND m2.evento IN ('COMPLETE_AGENT','ABANDON', 'TIMEOUT_CALLER','TIMEOUT_AGENT') - ) = 0 + AND (SELECT m2.evento FROM md_evento m2 WHERE ma.uniqueid = m2.uniqueid ORDER BY id DESC LIMIT 1) = 'START' AND ma.matricula notnull "; return $this->read($this->query, ['cliente_id' => $cliente_id])->fetch(); } + 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 = 'EMESPERA') { $this->query = "SELECT ma.*, me.fila as fila FROM {$this->atendimento} ma diff --git a/app/Models/Pause.php b/app/Models/Pause.php index 5c58b45..d02ab9b 100644 --- a/app/Models/Pause.php +++ b/app/Models/Pause.php @@ -22,6 +22,12 @@ class Pause extends Model 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) @@ -86,4 +92,4 @@ class Pause extends Model $this->query = "SELECT status, motivo_pausa FROM pbx_supervisor_agentes WHERE matricula = :matricula;"; return $this->read($this->query, ['matricula' => $matricula])->fetchAll(); } -} \ No newline at end of file +} diff --git a/app/Models/Supervisor.php b/app/Models/Supervisor.php index 6bf1905..833adb5 100644 --- a/app/Models/Supervisor.php +++ b/app/Models/Supervisor.php @@ -31,7 +31,39 @@ class Supervisor extends Model return $this->read($this->query)->fetchAll(); } - public function findByNumber($matricula) + public function statusAgente($matricula) + { + $this->query = "SELECT + *, + ( + SELECT + count(*) + FROM + md_atendimento ma + INNER JOIN md_evento me ON me.uniqueid = ma.uniqueid + WHERE + me.evento = 'START' + AND 'START' = ( + SELECT + m2.evento + FROM + md_evento m2 + WHERE + ma.uniqueid = m2.uniqueid + ORDER BY 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) { $this->query = "SELECT * " . "FROM " . self::SUPERVISOR_AGENTE . " WHERE (matricula = :matricula)"; @@ -97,7 +129,7 @@ class Supervisor extends Model ### SUPERVISOR AGENTE ### ######################################## - public function addAgent($matricula, $fila, $chamada_classificado = 1) + public function addAgent($matricula, $fila, $nome, $chamada_classificado = 1) { $this->query = "INSERT INTO " . self::SUPERVISOR_AGENTE . " ( @@ -106,7 +138,8 @@ class Supervisor extends Model fila, status, duracao, - chamada_classificado + chamada_classificado, + nome ) VALUES( :matricula, @@ -114,7 +147,8 @@ class Supervisor extends Model :fila, :status, :duracao, - :chamada_classificado + :chamada_classificado, + :nome );"; return $this->create($this->query, [ @@ -123,6 +157,7 @@ class Supervisor extends Model 'fila' => $fila, 'status' => 'LIVRE', 'duracao' => 'now()', + 'nome' => $nome, 'chamada_classificado' => $chamada_classificado ]); } @@ -248,4 +283,4 @@ class Supervisor extends Model $data['ramal'] = $ramal; return $this->update($this->query, $data); } -} +} \ No newline at end of file diff --git a/app/Providers/Positus.php b/app/Providers/Positus.php index 5ce8bf0..a3ac102 100644 --- a/app/Providers/Positus.php +++ b/app/Providers/Positus.php @@ -96,7 +96,6 @@ class Positus implements IApiMedia function enviarMsg($whatsapp, $mensagem) { $this->debug = debug_backtrace(); - //if ($this->getArgs(func_get_args())) { $this->params = array( "to" => "+$whatsapp", "type" => "text", @@ -105,8 +104,6 @@ class Positus implements IApiMedia $this->requestType("POST"); $this->setMetodo('messages'); return $this->exec(); - // } - // return false; } function enviarMsgIterativaLista($whatsapp, $mensagem, $nomeButton, $lista, $prex = '') @@ -465,22 +462,20 @@ class Positus implements IApiMedia } $this->request->header($header); $this->request->method_request($this->requestType); - - $response = $this->request->exec_request(); - return $this->response($response); + return $this->request->exec_request(); } function response($result) { - logger('deburguer')->info(gettype($result)); + logger('deburguer')->info(print_r($result, true)); if ($result) { try { if (json_decode($result, true) !== null) { return json_decode($result, true); } - return $result; - } catch (\Throwable $th) { - logger('deburguer')->info(print_r($result, true)); + return json_decode($result, true); + } catch (\Exception $th) { + logger('deburguer')->info($th->getMessage()); return false; } } else { @@ -503,4 +498,4 @@ class Positus implements IApiMedia } return null; } -} \ No newline at end of file +} diff --git a/app/Providers/RequestURL.php b/app/Providers/RequestURL.php index 22cf8c1..6c0b268 100644 --- a/app/Providers/RequestURL.php +++ b/app/Providers/RequestURL.php @@ -70,12 +70,10 @@ class RequestURL private function response($result) { - if ($result) { - if (json_decode($result, true) !== null) { - return json_decode($result, true); - } - return $result; - } else { + logger('deburguer')->info(print_r($result, true)); + try { + return json_decode($result, true); + } catch (\Throwable $th) { return false; } } @@ -98,4 +96,4 @@ class RequestURL curl_setopt($this->curl, CURLOPT_CONNECTTIMEOUT, self::CONF_TIMEOUT); curl_setopt($this->curl, CURLOPT_TIMEOUT, self::CONF_TIMEOUT); } -} \ No newline at end of file +} From 094cf8ee34bfdeaa1452c440a46310a75e33f040 Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Tue, 14 Dec 2021 18:26:36 -0400 Subject: [PATCH 026/144] update --- app/Controllers/AgentController.php | 35 +++++++++++++- app/Core/CoreMedia.php | 4 +- app/Middleware/ApiAgente.php | 39 ++++++++++++++- app/Models/Atendimento.php | 2 +- app/Models/Pause.php | 18 ++++++- app/Models/Supervisor.php | 75 ++++++++++++++++++++++++----- config/agent.php | 1 + config/event.php | 1 + 8 files changed, 155 insertions(+), 20 deletions(-) diff --git a/app/Controllers/AgentController.php b/app/Controllers/AgentController.php index 430b831..892c5bc 100644 --- a/app/Controllers/AgentController.php +++ b/app/Controllers/AgentController.php @@ -200,7 +200,32 @@ class AgentController extends Controller } return false; } + public function indisponivelAtendimento($matricula, $pausa) + { + try { + + $this->agent->begin(); + $agent = $this->agent->findAgentByMatricula($matricula); + $queue = $this->queue->findQueueByName($agent->fila); + $pause = $this->pause->findPauseByName($pausa); + 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 (!$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->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($matricula, $pausa) { try { @@ -222,7 +247,7 @@ 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!'); } @@ -303,6 +328,14 @@ class AgentController extends Controller $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 + ); $this->agent->commit(); return true; } catch (Exception $ex) { diff --git a/app/Core/CoreMedia.php b/app/Core/CoreMedia.php index 8477545..7d8d02c 100644 --- a/app/Core/CoreMedia.php +++ b/app/Core/CoreMedia.php @@ -229,7 +229,7 @@ class CoreMedia return null; } //cria o evento de inicio de atendimento e criar o protocolo - $this->eventos->createEvento($uniqueid, 'START', date('Y-m-d H:i:s'), date('Y-m-d H:i:s'), $fila); + $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); $protocol = $this->bilheteController->generateProtocol($uniqueid); $this->api->enviarMsg( $numero, @@ -326,4 +326,4 @@ class CoreMedia return null; } } -} +} \ No newline at end of file diff --git a/app/Middleware/ApiAgente.php b/app/Middleware/ApiAgente.php index 59ec209..3c71c15 100644 --- a/app/Middleware/ApiAgente.php +++ b/app/Middleware/ApiAgente.php @@ -159,7 +159,7 @@ class ApiAgente } function listaAgentesDisponivel() - { + {; try { $ret = $this->supervisor->listaAgentesDisponivel(); @@ -169,7 +169,14 @@ class ApiAgente array_push($agentes, $value); } } - echo json_encode($agentes); + if ($agentes) { + # code... + } + $this->retorno( + $agentes ? "Sucesso" : "Nenhum agente disponivel", + $agentes ? $agentes : null, + $agentes ? $agentes : null + ); } catch (\Throwable $th) { $this->retorno($th->getMessage()); } @@ -205,6 +212,13 @@ class ApiAgente $atendimento->fila, $request['matricula'] ); + if ($agente->status == 'INDISPONIVEL') { + $atends = $this->atendimento->getAtendimentoAbertoByAgente($request['matricula']); + if (empty($atends)) { + $this->agentController->enterPause($request['matricula'], $agente->motivo_pausa); + } + } + $this->retorno( $ret ? "Finalizado com sucesso" : "Erro", $ret ? $ret : null, @@ -349,6 +363,11 @@ class ApiAgente } $ret = $this->atendimento->findAtendAgent($request['matricula']); + if ($ret) { + foreach ($ret as $key => $value) { + $value->profile_name = utf8_encode($value->profile_name); + } + } $this->retorno( $ret ? "Sucesso" : "Erro", $ret ? $ret : null, @@ -440,6 +459,22 @@ class ApiAgente $this->retorno("Pausa não encontrado"); return; } + if ($agente->status != 'LIVRE') { + $this->retorno('Agente precisa estar livre para entrar em pausa!'); + } + $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 { + $this->retorno( + "Agente em 'indisponivel'", + $ret, + ); + } + return; + } $ret = $this->agentController->enterPause($request['matricula'], $pausa->motivo); $this->retorno( $ret ? "Agente em 'pausa'" : "Erro", diff --git a/app/Models/Atendimento.php b/app/Models/Atendimento.php index 0c05040..7a98b1f 100644 --- a/app/Models/Atendimento.php +++ b/app/Models/Atendimento.php @@ -52,7 +52,7 @@ class Atendimento extends Model public function findAtendAgent($matricula) { $this->query = "SELECT ma.*, - (SELECT profile_name FROM md_message mm WHERE uniqueid = ma.uniqueid AND src = ma.cliente_id LIMIT 1 ) AS profile_name, + (SELECT profile_name FROM md_message mm WHERE uniqueid = ma.uniqueid AND src = ma.cliente_id LIMIT 1) AS profile_name, (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 diff --git a/app/Models/Pause.php b/app/Models/Pause.php index d02ab9b..4b1dca4 100644 --- a/app/Models/Pause.php +++ b/app/Models/Pause.php @@ -47,6 +47,22 @@ 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 @@ -92,4 +108,4 @@ class Pause extends Model $this->query = "SELECT status, motivo_pausa FROM pbx_supervisor_agentes WHERE matricula = :matricula;"; return $this->read($this->query, ['matricula' => $matricula])->fetchAll(); } -} +} \ No newline at end of file diff --git a/app/Models/Supervisor.php b/app/Models/Supervisor.php index 833adb5..1f49a66 100644 --- a/app/Models/Supervisor.php +++ b/app/Models/Supervisor.php @@ -19,14 +19,50 @@ class Supervisor extends Model public function listaAgentesDisponivel() { $this->query = " SELECT *, - (SELECT count(*) - FROM md_atendimento maa - INNER JOIN md_evento me - ON me.uniqueid = maa.uniqueid - WHERE me.evento = 'EMESPERA' - AND maa.matricula = ms.matricula + ( + SELECT + count(*) + FROM + md_atendimento ma + INNER JOIN md_evento me ON me.uniqueid = ma.uniqueid + WHERE + me.evento = 'START' + AND 'START' = ( + SELECT + m2.evento + FROM + md_evento m2 + WHERE + ma.uniqueid = m2.uniqueid + ORDER BY id DESC + LIMIT 1 + ) + AND ma.matricula = ms.matricula + ) AS countAtendimentos FROM md_supervisor ms + WHERE ms.status = 'LIVRE' + AND ( + SELECT + count(*) + FROM + md_atendimento ma + INNER JOIN md_evento me ON me.uniqueid = ma.uniqueid + WHERE + me.evento = 'START' + AND 'START' = ( + SELECT + m2.evento + FROM + md_evento m2 + WHERE + ma.uniqueid = m2.uniqueid + ORDER BY id DESC + LIMIT 1 + ) + AND ma.matricula = ms.matricula + + ) < 3 "; return $this->read($this->query)->fetchAll(); } @@ -110,13 +146,26 @@ class Supervisor extends Model $data = []; $this->query = "SELECT * FROM md_supervisor ms - WHERE (SELECT count(*) - FROM md_atendimento maa - INNER JOIN md_evento me - ON me.uniqueid = maa.uniqueid - WHERE me.evento = 'EMESPERA' - AND maa.matricula = ms.matricula - ) < 3 + WHERE ( + SELECT + count(*) + FROM + md_atendimento ma + INNER JOIN md_evento me ON me.uniqueid = ma.uniqueid + WHERE + me.evento = 'START' + AND 'START' = ( + SELECT + m2.evento + FROM + md_evento m2 + WHERE + ma.uniqueid = m2.uniqueid + ORDER BY id DESC + LIMIT 1 + ) + AND ma.matricula = ms.matricula + ) < 3 AND ms.fila = :queue AND ms.status = :status"; diff --git a/config/agent.php b/config/agent.php index 0f0881a..65ec3b3 100644 --- a/config/agent.php +++ b/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'); \ No newline at end of file diff --git a/config/event.php b/config/event.php index 26b1f23..2e35163 100644 --- a/config/event.php +++ b/config/event.php @@ -18,4 +18,5 @@ 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_WHATSAPP_OCUPADO", 'MD_OCUPADO'); \ No newline at end of file From bb833b4d330e7ab8ba1b75bbd275f0608946ba8e Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Wed, 15 Dec 2021 09:13:48 -0400 Subject: [PATCH 027/144] orderby nas queries supervisor e atendimento --- app/Models/Atendimento.php | 1 + app/Models/Supervisor.php | 20 ++++++++++++++++++++ docker-compose.yml | 4 ++-- 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/app/Models/Atendimento.php b/app/Models/Atendimento.php index 7a98b1f..c12800e 100644 --- a/app/Models/Atendimento.php +++ b/app/Models/Atendimento.php @@ -60,6 +60,7 @@ class Atendimento extends Model END AS status FROM {$this->atendimento} ma WHERE ma.matricula = :matricula + ORDER BY status DESC LIMIT 10"; return $this->read($this->query, ['matricula' => $matricula])->fetchAll(); } diff --git a/app/Models/Supervisor.php b/app/Models/Supervisor.php index 1f49a66..8776edb 100644 --- a/app/Models/Supervisor.php +++ b/app/Models/Supervisor.php @@ -63,6 +63,26 @@ class Supervisor extends Model AND ma.matricula = ms.matricula ) < 3 + ORDER BY ( + SELECT + count(*) + FROM + md_atendimento ma + INNER JOIN md_evento me ON me.uniqueid = ma.uniqueid + WHERE + me.evento = 'START' + AND 'START' = ( + SELECT + m2.evento + FROM + md_evento m2 + WHERE + ma.uniqueid = m2.uniqueid + ORDER BY id DESC + LIMIT 1 + ) + AND ma.matricula = ms.matricula + ) "; return $this->read($this->query)->fetchAll(); } diff --git a/docker-compose.yml b/docker-compose.yml index 8e38bec..7f01720 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -4,7 +4,7 @@ services: container_name: mid build: . volumes: - - ./app:/app + - $PWD/data:/var/www/html/aplicativo/integracao/media/storage/files ports: - 8090:8090 - - 8081:8081 \ No newline at end of file + - 8081:8081 From 2a21da0b5245f8460dadd9aae4b45fb70c5accc3 Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Wed, 15 Dec 2021 16:12:08 -0400 Subject: [PATCH 028/144] varias melhorias --- app/Core/Commands.php | 51 +++++++++++++++++++++++++++++++----- app/Core/CoreMedia.php | 9 ++++++- app/Middleware/ApiAgente.php | 37 ++++++++++++++++++++++++-- app/Models/Atendimento.php | 14 ++-------- app/Models/Supervisor.php | 6 ++--- config/agent.php | 3 ++- websocket/WsInterface.php | 23 ++++++++++++++++ 7 files changed, 118 insertions(+), 25 deletions(-) diff --git a/app/Core/Commands.php b/app/Core/Commands.php index c64ce1b..17e330a 100644 --- a/app/Core/Commands.php +++ b/app/Core/Commands.php @@ -4,6 +4,10 @@ namespace app\Core; use app\Controllers\AgentController; use app\Controllers\BilheteController; +use app\Models\Atendimento; +use app\Models\Evento; +use app\Models\Supervisor; +use websocket\WsInterface; /** * @@ -246,12 +250,47 @@ class Commands 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!"; + try { + $atendimentoModel = new Atendimento(); + $eventosModel = new Evento(); + $supervisorModel = new Supervisor(); + $atendimento = $atendimentoModel->getAtendimentoByCliente($numero, CONF_EVENT_START); + //verifica se existe atendimento + if (empty($atendimento)) { + $this->callback = CONF_NAME_REPONSE . " : Não foi encontrado atendimento em aberto"; + return false; + } + $ret = $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); + $ws->enviaActions('Atendimento finalizado', 'FINISH', $agente->matricula, $atendimento->uniqueid); + if ($agente->status == CONF_AGENT_STATUS_INDISPONIVEL) { + $atends = $this->atendimento->getAtendimentoAbertoByAgente($atendimento->matricula); + if (empty($atends)) { + $this->agentController->enterPause($atendimento->matricula, $agente->motivo_pausa); + } + } + if ($agente->status == CONF_AGENT_STATUS_OCUPADO) { + $atendimentosAbertos = $this->atendimento->getAtendimentoAbertoByAgente($agente->matricula); + if (count($atendimentosAbertos) < CONF_ATENDIMENTOS_SIMULTANEOS) { + $this->supervisor->updateAgent($agente->matricula, CONF_AGENT_STATUS_LIVRE); + } + } + $this->callback = CONF_NAME_REPONSE . " : Atendimento finalizado"; + return true; + } else { + $this->callback = CONF_NAME_REPONSE . " : Erro ao finalizar!"; + return false; + } + } catch (\Throwable $th) { + $this->callback = CONF_NAME_REPONSE . " : " . $th->getMessage(); return false; } } else { diff --git a/app/Core/CoreMedia.php b/app/Core/CoreMedia.php index 7d8d02c..22fcf3c 100644 --- a/app/Core/CoreMedia.php +++ b/app/Core/CoreMedia.php @@ -228,9 +228,16 @@ class CoreMedia ); return null; } + $atendimentosAbertos = $this->atendimento->getAtendimentoAbertoByAgente($agent[0]->matricula); + $sup = new Supervisor(); + if ((count($atendimentosAbertos) + 1) >= CONF_ATENDIMENTOS_SIMULTANEOS) { + $sup->updateAgent($agent[0]->matricula, CONF_AGENT_STATUS_OCUPADO); + } + //cria o evento de inicio de atendimento e criar o protocolo $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); $protocol = $this->bilheteController->generateProtocol($uniqueid); + $this->api->enviarMsg( $numero, utf8_encode("Atendimento iniciado!\n") @@ -239,7 +246,7 @@ class CoreMedia $numero, utf8_encode("Número do protocolo do atendimento é $protocol\n") ); - return null; + return; } private function enviaLista($listas, $api, $numero, $nome, $pre = '') diff --git a/app/Middleware/ApiAgente.php b/app/Middleware/ApiAgente.php index 3c71c15..c278382 100644 --- a/app/Middleware/ApiAgente.php +++ b/app/Middleware/ApiAgente.php @@ -14,6 +14,7 @@ use app\Models\Pause; use app\Models\Queue; use app\Models\Supervisor; use app\Providers\Positus; +use websocket\WsInterface; class ApiAgente { @@ -165,7 +166,7 @@ class ApiAgente $ret = $this->supervisor->listaAgentesDisponivel(); $agentes = []; foreach ($ret as $key => $value) { - if ($value->countAtendimentos < 3) { + if ($value->countAtendimentos < CONF_ATENDIMENTOS_SIMULTANEOS) { array_push($agentes, $value); } } @@ -212,12 +213,20 @@ class ApiAgente $atendimento->fila, $request['matricula'] ); - if ($agente->status == 'INDISPONIVEL') { + $ws = new WsInterface(); + $ws->enviaMsg($this->enviaActions('Atendimento finalizado', 'FINISH', $agente->matricula, $request['uniqueid'])); + if ($agente->status == CONF_AGENT_STATUS_INDISPONIVEL) { $atends = $this->atendimento->getAtendimentoAbertoByAgente($request['matricula']); if (empty($atends)) { $this->agentController->enterPause($request['matricula'], $agente->motivo_pausa); } } + if ($agente->status == CONF_AGENT_STATUS_OCUPADO) { + $atendimentosAbertos = $this->atendimento->getAtendimentoAbertoByAgente($agente->matricula); + if (count($atendimentosAbertos) < CONF_ATENDIMENTOS_SIMULTANEOS) { + $this->supervisor->updateAgent($agente->matricula, CONF_AGENT_STATUS_LIVRE); + } + } $this->retorno( $ret ? "Finalizado com sucesso" : "Erro", @@ -291,6 +300,7 @@ class ApiAgente try { $mensagem = $request['event']['mensagem']; $contact = $request['event']['contact']; + // $this->atendimento->findAtenEmAberto(''); $provider = new Positus(); $modelMensagem = new Message(); if ($mensagem['type'] == 'text') { @@ -547,4 +557,27 @@ class ApiAgente $this->retorno($th->getMessage()); } } + + function enviaActions($msg, $tipo, $matricula, $uniqueid) + { + try { + $mensagem = []; + $mensagem["event"] = [ + "type" => 'actions', + "contact" => [ + "name" => 'Sistema', + "number" => '0' + ], + "mensagem" => [ + "type" => $tipo, + "dst" => $matricula, + "uniqueid" => $uniqueid, + "content" => utf8_encode($msg) + ], + ]; + return json_encode($mensagem); + } catch (\Throwable $th) { + logger('monitora')->info($th->getMessage(), debug_backtrace()); + } + } } \ No newline at end of file diff --git a/app/Models/Atendimento.php b/app/Models/Atendimento.php index c12800e..e77e527 100644 --- a/app/Models/Atendimento.php +++ b/app/Models/Atendimento.php @@ -16,12 +16,7 @@ class Atendimento extends Model INNER JOIN {$this->evento} me ON ma.uniqueid = me.uniqueid WHERE ma.cliente_id = :cliente_id - AND (SELECT count(*) - FROM md_evento m2 - WHERE ma.uniqueid = m2.uniqueid - AND m2.evento IN ('COMPLETE_AGENT','ABANDON', 'TIMEOUT_CALLER','TIMEOUT_AGENT') - ) = 0 - AND me.evento = :evento"; + 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(); } @@ -39,12 +34,7 @@ class Atendimento extends Model $this->query = "SELECT ma.* FROM {$this->atendimento} ma INNER JOIN {$this->evento} me ON ma.uniqueid = me.uniqueid - WHERE me.evento = :evento - AND (SELECT count(*) - FROM md_evento m2 - WHERE ma.uniqueid = m2.uniqueid - AND m2.evento IN ('COMPLETE_AGENT','ABANDON', 'TIMEOUT_CALLER','TIMEOUT_AGENT') - ) = 0 + 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(); } diff --git a/app/Models/Supervisor.php b/app/Models/Supervisor.php index 8776edb..13e27e2 100644 --- a/app/Models/Supervisor.php +++ b/app/Models/Supervisor.php @@ -62,7 +62,7 @@ class Supervisor extends Model ) AND ma.matricula = ms.matricula - ) < 3 + ) < " . CONF_ATENDIMENTOS_SIMULTANEOS . " ORDER BY ( SELECT count(*) @@ -185,7 +185,7 @@ class Supervisor extends Model LIMIT 1 ) AND ma.matricula = ms.matricula - ) < 3 + ) < " . CONF_ATENDIMENTOS_SIMULTANEOS . " AND ms.fila = :queue AND ms.status = :status"; @@ -242,7 +242,7 @@ class Supervisor extends Model $data['status'] = $status; $data['motivo_pausa'] = $motivo_pausa; $data['matricula'] = $matricula; - + logger('teste')->debug(print_r($data, true), true); return $this->update($this->query, $data); } diff --git a/config/agent.php b/config/agent.php index 65ec3b3..21bd275 100644 --- a/config/agent.php +++ b/config/agent.php @@ -12,4 +12,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'); \ No newline at end of file +define("CONF_AGENT_STATUS_CHAMANDO", 'CHAMANDO'); +define('CONF_ATENDIMENTOS_SIMULTANEOS', 3); \ No newline at end of file diff --git a/websocket/WsInterface.php b/websocket/WsInterface.php index 331d6ea..59daaa3 100644 --- a/websocket/WsInterface.php +++ b/websocket/WsInterface.php @@ -17,4 +17,27 @@ class WsInterface return null; } } + + function enviaActions($msg, $tipo, $matricula, $uniqueid) + { + try { + $mensagem = []; + $mensagem["event"] = [ + "type" => 'actions', + "contact" => [ + "name" => 'Sistema', + "number" => '0' + ], + "mensagem" => [ + "type" => $tipo, + "dst" => $matricula, + "uniqueid" => $uniqueid, + "content" => utf8_encode($msg) + ], + ]; + return $this->enviaMsg(json_encode($mensagem)); + } catch (\Throwable $th) { + logger('monitora')->info($th->getMessage(), debug_backtrace()); + } + } } \ No newline at end of file From e08d6cd220c3c885ac582a92cb19ecaba1b36653 Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Thu, 16 Dec 2021 09:50:34 -0400 Subject: [PATCH 029/144] add parametro de atendimentos simultaneos --- app/Core/Commands.php | 3 +- app/Core/CoreMedia.php | 3 +- app/Middleware/ApiAgente.php | 77 +++++++++++++++++++----------------- app/Models/Atendimento.php | 12 ++++++ app/Models/Supervisor.php | 25 ++---------- config/agent.php | 3 +- 6 files changed, 61 insertions(+), 62 deletions(-) diff --git a/app/Core/Commands.php b/app/Core/Commands.php index 17e330a..657c2af 100644 --- a/app/Core/Commands.php +++ b/app/Core/Commands.php @@ -278,8 +278,9 @@ class Commands } } if ($agente->status == CONF_AGENT_STATUS_OCUPADO) { + $param = $atendimentoModel->getQuantiAtendimentSimultaneos(); $atendimentosAbertos = $this->atendimento->getAtendimentoAbertoByAgente($agente->matricula); - if (count($atendimentosAbertos) < CONF_ATENDIMENTOS_SIMULTANEOS) { + if (count($atendimentosAbertos) < $param->prm_media_simultaneo) { $this->supervisor->updateAgent($agente->matricula, CONF_AGENT_STATUS_LIVRE); } } diff --git a/app/Core/CoreMedia.php b/app/Core/CoreMedia.php index 22fcf3c..fa00df6 100644 --- a/app/Core/CoreMedia.php +++ b/app/Core/CoreMedia.php @@ -230,7 +230,8 @@ class CoreMedia } $atendimentosAbertos = $this->atendimento->getAtendimentoAbertoByAgente($agent[0]->matricula); $sup = new Supervisor(); - if ((count($atendimentosAbertos) + 1) >= CONF_ATENDIMENTOS_SIMULTANEOS) { + $param = $this->atendimento->getQuantiAtendimentSimultaneos(); + if ((count($atendimentosAbertos) + 1) >= $param->prm_media_simultaneo) { $sup->updateAgent($agent[0]->matricula, CONF_AGENT_STATUS_OCUPADO); } diff --git a/app/Middleware/ApiAgente.php b/app/Middleware/ApiAgente.php index c278382..2351cc8 100644 --- a/app/Middleware/ApiAgente.php +++ b/app/Middleware/ApiAgente.php @@ -162,11 +162,11 @@ class ApiAgente function listaAgentesDisponivel() {; try { - + $param = $this->atendimento->getQuantiAtendimentSimultaneos(); $ret = $this->supervisor->listaAgentesDisponivel(); $agentes = []; foreach ($ret as $key => $value) { - if ($value->countAtendimentos < CONF_ATENDIMENTOS_SIMULTANEOS) { + if ($value->countAtendimentos < $param->prm_media_simultaneo) { array_push($agentes, $value); } } @@ -223,7 +223,8 @@ class ApiAgente } if ($agente->status == CONF_AGENT_STATUS_OCUPADO) { $atendimentosAbertos = $this->atendimento->getAtendimentoAbertoByAgente($agente->matricula); - if (count($atendimentosAbertos) < CONF_ATENDIMENTOS_SIMULTANEOS) { + $param = $this->atendimento->getQuantiAtendimentSimultaneos(); + if (count($atendimentosAbertos) < $param->prm_media_simultaneo) { $this->supervisor->updateAgent($agente->matricula, CONF_AGENT_STATUS_LIVRE); } } @@ -300,43 +301,47 @@ class ApiAgente try { $mensagem = $request['event']['mensagem']; $contact = $request['event']['contact']; - // $this->atendimento->findAtenEmAberto(''); - $provider = new Positus(); - $modelMensagem = new Message(); - if ($mensagem['type'] == 'text') { - logger('enviaMsg')->info(print_r($mensagem, true)); - $retuno = $provider->enviarMsg($mensagem['dst'], $mensagem['content']); - } else { - $anmeArquivo = __DIR__ . "/../../storage/files/" . $mensagem['id_provedor']; - $texto = base64_decode($mensagem['content']); - file_put_contents($anmeArquivo, $texto); - $retuno = $provider->enviarMedia( + $status = $this->atendimento->getStatusAtendimento($mensagem['uniqueid']); + if ($status->evento == CONF_EVENT_START) { + $provider = new Positus(); + $modelMensagem = new Message(); + if ($mensagem['type'] == 'text') { + logger('enviaMsg')->info(print_r($mensagem, true)); + $retuno = $provider->enviarMsg($mensagem['dst'], $mensagem['content']); + } else { + $anmeArquivo = __DIR__ . "/../../storage/files/" . $mensagem['id_provedor']; + $texto = base64_decode($mensagem['content']); + file_put_contents($anmeArquivo, $texto); + $retuno = $provider->enviarMedia( + $mensagem['dst'], + 'http://voip.simplesip.com.br:8090/link/' . + $mensagem['id_provedor'] . '/' . + base64_encode($mensagem['mimetype']), + $mensagem['type'], + $mensagem['file_name'] + ); + } + $modelMensagem->addMessage( + $mensagem['uniqueid'], + $contact['number'], $mensagem['dst'], - 'http://voip.simplesip.com.br:8090/link/' . - $mensagem['id_provedor'] . '/' . - base64_encode($mensagem['mimetype']), $mensagem['type'], - $mensagem['file_name'] + $mensagem['type'] == 'text' ? $mensagem['content'] : $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 ? $retuno : null, ); + } else { + $this->retorno('Atendimento já foi finalizado'); } - $modelMensagem->addMessage( - $mensagem['uniqueid'], - $contact['number'], - $mensagem['dst'], - $mensagem['type'], - $mensagem['type'] == 'text' ? $mensagem['content'] : $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 ? $retuno : null, - ); return; } catch (\Throwable $th) { diff --git a/app/Models/Atendimento.php b/app/Models/Atendimento.php index e77e527..f2aad55 100644 --- a/app/Models/Atendimento.php +++ b/app/Models/Atendimento.php @@ -20,6 +20,12 @@ class Atendimento extends Model 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 @@ -120,4 +126,10 @@ class Atendimento extends Model $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(); + } } \ No newline at end of file diff --git a/app/Models/Supervisor.php b/app/Models/Supervisor.php index 13e27e2..d2eb253 100644 --- a/app/Models/Supervisor.php +++ b/app/Models/Supervisor.php @@ -62,27 +62,8 @@ class Supervisor extends Model ) AND ma.matricula = ms.matricula - ) < " . CONF_ATENDIMENTOS_SIMULTANEOS . " - ORDER BY ( - SELECT - count(*) - FROM - md_atendimento ma - INNER JOIN md_evento me ON me.uniqueid = ma.uniqueid - WHERE - me.evento = 'START' - AND 'START' = ( - SELECT - m2.evento - FROM - md_evento m2 - WHERE - ma.uniqueid = m2.uniqueid - ORDER BY id DESC - LIMIT 1 - ) - AND ma.matricula = ms.matricula - ) + ) < (SELECT prm_media_simultaneo FROM pbx_parametros pp LIMIT 1 ) + ORDER BY countAtendimentos "; return $this->read($this->query)->fetchAll(); } @@ -185,7 +166,7 @@ class Supervisor extends Model LIMIT 1 ) AND ma.matricula = ms.matricula - ) < " . CONF_ATENDIMENTOS_SIMULTANEOS . " + ) < (SELECT prm_media_simultaneo FROM pbx_parametros pp LIMIT 1 ) AND ms.fila = :queue AND ms.status = :status"; diff --git a/config/agent.php b/config/agent.php index 21bd275..65ec3b3 100644 --- a/config/agent.php +++ b/config/agent.php @@ -12,5 +12,4 @@ 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'); -define('CONF_ATENDIMENTOS_SIMULTANEOS', 3); \ No newline at end of file +define("CONF_AGENT_STATUS_CHAMANDO", 'CHAMANDO'); \ No newline at end of file From d71a5972898ae52111da3226292ad67cdc8d277e Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Fri, 17 Dec 2021 11:18:31 -0400 Subject: [PATCH 030/144] add mensagem de finalizar e iniciar atendimento --- app/Core/Commands.php | 38 +++++++++++++++++++------ app/Core/CoreMedia.php | 24 ++++++++++------ app/Middleware/ApiAgente.php | 55 +++++++++++++++++++++--------------- 3 files changed, 77 insertions(+), 40 deletions(-) diff --git a/app/Core/Commands.php b/app/Core/Commands.php index 657c2af..fce8b11 100644 --- a/app/Core/Commands.php +++ b/app/Core/Commands.php @@ -6,6 +6,7 @@ use app\Controllers\AgentController; use app\Controllers\BilheteController; use app\Models\Atendimento; use app\Models\Evento; +use app\Models\Message; use app\Models\Supervisor; use websocket\WsInterface; @@ -270,18 +271,30 @@ class Commands if ($ret) { $ws = new WsInterface(); $agente = $supervisorModel->findAgentByMatricula($atendimento->matricula); - $ws->enviaActions('Atendimento finalizado', 'FINISH', $agente->matricula, $atendimento->uniqueid); + $ws->enviaActions('Atendimento finalizado', 'finish', $agente->matricula, $atendimento->uniqueid); + $messegeModel = new Message(); + $nameCliente = $messegeModel->getNameCliente($atendimento->uniqueid, $numero); + $messegeModel->addMessage( + $atendimento->uniqueid, + $numero, + $agente->matricula, + 'finish', + 'Atendimento finalizado', + $nameCliente->profile_name, + $atendimento->context, + 'read' + ); if ($agente->status == CONF_AGENT_STATUS_INDISPONIVEL) { - $atends = $this->atendimento->getAtendimentoAbertoByAgente($atendimento->matricula); + $atends = $atendimentoModel->getAtendimentoAbertoByAgente($atendimento->matricula); if (empty($atends)) { - $this->agentController->enterPause($atendimento->matricula, $agente->motivo_pausa); + $agente->enterPause($atendimento->matricula, $agente->motivo_pausa); } } if ($agente->status == CONF_AGENT_STATUS_OCUPADO) { $param = $atendimentoModel->getQuantiAtendimentSimultaneos(); - $atendimentosAbertos = $this->atendimento->getAtendimentoAbertoByAgente($agente->matricula); + $atendimentosAbertos = $atendimentoModel->getAtendimentoAbertoByAgente($agente->matricula); if (count($atendimentosAbertos) < $param->prm_media_simultaneo) { - $this->supervisor->updateAgent($agente->matricula, CONF_AGENT_STATUS_LIVRE); + $supervisorModel->updateAgent($agente->matricula, CONF_AGENT_STATUS_LIVRE); } } $this->callback = CONF_NAME_REPONSE . " : Atendimento finalizado"; @@ -333,11 +346,20 @@ class Commands } } - private function cancelar($ramal) + private function cancelar($numero) { if ($this->isPermitted($this->personType, $this->permission(__FUNCTION__))) { - $bilheteCancelado = $this->bilheteController->cancelarBilheteForaHorario($ramal); - if ($bilheteCancelado) { + $atendimentoModel = new Atendimento(); + $eventosModel = new Evento(); + $atendimento = $atendimentoModel->getAtendimentoByCliente($numero, CONF_EVENT_ESPERA); + $ret = $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) { $this->callback = CONF_NAME_REPONSE . " : Cancelado o atendimento!"; return true; } else { diff --git a/app/Core/CoreMedia.php b/app/Core/CoreMedia.php index fa00df6..246d621 100644 --- a/app/Core/CoreMedia.php +++ b/app/Core/CoreMedia.php @@ -70,6 +70,7 @@ class CoreMedia $this->api->setHook($this->request); $msg = $this->api->getIsValidMessage(); logger('deburguer')->info(var_export($msg, true)); + $this->ws->enviaMsg('att'); if (empty($msg)) { return; } @@ -197,7 +198,7 @@ class CoreMedia public function criaAtendimento(string $fila, $numero, $uniqueid = null) { if (empty($uniqueid)) { - $uniqueid = $this->atendimento->createAtendimento(null, $numero, 'E', $this->api->getchannel()); + $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); } @@ -238,15 +239,20 @@ class CoreMedia //cria o evento de inicio de atendimento e criar o protocolo $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); $protocol = $this->bilheteController->generateProtocol($uniqueid); + if ($protocol) { + $ws = new WsInterface(); + $agente = $sup->findAgentByMatricula($agent[0]->matricula); + $ws->enviaActions('Atendimento iniciado', 'init', $agente->matricula, $uniqueid); + $this->api->enviarMsg( + $numero, + utf8_encode("Atendimento iniciado!\n") + ); + $this->api->enviarMsg( + $numero, + utf8_encode("Número do protocolo do atendimento é $protocol\n") + ); + } - $this->api->enviarMsg( - $numero, - utf8_encode("Atendimento iniciado!\n") - ); - $this->api->enviarMsg( - $numero, - utf8_encode("Número do protocolo do atendimento é $protocol\n") - ); return; } diff --git a/app/Middleware/ApiAgente.php b/app/Middleware/ApiAgente.php index 2351cc8..f49ec55 100644 --- a/app/Middleware/ApiAgente.php +++ b/app/Middleware/ApiAgente.php @@ -170,9 +170,6 @@ class ApiAgente array_push($agentes, $value); } } - if ($agentes) { - # code... - } $this->retorno( $agentes ? "Sucesso" : "Nenhum agente disponivel", $agentes ? $agentes : null, @@ -214,7 +211,20 @@ class ApiAgente $request['matricula'] ); $ws = new WsInterface(); - $ws->enviaMsg($this->enviaActions('Atendimento finalizado', 'FINISH', $agente->matricula, $request['uniqueid'])); + $ws->enviaMsg($this->enviaActions('Atendimento finalizado', 'finish', $agente->matricula, $request['uniqueid'])); + $provedor = new Positus(); + $provedor->enviarMsg($atendimento->cliente_id, CONF_NAME_REPONSE . " : Atendimento finalizado"); + $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)) { @@ -378,11 +388,6 @@ class ApiAgente } $ret = $this->atendimento->findAtendAgent($request['matricula']); - if ($ret) { - foreach ($ret as $key => $value) { - $value->profile_name = utf8_encode($value->profile_name); - } - } $this->retorno( $ret ? "Sucesso" : "Erro", $ret ? $ret : null, @@ -406,7 +411,7 @@ class ApiAgente foreach ($retunr as $key => $value) { $mensagem = []; $mensagem["event"] = [ - "type" => "mensagem", + "type" => $value->type == 'finish' ? 'action' : "mensagem", "contact" => [ "name" => utf8_encode($value->profile_name), "number" => $value->src, @@ -426,11 +431,11 @@ class ApiAgente ]; array_push($mensagenss, $mensagem); } - $this->retorno( - $mensagenss ? "Sucesso" : "Erro", - $mensagenss ? $mensagenss : null, - $mensagenss ? $mensagenss : null - ); + $data = []; + $data['message'] = utf8_encode("Sucesso"); + $data['status'] = "success"; + $data['data'] = $mensagenss; + echo json_encode($data); return; } catch (\Throwable $th) { $this->retorno($th->getMessage()); @@ -507,11 +512,15 @@ class ApiAgente return; } $ret = $this->agentController->exitPause($request['matricula']); - $this->retorno( - $ret ? "Agente 'livre'" : "Erro", - $ret ? $ret : null, - ); - return null; + if (is_string($ret)) { + $this->retorno($ret); + } else { + $this->retorno( + "Agente 'livre'", + $ret, + ); + } + return; } function transfAtendimento($request) @@ -563,19 +572,19 @@ class ApiAgente } } - function enviaActions($msg, $tipo, $matricula, $uniqueid) + function enviaActions($msg, $tipo, $destino, $uniqueid) { try { $mensagem = []; $mensagem["event"] = [ - "type" => 'actions', + "type" => 'action', "contact" => [ "name" => 'Sistema', "number" => '0' ], "mensagem" => [ "type" => $tipo, - "dst" => $matricula, + "destino" => $destino, "uniqueid" => $uniqueid, "content" => utf8_encode($msg) ], From 3cbe6415be90604606ae55a6b8c541878145a3ca Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Mon, 20 Dec 2021 08:40:42 -0400 Subject: [PATCH 031/144] =?UTF-8?q?corre=C3=A7=C3=A3o=20da=20notifica?= =?UTF-8?q?=C3=A7=C3=A3o=20do=20ws=20e=20remo=C3=A7=C3=A3o=20de=20loggers?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Controllers/AgentController.php | 2 +- app/Controllers/BilheteController.php | 6 +- app/Core/Commands.php | 5 +- app/Core/CoreMedia.php | 120 ++++++++++++++------------ app/Middleware/ApiAgente.php | 6 +- app/Models/Atendimento.php | 15 ++-- app/Models/Message.php | 13 ++- app/Models/Supervisor.php | 2 +- app/Providers/Positus.php | 4 +- app/Providers/RequestURL.php | 4 +- service/MonitoraAgente.php | 4 +- websocket/Servidorsocket.php | 26 +++--- 12 files changed, 114 insertions(+), 93 deletions(-) diff --git a/app/Controllers/AgentController.php b/app/Controllers/AgentController.php index 892c5bc..92fc40d 100644 --- a/app/Controllers/AgentController.php +++ b/app/Controllers/AgentController.php @@ -284,7 +284,7 @@ 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, CONF_AGENT_STATUS_LIVRE); diff --git a/app/Controllers/BilheteController.php b/app/Controllers/BilheteController.php index b7e9a63..5f9e71c 100644 --- a/app/Controllers/BilheteController.php +++ b/app/Controllers/BilheteController.php @@ -142,6 +142,7 @@ class BilheteController extends Controller $useProtoParceiro = $param->prm_use_proto_parceiro; if (!$useProtocolo) { + $this->protocol->rollback(); return '99'; } @@ -149,7 +150,7 @@ class BilheteController extends Controller if ($numProto) { $numProto = $numProto->protocolo; $numProtoParceiro = trim($numProto->protoparceiro); - + $this->protocol->rollback(); if ($useProtoParceiro && $numProtoParceiro) { return $numProtoParceiro; } @@ -168,6 +169,7 @@ class BilheteController extends Controller $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; } @@ -178,4 +180,4 @@ class BilheteController extends Controller } return false; } -} +} \ No newline at end of file diff --git a/app/Core/Commands.php b/app/Core/Commands.php index fce8b11..bc7be5a 100644 --- a/app/Core/Commands.php +++ b/app/Core/Commands.php @@ -271,16 +271,15 @@ class Commands if ($ret) { $ws = new WsInterface(); $agente = $supervisorModel->findAgentByMatricula($atendimento->matricula); - $ws->enviaActions('Atendimento finalizado', 'finish', $agente->matricula, $atendimento->uniqueid); + $ws->enviaMsg($ws->enviaActions('Atendimento finalizado', 'finish', $agente->matricula, $atendimento->uniqueid)); $messegeModel = new Message(); - $nameCliente = $messegeModel->getNameCliente($atendimento->uniqueid, $numero); $messegeModel->addMessage( $atendimento->uniqueid, $numero, $agente->matricula, 'finish', 'Atendimento finalizado', - $nameCliente->profile_name, + $atendimento->nome, $atendimento->context, 'read' ); diff --git a/app/Core/CoreMedia.php b/app/Core/CoreMedia.php index 246d621..32ffe7a 100644 --- a/app/Core/CoreMedia.php +++ b/app/Core/CoreMedia.php @@ -69,7 +69,7 @@ class CoreMedia $this->api = $api; $this->api->setHook($this->request); $msg = $this->api->getIsValidMessage(); - logger('deburguer')->info(var_export($msg, true)); + // logger('deburguer')->info(var_export($msg, true)); $this->ws->enviaMsg('att'); if (empty($msg)) { return; @@ -123,7 +123,7 @@ class CoreMedia $atendiment->matricula, $this->api->getType(), $this->retornaConteudo(), - $this->api->getProfile(), + $this->api->getProfile() ?? '', $this->api->getchannel(), "sended", $this->api->getMimetype(), @@ -197,63 +197,73 @@ class CoreMedia } public function criaAtendimento(string $fila, $numero, $uniqueid = null) { - 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); + 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->api->enviarMsg( + $numero, + utf8_encode('Não temos nenhum atendente disponível no momento, iremos lhe atender assim que um atendente estiver disponível!') + ); + return null; } - } - $agentes = $this->agente->infoAgentes($fila); - if (empty($agentes)) { - $this->api->enviarMsg( - $numero, - utf8_encode('Não temos nenhum atendente disponível no momento, iremos lhe atender assim que um atendente estiver disponível!') - ); - return null; - } - $agent = $this->supervisor->findAgentByQueue($fila, 'LIVRE'); - if (empty($agent)) { - $this->api->enviarMsg( - $numero, - utf8_encode('Nossos atendentes estão ocupados, por favor aguarde que iremos lhe atender!') - ); - $this->mostraPosiscaoNaFila($numero, $fila); - return null; - } - $retServe = $this->atendimento->updAtendimento($uniqueid, $agent[0]->matricula); - if (empty($retServe)) { - $this->api->enviarMsg( - $numero, - utf8_encode('Erro ao gerar atendimento!') - ); - return null; - } - $atendimentosAbertos = $this->atendimento->getAtendimentoAbertoByAgente($agent[0]->matricula); - $sup = new Supervisor(); - $param = $this->atendimento->getQuantiAtendimentSimultaneos(); - if ((count($atendimentosAbertos) + 1) >= $param->prm_media_simultaneo) { - $sup->updateAgent($agent[0]->matricula, CONF_AGENT_STATUS_OCUPADO); - } + $agent = $this->supervisor->findAgentByQueue($fila, 'LIVRE'); + if (empty($agent)) { + $this->api->enviarMsg( + $numero, + utf8_encode('Nossos atendentes estão ocupados, por favor aguarde que iremos lhe atender!') + ); + $this->mostraPosiscaoNaFila($numero, $fila); + return null; + } + $retServe = $this->atendimento->updAtendimento($uniqueid, $agent[0]->matricula); + if (empty($retServe)) { + $this->api->enviarMsg( + $numero, + utf8_encode('Erro ao gerar atendimento!') + ); + return null; + } - //cria o evento de inicio de atendimento e criar o protocolo - $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); - $protocol = $this->bilheteController->generateProtocol($uniqueid); - if ($protocol) { - $ws = new WsInterface(); - $agente = $sup->findAgentByMatricula($agent[0]->matricula); - $ws->enviaActions('Atendimento iniciado', 'init', $agente->matricula, $uniqueid); - $this->api->enviarMsg( - $numero, - utf8_encode("Atendimento iniciado!\n") - ); - $this->api->enviarMsg( - $numero, - utf8_encode("Número do protocolo do atendimento é $protocol\n") - ); - } + $protocol = $this->bilheteController->generateProtocol($uniqueid); + //cria o evento de inicio de atendimento e criar o protocolo + if ($protocol) { + $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)) { + $atendimentosAbertos = $this->atendimento->getAtendimentoAbertoByAgente($agent[0]->matricula); + $sup = new Supervisor(); + $param = $this->atendimento->getQuantiAtendimentSimultaneos(); + $ws = new WsInterface(); - return; + if ((count($atendimentosAbertos)) >= $param->prm_media_simultaneo) { + $sup->updateAgent($agent[0]->matricula, CONF_AGENT_STATUS_OCUPADO); + } + $this->api->enviarMsg( + $numero, + utf8_encode("Atendimento iniciado!\n") + ); + $this->api->enviarMsg( + $numero, + utf8_encode("Número do protocolo do atendimento é $protocol\n") + ); + try { + $ws->enviaMsg($ws->enviaActions('Atendimento iniciado!', 'start', $agent[0]->matricula, $uniqueid)); + } catch (\Throwable $th) { + return $ws->enviaActions('Atendimento iniciado!', 'start', $agent[0]->matricula, $uniqueid); + } + } + } + return; + } catch (\Throwable $th) { + logger('criaAtendimento')->info($th->getMessage()); + return; + } } private function enviaLista($listas, $api, $numero, $nome, $pre = '') diff --git a/app/Middleware/ApiAgente.php b/app/Middleware/ApiAgente.php index f49ec55..4cf3b00 100644 --- a/app/Middleware/ApiAgente.php +++ b/app/Middleware/ApiAgente.php @@ -316,7 +316,7 @@ class ApiAgente $provider = new Positus(); $modelMensagem = new Message(); if ($mensagem['type'] == 'text') { - logger('enviaMsg')->info(print_r($mensagem, true)); + // logger('enviaMsg')->info(print_r($mensagem, true)); $retuno = $provider->enviarMsg($mensagem['dst'], $mensagem['content']); } else { $anmeArquivo = __DIR__ . "/../../storage/files/" . $mensagem['id_provedor']; @@ -411,7 +411,7 @@ class ApiAgente foreach ($retunr as $key => $value) { $mensagem = []; $mensagem["event"] = [ - "type" => $value->type == 'finish' ? 'action' : "mensagem", + "type" => $value->type == 'finish' ? 'actions' : "mensagem", "contact" => [ "name" => utf8_encode($value->profile_name), "number" => $value->src, @@ -577,7 +577,7 @@ class ApiAgente try { $mensagem = []; $mensagem["event"] = [ - "type" => 'action', + "type" => 'actions', "contact" => [ "name" => 'Sistema', "number" => '0' diff --git a/app/Models/Atendimento.php b/app/Models/Atendimento.php index f2aad55..b92b436 100644 --- a/app/Models/Atendimento.php +++ b/app/Models/Atendimento.php @@ -48,7 +48,7 @@ class Atendimento extends Model public function findAtendAgent($matricula) { $this->query = "SELECT ma.*, - (SELECT profile_name FROM md_message mm WHERE uniqueid = ma.uniqueid AND src = ma.cliente_id LIMIT 1) AS profile_name, + ma.nome AS profile_name, (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 @@ -56,7 +56,7 @@ class Atendimento extends Model END AS status FROM {$this->atendimento} ma WHERE ma.matricula = :matricula - ORDER BY status DESC + ORDER BY status DESC, data_reg DESC LIMIT 10"; return $this->read($this->query, ['matricula' => $matricula])->fetchAll(); } @@ -85,11 +85,11 @@ class Atendimento extends Model ON ma.uniqueid = me.uniqueid WHERE me.evento = :evento AND me.fila = :fila - AND ma.matricula IS NULL"; + AND (SELECT m2.evento FROM md_evento m2 WHERE ma.uniqueid = m2.uniqueid ORDER BY id DESC LIMIT 1) = 'EMESPERA' "; return $this->read($this->query, ['evento' => $evento, 'fila' => $fila])->fetchAll(); } - public function createAtendimento($matricula, $cliente_id, $direcao, $context) + public function createAtendimento($matricula, $cliente_id, $direcao, $context, $nome) { $unique = uniqid('', true); $this->query = "INSERT INTO {$this->atendimento} ( @@ -97,19 +97,22 @@ class Atendimento extends Model cliente_id, direcao, uniqueid, - context) + context, + nome) VALUES( :matricula, :cliente_id, :direcao, :uniqueid, - :context);"; + :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) { diff --git a/app/Models/Message.php b/app/Models/Message.php index 16445d3..f4f37ea 100644 --- a/app/Models/Message.php +++ b/app/Models/Message.php @@ -14,7 +14,7 @@ class Message extends Model const MESSAGE = "md_message"; - public function addMessage($uniqueid, $src, $dst, $tipo, $conteudo, $profile_name, $media, $status, $mimetype, $file_name, $id_provedor) + 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, media, status, mimetype, file_name, id_provedor) VALUES(:uniqueid, :src, :dst, :type, :content, :profile_name, :media, :status, :mimetype, :file_name, :id_provedor);"; @@ -23,7 +23,7 @@ class Message extends Model 'src' => $src, 'dst' => $dst, 'type' => $tipo, - 'content' => utf8_decode($conteudo), + 'content' => utf8_decode($content), 'profile_name' => utf8_decode($profile_name), 'media' => $media, 'status' => $status, @@ -58,4 +58,11 @@ class Message extends Model $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(); + } +} \ No newline at end of file diff --git a/app/Models/Supervisor.php b/app/Models/Supervisor.php index d2eb253..0120ae5 100644 --- a/app/Models/Supervisor.php +++ b/app/Models/Supervisor.php @@ -223,7 +223,7 @@ class Supervisor extends Model $data['status'] = $status; $data['motivo_pausa'] = $motivo_pausa; $data['matricula'] = $matricula; - logger('teste')->debug(print_r($data, true), true); + // logger('teste')->debug(print_r($data, true), true); return $this->update($this->query, $data); } diff --git a/app/Providers/Positus.php b/app/Providers/Positus.php index a3ac102..ee2c7c2 100644 --- a/app/Providers/Positus.php +++ b/app/Providers/Positus.php @@ -467,7 +467,7 @@ class Positus implements IApiMedia function response($result) { - logger('deburguer')->info(print_r($result, true)); + // logger('deburguer')->info(print_r($result, true)); if ($result) { try { if (json_decode($result, true) !== null) { @@ -498,4 +498,4 @@ class Positus implements IApiMedia } return null; } -} +} \ No newline at end of file diff --git a/app/Providers/RequestURL.php b/app/Providers/RequestURL.php index 6c0b268..afe3811 100644 --- a/app/Providers/RequestURL.php +++ b/app/Providers/RequestURL.php @@ -70,7 +70,7 @@ class RequestURL private function response($result) { - logger('deburguer')->info(print_r($result, true)); + // logger('deburguer')->info(print_r($result, true)); try { return json_decode($result, true); } catch (\Throwable $th) { @@ -96,4 +96,4 @@ class RequestURL curl_setopt($this->curl, CURLOPT_CONNECTTIMEOUT, self::CONF_TIMEOUT); curl_setopt($this->curl, CURLOPT_TIMEOUT, self::CONF_TIMEOUT); } -} +} \ No newline at end of file diff --git a/service/MonitoraAgente.php b/service/MonitoraAgente.php index 88f5661..d02b9d7 100644 --- a/service/MonitoraAgente.php +++ b/service/MonitoraAgente.php @@ -52,17 +52,15 @@ class MonitoraAgente ]; } - function monitora() { try { $this->coremedia->setApi(new Positus()); $agentesLista = $this->supervisor->listaAgentesDisponivel(); foreach ($agentesLista as $item) { - logger('bug')->info(var_export($item, true), debug_backtrace()); $atendimentos = $this->atendimento->getAtendFila($item->fila); if (count($atendimentos) > 0) { - $this->coremedia->criaAtendimento( + return $this->coremedia->criaAtendimento( $atendimentos[0]->fila, $atendimentos[0]->cliente_id, $atendimentos[0]->uniqueid diff --git a/websocket/Servidorsocket.php b/websocket/Servidorsocket.php index 1ae1057..6a69f51 100644 --- a/websocket/Servidorsocket.php +++ b/websocket/Servidorsocket.php @@ -16,12 +16,12 @@ class Servidorsocket implements MessageComponentInterface public array $conectado = []; public array $idConexion = []; public array $clientes = []; - public $provider; + public $monitora; public function __construct() { $this->clients = new SplObjectStorage(); - $this->provider = new Positus(); + $this->monitora = new MonitoraAgente(); } public function onOpen(ConnectionInterface $conn) @@ -45,19 +45,21 @@ class Servidorsocket implements MessageComponentInterface public function onMessage(ConnectionInterface $from, $msg) { - $mensagem = json_decode($msg); - if ($mensagem->msg) { - $this->provider->enviarMsg($mensagem->para, $mensagem->msg); - } else { - try { - $monitora = new MonitoraAgente(); - $monitora->monitora(); + try { + $retMoni = $this->monitora->monitora(); + logger('debuge')->debug($retMoni); + if ($retMoni) { + foreach ($this->clientes as $key => $value) { + $value['conection']->send($retMoni); + } + } + if (trim($msg) != 'att') { foreach ($this->clientes as $key => $value) { $value['conection']->send($msg); } - } catch (\Throwable $th) { - $from->send(mb_convert_encoding('deu erro boy', "UTF-8")); } + } catch (\Throwable $th) { + logger('debuge')->debug($th->getMessage()); } } @@ -73,4 +75,4 @@ class Servidorsocket implements MessageComponentInterface { $conn->close(); } -} +} \ No newline at end of file From fb15e986c2e53ecc2144eff49705493547cf48c4 Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Mon, 20 Dec 2021 10:12:39 -0400 Subject: [PATCH 032/144] =?UTF-8?q?add=20notica=C3=A7=C3=A3o=20de=20transf?= =?UTF-8?q?erencia?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Controllers/AgentController.php | 19 +++++++++++++++++++ app/Middleware/ApiAgente.php | 12 ++++++++---- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/app/Controllers/AgentController.php b/app/Controllers/AgentController.php index 92fc40d..855656d 100644 --- a/app/Controllers/AgentController.php +++ b/app/Controllers/AgentController.php @@ -12,8 +12,11 @@ use app\Models\EventQueue; use app\Controllers\ClassificacaoController; use app\Models\Atendimento; use app\Models\Evento; +use app\Models\Message; use app\Models\Supervisor; +use app\Providers\Positus; use Exception; +use websocket\WsInterface; /** * Description of AgentController @@ -336,6 +339,22 @@ class AgentController extends Controller $atendAtual->fila, $agentTransf->matricula ); + $ws = new WsInterface(); + $ws->enviaMsg($ws->enviaActions('Atendimento transferido', 'transfer', $agentTransf->matricula, $uniqueid)); + $ws->enviaMsg($ws->enviaActions('Atendimento transferido', 'transfer', $agent->matricula, $uniqueid)); + $provedor = new Positus(); + $provedor->enviarMsg($atendAtual->cliente_id, CONF_NAME_REPONSE . ": Atendimento finalizado"); + $messegeModel = new Message(); + $messegeModel->addMessage( + $uniqueid, + $agent->matricula, + $agentTransf->matricula, + 'transfer', + 'Atendimento transferido', + $agent->nome, + $atendAtual->context, + 'read' + ); $this->agent->commit(); return true; } catch (Exception $ex) { diff --git a/app/Middleware/ApiAgente.php b/app/Middleware/ApiAgente.php index 4cf3b00..5068c60 100644 --- a/app/Middleware/ApiAgente.php +++ b/app/Middleware/ApiAgente.php @@ -530,10 +530,14 @@ class ApiAgente $request['matricula_destino'], $request['uniqueid'] ); - $this->retorno( - $retunr ? "Sucesso" : "Erro", - $retunr ? $retunr : null, - ); + if (is_string($retunr)) { + $this->retorno($retunr); + } else { + $this->retorno( + $retunr ? "Sucesso" : "Erro", + $retunr ? $retunr : null, + ); + } return null; } function markMessegeRead($request) From b2229a4e8ed2e2173dacf6281fc7f39307a04ca6 Mon Sep 17 00:00:00 2001 From: Simples IP Desenvolvimento Date: Mon, 20 Dec 2021 15:53:36 -0400 Subject: [PATCH 033/144] add files and configs. Create new methods to simpleschat --- public/css/styles.css | 31 +- public/images/favicon.ico | Bin 32988 -> 438 bytes public/images/icons/csv-file.png | Bin 0 -> 12605 bytes public/images/icons/doc-file.png | Bin 0 -> 9930 bytes public/images/icons/notfound-file.png | Bin 0 -> 12799 bytes public/images/icons/pdf-file.png | Bin 0 -> 8217 bytes public/images/icons/ppt-file.png | Bin 0 -> 8343 bytes public/images/icons/txt-file.png | Bin 0 -> 9495 bytes public/images/icons/xls-file.png | Bin 0 -> 11617 bytes public/images/icons/zip-file.png | Bin 0 -> 8533 bytes public/images/loading.gif | Bin 0 -> 36058 bytes public/images/play.svg | 2 + public/index.html | 37 +- public/js/config.js | 7 + public/js/cronometro.js | 2 +- public/js/main.js | 564 +++++++++++++++----------- public/js/requests.js | 181 +++++++++ public/js/util.js | 496 ++++++++++++++++++++++ 18 files changed, 1059 insertions(+), 261 deletions(-) create mode 100644 public/images/icons/csv-file.png create mode 100644 public/images/icons/doc-file.png create mode 100644 public/images/icons/notfound-file.png create mode 100644 public/images/icons/pdf-file.png create mode 100644 public/images/icons/ppt-file.png create mode 100644 public/images/icons/txt-file.png create mode 100644 public/images/icons/xls-file.png create mode 100644 public/images/icons/zip-file.png create mode 100644 public/images/loading.gif create mode 100644 public/images/play.svg create mode 100644 public/js/config.js create mode 100644 public/js/requests.js create mode 100644 public/js/util.js diff --git a/public/css/styles.css b/public/css/styles.css index 451100b..bbcddc2 100644 --- a/public/css/styles.css +++ b/public/css/styles.css @@ -51,6 +51,7 @@ a { --chat-active-color: #ebebeb; --chat-window-beige: #e5ddd5; --sender-message-green: #b8ffd5; + --events-message: #f3f3f3; --type-message-bar: #f0f0f0; --backgroup-icons-upload: #E9E9E9; --background-color-message-receive: #dbf2ff; @@ -239,7 +240,7 @@ a { /* Chats */ .chats { - height: 63rem; + height: 80rem; overflow-y: scroll; overflow-x: hidden; } @@ -516,13 +517,19 @@ a { } .sender, -.receiver { +.receiver, +.events { border-radius: 0.5rem; padding: 1.2rem 1rem; margin-bottom: 0.3rem; position: relative; } +.events { + background: var(--events-message); + align-self: center; +} + .sender { background: var(--sender-message-green); align-self: flex-end; @@ -537,6 +544,14 @@ a { .receiver-message { font-size: 1.4rem; margin-right: 3rem; + word-break: break-all; +} + +.message-column { + display: flex; + flex: 1; + flex-direction: row; + align-items: center; } .message-time, @@ -972,6 +987,10 @@ input[type="range"] { font-size: 10px; } +.fz-12 { + font-size: 12px; +} + .fz-14 { font-size: 14px; } @@ -995,6 +1014,14 @@ input[type="range"] { color: rgb(241, 165, 65); } +.status-disabled { + color: #D8D8D8 +} + +.opacity-3 { + opacity: 0.3 +} + .cursor-pointer { cursor: pointer; } \ No newline at end of file diff --git a/public/images/favicon.ico b/public/images/favicon.ico index 6ee060e1e548d32c93f4bb3c99eeb9ff3370fa01..cd8751f817f5d802807edf45a6b13c1ff4fb6db7 100644 GIT binary patch literal 438 zcmV;n0ZIOeP)l2Nz?J zEFHwbO_7UPf?#+`Zfct59+wav>MNbGoT)1z^U573*Q@~r4HwY!b1INI3 zU@eSok_qTkO+|znFqtv*444mNyK@WRBJ&JnFZbwm5x^hS$<4fkTrliV^xUFt+eLX1 zuGqtySO4MSSyff_-mCjiQr}H9E$_YdwpZ0v%@#l|Wqtt6Wj$7Z_Y3{T8X`je)e;C5 z7nwqk>KRW1Z>bPMzBo+c0Sdrm8qiWW6_~!upD!;Ay#xC7&j5c=k`W+`EuE@4AOp~= zEuz0J(4>i8wov8g#EQKd0XkI!!o#ctgXTVT+oM{9PXGaciwwXr@Ekv&xAAV2_4np` z7|TPiljeN`HUY#%=83{r`O5}Ux&l5b#1pagF#Jd^GJ(QVQOdw$prkM*LI8Au6QBXC ghp}z_!=V4DYhSjU3u91_bpQYW07*qoM6N<$f@~DSz5oCK literal 32988 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 diff --git a/public/images/icons/csv-file.png b/public/images/icons/csv-file.png new file mode 100644 index 0000000000000000000000000000000000000000..d483883ab799021785a5432bb8c5d51187a320ad GIT binary patch literal 12605 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 literal 0 HcmV?d00001 diff --git a/public/images/icons/doc-file.png b/public/images/icons/doc-file.png new file mode 100644 index 0000000000000000000000000000000000000000..c10d2123eeae6629e471e8f278b92dcb54a68274 GIT binary patch literal 9930 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⁡Awl<` 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 literal 0 HcmV?d00001 diff --git a/public/images/icons/notfound-file.png b/public/images/icons/notfound-file.png new file mode 100644 index 0000000000000000000000000000000000000000..e07f515b684ab0c4395a9c6e2e4bba4205db9d14 GIT binary patch literal 12799 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 literal 0 HcmV?d00001 diff --git a/public/images/icons/txt-file.png b/public/images/icons/txt-file.png new file mode 100644 index 0000000000000000000000000000000000000000..91da413029c2afc6ee49d5e1a3f852257d4d0152 GIT binary patch literal 9495 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 JinQ$>{vWD-#T5Vm literal 0 HcmV?d00001 diff --git a/public/images/icons/xls-file.png b/public/images/icons/xls-file.png new file mode 100644 index 0000000000000000000000000000000000000000..e9528611358b38d770020b03abd9949baf5a7417 GIT binary patch literal 11617 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 L*l~$QU?Bem`Nj3* literal 0 HcmV?d00001 diff --git a/public/images/play.svg b/public/images/play.svg new file mode 100644 index 0000000..27abb1f --- /dev/null +++ b/public/images/play.svg @@ -0,0 +1,2 @@ + + diff --git a/public/index.html b/public/index.html index 9eb8a0d..7bf4f59 100644 --- a/public/index.html +++ b/public/index.html @@ -4,7 +4,7 @@ - Whatsapp + Multi Channel | Simples Chat @@ -21,12 +21,16 @@ @@ -49,7 +53,6 @@ --> -
@@ -64,12 +67,12 @@
-
+
- Transf. + Transferir
-
+
Finalizar
@@ -78,7 +81,7 @@

Bem-vindo

-

Seu canal de atendimento de mensagens!

+

Seu canal de atendimento por mensagens!

@@ -86,7 +89,7 @@ -
+ + + diff --git a/public/js/config.js b/public/js/config.js new file mode 100644 index 0000000..4d1ff03 --- /dev/null +++ b/public/js/config.js @@ -0,0 +1,7 @@ +const ws = 'ws://192.168.115.65:8090/ws' +const server_api = '192.168.115.65:8081' +const URLApi = `http://${server_api}` + +let mediaRecorder +localStorage.setItem('my_uniqueid', 1020) +const icontypes = ['csv', 'doc', 'pdf', 'txt', 'xls', 'zip', 'ppt'] diff --git a/public/js/cronometro.js b/public/js/cronometro.js index 82a69fa..581a52c 100644 --- a/public/js/cronometro.js +++ b/public/js/cronometro.js @@ -24,7 +24,7 @@ function start() { pause(); cron = setInterval(() => { timer(); - }, 990); + }, 1005); } function pause() { diff --git a/public/js/main.js b/public/js/main.js index e9ffc89..60115c0 100644 --- a/public/js/main.js +++ b/public/js/main.js @@ -1,117 +1,37 @@ -const ws = new WebSocket('ws://192.168.115.65:8090/ws'); -const server_api = '192.168.115.65:8081' - -let mediaRecorder - -ws.onopen = function (error) { - $("#status_agent").addClass("status-connect").text('CONECTADO'); -}; - -ws.onerror = function (error) { - $("#status_agent").addClass("status-desconnect").text('DESCONECTADO'); -}; - -/** ROLAGEM DO SCROLL ATÉ NO FINAL DO CHAT */ -const scrollDown = () => { - $(".chat-window").animate({scrollTop: 9999 }, 1); -} - -const modalStart = () => { - $('#footer-content-left').empty() - $('#footer-content-right').empty() - $('.modal-content-body').empty() - $('.modal-header-title').empty() -} - -const startChannelMessage = () => { - $('#typemessagebar').hide() - $('#headerbuttonsagent').hide() - $("#headermediaagent").hide() - $('#chatwindowagent').css({ overflow: 'hidden' }) - $('#uploadfiles').hide() -} - -const startNotification = () => { - $('#typemessagebar').show(); - $('#headerbuttonsagent').show() - $("#headermediaagent").show() - $('#welcometomessage').hide() - $('#chatwindowagent').css({ overflow: 'scroll' }) -} - -const startSendImage = () => { - modalStart() - $("#uploadimage").on('change', function(){ - const file = new FileReader(); - file.readAsDataURL(this.files[0]); - const imgName = this.files[0].name - - file.onload = function(e) { - $('#myImg').remove() - $('#footername').remove() - $('#footersend').remove() - - $('.modal-content-body').append(``) - $('#footer-content-left').append(``) - $('#footer-content-right').append(`
`) - } - $('#modalselect').show() - }) -} - -function recorderVoice () { - $('#modalselect').show() - modalStart() - - $('.modal-content-body').append(` - `) - - $('#footer-content-right').append(``) - - navigator.mediaDevices.getUserMedia({audio: true}).then( - stream => { - mediaRecorder = new MediaRecorder(stream) - let chunks = [] - - mediaRecorder.ondataavailable = data => { - chunks.push(data.data) - } - - mediaRecorder.onstop = () => { - const blob = new Blob(chunks, {type : 'audio/ogg; code=opus'}) - const reader = new window.FileReader() - reader.readAsDataURL(blob) - reader.onloadend = () => { - const audio = document.createElement('audio') - audio.src = reader.result - audio.controls = true - $('#footer-content-left').append(audio) - } - } - mediaRecorder.start() - }, err => { - // add msg de error - } - ) -} - +// var wserror +// /** SOCKET CONNECT */ +// ws.onopen = function (error) { +// $("#status_agent").addClass("status-connect").text('CONECTADO'); +// if(wserror == 'teste'){ +// location.reload(); +// } +// }; + +// ws.onclose = function(e) { +// console.log('socket closed try again'); +// } + +// /** SOCKET ERROR */ +// ws.onerror = function (error) { +// $("#status_agent").addClass("status-desconnect").text('DESCONECTADO'); +// wserror = 'teste' +// }; + + +/** + * EVENTOS GERADOS PELO USUÁRIO DA APLICAÇÃO + */ $(function(){ - localStorage.setItem('my_uniqueid', 1040) - + connect(ws) + notifications() /** * VOICE RECORDER */ - $('.modal-content-body').on('click', '#stoprecorder',() => { + $('.modal-content-body').on('click', '#stoprecorder', () => { $('#msgRecorder').text('Paramos de gravar sua voz!') mediaRecorder.stop() }) + $('#voicerecorder').on('click', () => { recorderVoice() }) @@ -122,10 +42,12 @@ $(function(){ $('#uploadfiles').fadeOut('slow') }); - $("#myBtn").on('click', () => modalStart()) - - /** ENVIO DE IMAGEM */ + /** INICIO DAS FUNCIONALIDADES */ startSendImage() + startSendFile() + startPause() + startTransfer() + startFinalizar() /** INICIA COM O HEADER DO CONTATO VAZIO */ startChannelMessage() @@ -142,187 +64,329 @@ $(function(){ } }) - $('.type-message-bar-right').on('click',function(){ + $('.type-message-bar-right').on('click',() => { sendMessage() }) $('#imgclip').on('click', function(){ + modalStart() + $("#uploadimage").val('') + $('#uploadfile').val('') if($('#uploadfiles').is(':hidden')){ $('#uploadfiles').fadeIn('slow') } else { $('#uploadfiles').fadeOut('slow') } }) + + $('#footer-content-right').on('click', '#footersend', () => { + sendMedia() + $('#modalselect').css({display: 'none'}) + }) + supervisorAgente() }) +/** + * EVENTOS DE CLICK PARA SELECIONAR A SESSÃO DE MENSAGEM/CONVERSA E RECUPERAR AS MENSAGENS JÁ TROCADAS NO ATENDIMENTO + * @param {*} id + */ const selectNotification = (id) => { - const dataStorage = JSON.parse(localStorage.getItem('keep_msg')) - const dataContact = dataStorage.data.filter(e => { - if(id.trim() == e.event?.contact.number || (e.de == localStorage.getItem('my_uniqueid') && e.para == id.trim())){ - return true + marcarMensagemVista(id) + + listaMensagem(id).then(() => { + let uniqueid + let number + let name + let dataContact + + const dataRequest = JSON.parse(localStorage.getItem('obj_contact')) + listarAtendimentoAgente(localStorage.getItem('my_uniqueid')) + const allNotifications = JSON.parse(localStorage.getItem('obj_notification')) + + hideButtons(false) + allNotifications.data.forEach(e => { + if(e.uniqueid == id && e.status == 0){ + hideButtons(true) + } + }) + + if(dataRequest.data.length > 0){ + dataContact = dataRequest.data.filter(e => { + if(id.trim() == e.event?.mensagem.uniqueid){ + return true + } + }) } - }) - if(dataContact){ + allNotifications.data.forEach(e => { + if(id === e.uniqueid){ + uniqueid = e.uniqueid + name = e.nome + number = e.cliente_id + } + }) + localStorage.removeItem('session_window') - localStorage.setItem('session_window', dataContact[0].event.contact.number) + localStorage.removeItem('session_uniqueid') + localStorage.setItem('session_uniqueid', uniqueid) + localStorage.setItem('session_window', number) - $('.chat-window-contact-name').text(dataContact[0].event.contact.name) + $('.chat-window-contact-name').text(name) $('.chat-window-contact-status').text('WhatsApp') /** REMOVE AS MSG NA E CONSTRIO A TELA NOVAMENTE (EVITAR DUPLICAR) */ $('.chat-window .sender').remove() $('.chat-window .receiver').remove() - + $('.chat-window .events').remove() + alertNotification(localStorage.getItem('session_uniqueid'),'remove') + dataContact.forEach(e => { - console.log(e) - const datesend = e.event?.mensagem.datetime ? converdata(e.event?.mensagem.datetime, true) : 'algumas horas'; + const datesend = e.event?.mensagem.datetime ? converdata(new Date(e.event?.mensagem.datetime).getTime()) : 'algumas horas'; + let typesend = localStorage.getItem('my_uniqueid') == e.event.contact.number ? 'sender': 'receiver' + if(e.event?.mensagem.type == 'text'){ - $('.chat-window').append(`
${e.event.mensagem.content}${datesend}
`) + $('.chat-window').append(` +
+ ${e.event.mensagem.content} +
+ ${datesend} +
` + ) + } + + if(e.event?.mensagem.type == 'finish'){ + $('.chat-window').append(` +
+ Atendimento finalizado +
` + ) + } + + if(e.event?.mensagem.type != 'text'){ + const sendobj = { + filename: e.event?.mensagem.file_name, + id_provedor: e.event?.mensagem.id_provedor, + type: e.event?.mensagem.type, + mimetype: e.event?.mensagem.mimetype, + from: typesend + } + messageTypeMedia(sendobj) } if(e.de == localStorage.getItem('my_uniqueid')){ const datereceived = e.datetime ? converdata(e.datetime) : 'algumas horas'; - $('.chat-window').append(`
${e.msg}${datereceived}
`) + if(e.type == 'text'){ + $('.chat-window').append(` +
+ ${e.msg} +
+ ${datereceived} +
`) + } else if (e.type == 'audio' || e.type == 'voice'){ + const audio = `` + $('.chat-window').append(` +
+ ${audio} +
+ ${datereceived} +
`) + } else if (e.type == 'document'){ + icontypes.forEach(l => { + if(e.filename.indexOf(l) >= 0){ + $('.chat-window').append(` +
+ + + + + + ${e.filename} +
+ ${datereceived} +
`) + } + }) + } else if (e.type == 'image'){ + const fileimg = `data:${e.mimetype};base64,` + e.msg + $('.chat-window').append(` +
+ +
+ ${datereceived} +
`) + } } }) - - startNotification() - } + scrollDown() + }) + startNotification() } -const sendMessage = () => { +/** + * REALIZA O ENVIO DE MENSAGEM ATRAVEZ DA API + * @param {*} obj + * @returns + */ +const sendMessage = (obj = {}) => { const sendNumber = localStorage.getItem('session_window') const myUniqueid = localStorage.getItem('my_uniqueid') - let sendText = $('#fieldsendmessage').val() - if(!sendText){ - return - } + let sendContent = (typeof obj.fileContent === "undefined") ? $('#fieldsendmessage').val() : obj.content + let name = obj.name ? obj.name : 'DESCONHECIDO' + let uniqueid = localStorage.getItem('session_uniqueid') + let media = obj.media ? obj.media : 'whatsapp' + let type = obj.type ? obj.type : 'text' + let mimetype = obj.mimetype ? obj.mimetype : 'text' + let filename = obj.filename ? obj.filename : Date.now() - let dataStorage = JSON.parse(localStorage.getItem('keep_msg')) - if (!dataStorage) { - dataStorage = { - data: [] - } + if(!sendContent){ + return } - dataStorage.data.push({ para: sendNumber, de: myUniqueid, msg: sendText, type: 'text', datetime: Date.now() }) - /** REMOVE TODOS OS ITENS ANTES PARA NÃO DUPLICAR */ - localStorage.removeItem('keep_msg'); - /** ADD LOCALSTORAGE */ - localStorage.setItem('keep_msg', JSON.stringify(dataStorage)) - const dataSend = { + let dataSend = { "event": { "type": "mensagem", "contact": { - "name": "Lucas Awade", - "number": sendNumber, + "name": name, + "number": myUniqueid, "matricula": myUniqueid }, "mensagem": { + "uniqueid": uniqueid, "dst": sendNumber, - "idAtendimento": "43524352345", - "type": "text", - "media": "whatsapp", - "content": sendText, + "id_provedor": `${uniqueid}_${Date.now()}`, + "type": type, + "mimetype": mimetype, + "media": media, + "content": sendContent, "status": "sended" } } } - $.ajax({ - url: `http://${server_api}/api/enviaMsg`, - type: "POST", - data: JSON.stringify(dataSend), - success: function(res){ - console.log(res) - }, - error: function(res){ - $('.chat-window').append(`
MENSAGEM NÃO FOI ENVIADA!
`) - } - }); + if(type != 'audio'){ + dataSend.event.mensagem.file_name = filename + } + + enviarMensagem(dataSend) - $('.chat-window').append(`
${sendText}${converdata(Date.now())}
`) + let msgContent = type == 'text' ? sendContent : obj.fileContent + $('.chat-window').append(` +
+ ${msgContent} +
+ ${converdata(Date.now())} +
`) scrollDown() /** LIMPA O CAMPO DE ENVIO DE MENSAGEM */ $('#fieldsendmessage').val("") } +/** + * ATUALIZA AS MENSAGEM QUE SÃO RECEBIDAS NA TELA DO ATENDIMENTO + * @param {*} ev + */ const viewMessage = (ev) => { - const sessionOpen = localStorage.getItem('session_window') + const sessionOpen = localStorage.getItem('session_uniqueid') const datesend = ev.event?.mensagem.datetime ? converdata(ev.event?.mensagem.datetime, true) : 'algumas horas'; - if(ev.event?.contact.number == sessionOpen && ev.event?.mensagem.type == 'text'){ - $('.chat-window').append(`
${ev.event.mensagem.content}${datesend}
`) - } - scrollDown() -} -const receiveNotification = (data) => { - let contact = JSON.parse(localStorage.getItem('obj_contact')) - if (!contact) { - contact = { - data: [] + if(ev.event?.mensagem.uniqueid == sessionOpen){ + marcarMensagemVista(sessionOpen) + switch(ev.event?.mensagem.type){ + case 'text': + $('.chat-window').append(` +
+ ${ev.event.mensagem.content} +
+ ${datesend} +
`) + break + case 'finish': + $('.chat-window').append(` +
+ Atendimento finalizado +
`) + break } } - /** VERIFICA SE JÁ EXIST O NUMERO ENVIADO */ - let validate = contact.data.filter(e => { - if (e.numero == data.event?.contact.number) { - return true - } - }); - - /** VALIDA O NUMERO, VERIFICA SE O TEM ALGMA MSG INICIAL, SE JÁ TEVE UM NUMERO NA VERIFICACAO */ - if (data.event?.contact.number && (contact?.data.length == 0 || validate.length == 0)) { - const datesend = converdata(data.event?.mensagem.datetime) - const chatList = - `
-
- -
-
-
- ${data.event?.contact.name} - ${datesend} -
-
-
- WhatsApp -
-
- 1 - -
-
-
-
` - - /** CRIA O OBJETO PARA ADD NO LOCALSTORAGE */ - let objt = { - numero: data.event?.contact.number, - chatHTML: chatList + const mediaDownload = ["image", "voice", "document", "audio", "video", "sticker"] + if(mediaDownload.indexOf(ev.event?.mensagem.type) >= 0){ + const sendobj = { + filename: ev.event?.mensagem.file_name, + id_provedor: ev.event?.mensagem.id_provedor, + type: ev.event?.mensagem.type, + mimetype: ev.event?.mensagem.mimetype, + from: 'receiver' } - /** ADD OBJETO NO ARRAY DO LOCALSTORAGE */ - contact.data.push(objt) - /** REMOVE TODOS OS ITENS ANTES PARA NÃO DUPLICAR */ - localStorage.removeItem('obj_contact'); - /** ADD LOCALSTORAGE */ - localStorage.setItem('obj_contact', JSON.stringify(contact)) + messageTypeMedia(sendobj) } + scrollDown() +} - /** REMOVE OS ITENS DO HTML E ADD NOVAMENTE PARA N DUPLICAR AS NOTIFICATIONS */ - contact.data.forEach(e => { - $('#' + e.numero).remove() - $('#chats').append(e.chatHTML) - }); +/** + * APRESENTA AS NOVAS NOTIFICACOES DE ATENDIMENTO NA TELA DO ATENDENTE + * -> CADA ATENDIMENTO DEVE POSSUIR APENAS UMA NOTIFICACAO + * @param {*} data + */ +const receiveNotification = (data) => { + let validate = null + + switch(data.event?.type){ + case "mensagem": + /** VALIDA O NUMERO, VERIFICA SE O TEM ALGMA MSG INICIAL, SE JÁ TEVE UM NUMERO NA VERIFICACAO */ + listarAtendimentoAgente(localStorage.getItem('my_uniqueid')) + validate = JSON.parse(localStorage.getItem('obj_notification')) + + const vald = validate.data.filter((e) => { + return data.event?.mensagem.uniqueid == e.uniqueid + }) + + if(data.event?.contact.number != localStorage.getItem('session_window')){ + alertNotification(data.event?.mensagem.uniqueid) + } + + if (data.event?.mensagem.uniqueid && data.event?.contact.number && vald.length == 0) { + notifications( + { + uniqueid: data.event?.mensagem.uniqueid, + cliente_id: data.event?.contact.number, + context: data.event?.mensagem.media, + profile_name: data.event?.contact.name, + data_reg: data.event?.mensagem.datetime, + status: 1, + action: 'mensagem', + } + ) + } + break + case "actions": + let obj + switch(data.event.mensagem.type){ + case 'start': + obj = {} + break + case 'finish': + obj = { + uniqueid: data.event?.mensagem.uniqueid, + action: 'finish' + } + break + } + console.log(obj) + notifications(obj) + break + } } + +/** + * MANTEM TODAS AS MENSAGENS ARMAZENADAS NO LOCALSTORAGE, SEMPRE QUE ENVIA E RECEBE SERÁ GUARDADO + * @param {*} ev + */ const keepMensage = (ev) => { let msg = JSON.parse(localStorage.getItem('keep_msg')) if(!msg){ @@ -336,23 +400,41 @@ const keepMensage = (ev) => { } } -ws.addEventListener("open", () => { - // localStorage.removeItem('obj_contact'); - // return; - ws.addEventListener("message", e => { - const data = JSON.parse(e?.data) - console.log(data) - if(data){ - keepMensage(data) - } - /** RECEBE AS PRIMEIRAS MENSAGENS */ - receiveNotification(data) +/** + * FUNÇÃO PARA CAPTURAR O ARQUIVO A SER ENVIADO + */ + const sendMedia = () => { + let rec + let filename + if($("#footer-content-left audio").length){ + let el = $("#footer-content-left audio")[0].src + fileContent = $("#footer-content-left audio")[0].outerHTML + sendMessage({ content: el.replace("data:", "").replace(/^.+,/, ""), type: 'audio', mimetype: 'audio/mpeg', fileContent }) + return + } else if($("#uploadfile")[0].files[0]) { + let el = $("#uploadfile")[0].files[0] + rec = new Blob([el], { type : el.type }) + let filesent = $("#myImg") + filesent[0].id = Date.now() + imgContent = filesent.css({'max-width': '60px'})[0].outerHTML + fileContent = `${imgContent}` + filename = el.name + } else { + rec = $("#uploadimage")[0].files[0] + let filesent = $("#myImg") + filesent[0].id = Date.now() + fileContent = filesent.css({'max-width': '350px'})[0].outerHTML + $("#myImg").empty() + } - if($("#welcometomessage").is(':hidden') == false){ - localStorage.setItem('session_window', null) - } + const file = new FileReader(); + file.onload = function() { + const typefile = rec.type.split("/")[0].indexOf('image') >= 0 ? rec.type.split("/")[0] : 'document' + sendMessage({ content: file.result.replace("data:", "").replace(/^.+,/, ""), type: typefile, mimetype: rec.type, fileContent, filename }) + } + file.readAsDataURL(rec); +} - /** ATUALIZACAO DA SESSAO CORRENTE */ - viewMessage(data) - }) -}) \ No newline at end of file +/** + * EVENTO DE INTERAÇÃO COM O WEBSOCKET PARA RECURAR TODOS OS EVENTOS TRANSMITIDOS + */ diff --git a/public/js/requests.js b/public/js/requests.js new file mode 100644 index 0000000..27d1ea9 --- /dev/null +++ b/public/js/requests.js @@ -0,0 +1,181 @@ +const enviarMensagem = (dataSend) => { + $.ajax({ + url: `http://${server_api}/api/enviarMensagem`, + type: "POST", + data: JSON.stringify(dataSend), + success: function(res){ + //console.log(res) + }, + error: function(res){ + $('.chat-window').append(`
MENSAGEM NÃO FOI ENVIADA!
`) + } + }); +} + +const listaMensagem = (uniqueid) => new Promise((resolve) => { + $.ajax({ + url: `http://${server_api}/api/listarMensagem`, + type: "POST", + data: JSON.stringify({uniqueid}), + success: function(res){ + localStorage.removeItem('obj_contact') + localStorage.setItem('obj_contact', JSON.stringify(res)) + resolve() + }, + error: function(res){ + alert('Nao foi possivel carregar as listas de mensagens.') + } + }) +}) + +const listarAgentesDisponivel = () => new Promise((resolve) => { + $.ajax({ + url: `http://${server_api}/api/listarAgentesDisponivel`, + type: "GET", + success: function(res){ + localStorage.removeItem('obj_agentes_disponivel') + localStorage.setItem('obj_agentes_disponivel', JSON.stringify(res)) + resolve(res) + }, + error: function(res){ + alert('Nao foi possivel carregar as listas de agentes.') + } + }); +}) + +const listarAtendimentoAgente = (matricula) => new Promise((resolve) => { + $.ajax({ + url: `http://${server_api}/api/listarAtendimentoAgente`, + type: "POST", + data: JSON.stringify({matricula}), + success: function(res){ + localStorage.removeItem('obj_notification') + localStorage.setItem('obj_notification', JSON.stringify(res)) + resolve(res) + }, + error: function(res){ + alert('Nao foi possivel carregar as listas de atendimento.') + } + }); +}) + +const listarPausasAgente = (matricula) => new Promise((resolve) => { + $.ajax({ + url: `http://${server_api}/api/listarPausasAgente`, + type: "POST", + data: JSON.stringify({matricula}), + success: function(res){ + localStorage.removeItem('obj_pauses') + localStorage.setItem('obj_pauses', JSON.stringify(res)) + resolve(res) + }, + error: function(res){ + alert('Nao foi possivel carregar as listas de pausa.') + } + }); +}) + +const entrarPausa = (id_pausa, matricula) => { + $.ajax({ + url: `http://${server_api}/api/entrarPausa`, + type: "POST", + data: JSON.stringify({ id_pausa, matricula }), + success: function(res){ + if(res.status == 'success'){ + alert('Agente em Pausa!') + } else { + alert(res.message) + } + }, + error: function(res){ + alert('Erro ao carregar as listas de pausa.') + } + }); +} + +const sairPausa = (matricula) => { + $.ajax({ + url: `http://${server_api}/api/sairPausa`, + type: "POST", + data: JSON.stringify({matricula}), + success: function(res){ + alert('Pausa removida do Agente!') + }, + error: function(res){ + alert('Nao foi possivel carregar as listas de pausa.') + } + }); +} + +const finalizarAtendimento = (matricula, uniqueid) => new Promise((resolve) => { + $.ajax({ + url: `http://${server_api}/api/finalizarAtendimento`, + type: "POST", + data: JSON.stringify({matricula, uniqueid}), + success: function(res){ + if(res.status == "success"){ + alert('Atendimento foi finalizado!') + notifications({ matricula, uniqueid, action: 'finish' }) + } + resolve(res) + }, + error: function(res){ + alert('Nao foi possivel carregar as listas de pausa.') + } + }); +}) + +const statusAgente = (matricula) => new Promise((resolve) => { + $.ajax({ + url: `http://${server_api}/api/statusAgente`, + type: "POST", + data: JSON.stringify({matricula}), + success: function(res){ + localStorage.removeItem('obj_status') + localStorage.setItem('obj_status', JSON.stringify(res)) + resolve(res) + }, + error: function(res){ + reconnectWS() + console.log('Recarregando ...') + //alert('Não foi possível carregar as infoemações do agente.') + } + }); +}) + +const transferirAtendimento = (origem, destino, uniqueid) => new Promise((resolve) => { + $.ajax({ + url: `http://${server_api}/api/transferirAtendimento`, + type: "POST", + data: JSON.stringify({ + matricula_origem: origem, + matricula_destino: destino, + uniqueid + }), + success: function(res){ + if(res.status == 'success'){ + alert('Atendimento foi transferido!') + } else { + alert(res.message) + } + resolve(res) + }, + error: function(res){ + alert('Nao foi possivel carregar as infoemacoes do agente.') + } + }); +}) + +const marcarMensagemVista = (uniqueid) => { + $.ajax({ + url: `http://${server_api}/api/marcarMensagemVista`, + type: "POST", + data: JSON.stringify({ uniqueid }), + success: function(res){ + //console.log('success') + }, + error: function(res){ + alert('Nao foi possivel carregar as informacoes do agente.') + } + }); +} \ No newline at end of file diff --git a/public/js/util.js b/public/js/util.js new file mode 100644 index 0000000..97b0db2 --- /dev/null +++ b/public/js/util.js @@ -0,0 +1,496 @@ +/** ROLAGEM DO SCROLL ATÉ NO FINAL DO CHAT */ +const scrollDown = () => { + $(".chat-window").animate({scrollTop: 99999 * $(this).height() }, 1); +} + +/** LIMPA O CONTEUDO DO MODAL */ +const modalStart = () => { + $('#footer-content-left').empty() + $('#footer-content-right').empty() + $('.modal-content-body').empty() + $('.modal-header-title').empty() +} + +/** INICIA COM O HEADER DO CONTATO VAZIO */ +const startChannelMessage = () => { + $('#typemessagebar').hide() + $('#headerbuttonsagent').hide() + $("#headermediaagent").hide() + $('#chatwindowagent').css({ overflow: 'hidden' }) + $('#uploadfiles').hide() +} + +/** CARREGA OS ELEMENTOS DA SESSAO DA CONVERSA */ +const startNotification = () => { + $('#typemessagebar').show(); + $('#headerbuttonsagent').show() + $("#headermediaagent").show() + $('#welcometomessage').hide() + $('#chatwindowagent').css({ overflow: 'scroll' }) +} + +const removeMensagemBody = () => { + $('.chat-window .sender').remove() + $('.chat-window .receiver').remove() + $('.chat-window .events').remove() +} + +const hideButtons = (type) => { + if(type){ + $('#voicerecorder').css({'pointer-events': 'none'}) + $('#imgclip').css({'pointer-events': 'none'}) + $('#fieldsendmessage').css({'pointer-events': 'none'}) + $('#fieldsendmessage').hide() + $('#tranferagent').hide() + $('#finalizaratendimento').hide() + } else { + $('#voicerecorder').css({'pointer-events': 'auto'}) + $('#imgclip').css({'pointer-events': 'auto'}) + $('#fieldsendmessage').css({'pointer-events': 'auto'}) + $('#fieldsendmessage').show() + $('#tranferagent').show() + $('#finalizaratendimento').show() + } +} + +const reconnectWS = () => { + modalStart() + $(this).css({'align-items': 'center'}) + $('.modal-content-body').append(() => + `

RECONECTANDO, AGUARDE  

` + ); + $('.modal-header-title').append(`Por favor, aguarde alguns instantes!`) + $('#modalselect').show() +} +/** + * HABILITA O ENVIO DE ARQUIVO DE IMAGENS E APRESENTA UMA MODAL PARA APRESENTAÇÃO DA IMAGEM SELECIONADA + */ + const startSendImage = () => { + modalStart() + $("#uploadimage").on('change', function(){ + const file = new FileReader(); + file.readAsDataURL(this.files[0]); + const imgName = this.files[0].name + + file.onload = function(e) { + $('#myImg').remove() + $('#footername').remove() + $('#footersend').remove() + + $('.modal-content-body').append(``) + $('#footer-content-left').append(``) + $('#footer-content-right').append(``) + } + $('#modalselect').show() + }) +} + +const startSendFile = () => { + modalStart() + $("#uploadfile").on('change', function(){ + const file = new FileReader(); + file.readAsDataURL(this.files[0]); + const filename = this.files[0].name + const typefile = this.files[0].name.split('.')[1] + + $('#myImg').remove() + icontypes.forEach(e => { + if(typefile.indexOf(e) >= 0){ + $('.modal-content-body').append(``) + return + } + }) + + if(!$('#myImg')[0]){ + $('.modal-content-body').append(``) + } + + file.onload = function(e) { + $('#footername').remove() + $('#footersend').remove() + $('#footer-content-left').append(``) + $('#footer-content-right').append(``) + } + $('#modalselect').show() + }) +} + +const startPause = () => { + $("#entrePause").on('click', function(){ + modalStart() + $("#modalselect").show() + listarPausasAgente(localStorage.getItem('my_uniqueid')) + const pausas = JSON.parse(localStorage.getItem('obj_pauses')) + $('.modal-content-body').append(() => { + let selectPause = `` + return selectPause + }); + + $('.modal-header-title').append(`Selecione uma Pausa:`) + $('#footer-content-right').append(``) + $('#modalselect').show() + }) + + $("#exitPause").on('click', () => { + sairPausa(localStorage.getItem('my_uniqueid')) + supervisorAgente() + }) + + $('#footer-content-right').on('click', '#pausesend', () => { + entrarPausa($("#selectpause").val(), localStorage.getItem('my_uniqueid')) + $('#modalselect').css({display: 'none'}) + }) +} + +const startTransfer = () => { + $("#tranferagent").on('click', function(){ + modalStart() + listarAgentesDisponivel() + const agentes = JSON.parse(localStorage.getItem('obj_agentes_disponivel')) + $('.modal-content-body').append(() => { + let selectPause = `` + return selectPause + }); + + $('.modal-header-title').append(`Selecione um agente para transferir:`) + $('#footer-content-right').append(``) + $('#modalselect').show() + }) + + $('#footer-content-right').on('click', '#transfersend', () => { + transferirAtendimento(localStorage.getItem('my_uniqueid'), $("#selectranfer").val(), localStorage.getItem('session_uniqueid')) + $('#modalselect').css({display: 'none'}) + }) +} + +const startFinalizar = () => { + $("#finalizaratendimento").on('click', function(){ + if(confirm('Deseja realmente finalizar o atendimento?')){ + finalizarAtendimento(localStorage.getItem('my_uniqueid'), localStorage.getItem('session_uniqueid')) + } + }) +} + +/** + * FUNÇÃO PARA RECUPERAR O AUDIO DO MICROFONE + */ +function recorderVoice () { + $('#modalselect').show() + modalStart() + $('.modal-content-body').append(` + `) + + $('#footer-content-right').append(``) + + navigator.mediaDevices.getUserMedia({audio: true}).then( + stream => { + mediaRecorder = new MediaRecorder(stream) + let chunks = [] + + mediaRecorder.ondataavailable = data => { + chunks.push(data.data) + } + + mediaRecorder.onstop = () => { + const blob = new Blob(chunks, {type : 'audio/mpeg'}) + const reader = new FileReader() + reader.readAsDataURL(blob) + reader.onloadend = () => { + const audio = document.createElement('audio') + audio.src = reader.result + audio.controls = true + $('#footer-content-left').append(audio) + } + } + mediaRecorder.start() + }, err => { + // add msg de error + } + ) +} + +/** + * ENVIA AS MENSAGEM DO TIPO MIDIA (RECEPTIVO) + * @param {*} id_provedor + * @param {*} type + * @param {*} mimetype + * @param {*} delivery + * @returns + */ + const messageTypeMedia = (obj) => { + const fileDownload = URLApi + "/link/" + obj.id_provedor + "/" + window.btoa(obj.mimetype) + if(obj.type == 'voice' || obj.type == 'audio'){ + $('.chat-window').append(` +
+ + + +
+ ${converdata(Date.now())} +
`) + return + } + + if(obj.type == 'video'){ + $('.chat-window').append(` +
+ + + +
+ ${converdata(Date.now())} +
`) + return + } + + if(obj.type == 'document'){ + const typefile = obj.filename.split('.')[1] + let icon + if(icontypes.indexOf(typefile) >= 0){ + icon = `` + } else { + icon = `` + } + + $('.chat-window').append(` +
+ + + ${icon} + + + ${obj.filename} +
+ ${converdata(Date.now())} +
`) + return + } + + if(obj.type == 'image' || obj.type == 'sticker'){ + $('.chat-window').append(` +
+ + + + +
+ ${converdata(Date.now())} +
`) + return + } +} + +/** + * data = { number, media, name, datetime } + * + * @param {} data + * @returns + */ +const buildNotification = (data = {}) => { + if(data.length == 'undefined'){ + return + } + + const datesend = converdata(data.datetime) + const status = data.status == 0 ? 'opacity-3' : '' + return `
+
+ +
+
+
+ ${data.name} + ${datesend} +
+
+
+ ${data.media} +
+
+
+
+
+
` +} + +const alertNotification = (uniqueid, type = 'add') => { + $('#' + uniqueid.replace('.', `\\.`) + " .chat-right-bottom-right").empty() + if(type != 'remove'){ + listaMensagem(uniqueid) + const mensagens = JSON.parse(localStorage.getItem('obj_contact')) + const countMsg = mensagens.data.filter(e => { + if(e.event.mensagem.status != 'read'){ + return true + } + }) + + console.log(countMsg) + let notf = countMsg.length + 1 + $('#' + uniqueid.replace('.', `\\.`) + " .chat-right-bottom-right").append(`${notf}`) + + } +} + +/** + * CRIA AS NOTIFICACOES DE TODOS OS ATENDIMENTOS NA INICIALIZACAO DO SISTEMA OU ATUALIZACAO + */ +const notifications = (obj = {}) => { + + listarAtendimentoAgente(localStorage.getItem('my_uniqueid')).then(() => { + let chatList = '' + $('#chats').empty() + const notification = JSON.parse(localStorage.getItem('obj_notification')) + + if(Object.values(obj).length > 0) { + if(obj.action == "mensagem"){ + notification.data.push(obj) + } else if (obj.action == "finish"){ + /** RECEBIMENTO DO SOCKET PARA ALTERAR ESTILO DO HTML */ + notification.data.forEach(el => { + if(el.uniqueid == obj.uniqueid){ + /** MARCA ATENDIMENTO COMO FINALIZADO */ + el.status = 0 + /** REMOVE OS BOTÕES E CAIXA DE TEXTO DEPOIS DA FINALIZACAO */ + if(el.uniqueid == localStorage.getItem('session_uniqueid')){ + hideButtons(true) + } + } + }) + } + } + + notification.data.sort((a, b) => b.status - a.status) + notification.data.forEach(e => { + chatList += buildNotification({ + uniqueid: e.uniqueid, + number: e.cliente_id, + media: e.context, + name: e.profile_name, + datetime: e.data_reg, + status: e.status + }) + }) + $('#chats').append(chatList) + }) +} + +const supervisorAgente = () => { + statusAgente(localStorage.getItem('my_uniqueid')) + const status = JSON.parse(localStorage.getItem('obj_status')) + + /** CONFIGURACAO DO BOTAO STATUS */ + if(status.data.status == 'LIVRE'){ + $('#exitPause').hide() + $('#entrePause').show() + } else { + $('#entrePause').hide() + $('#exitPause').show() + } + + /** CONFIGURACAO DE MATRICULA */ + $('#myuniqueid').text(localStorage.getItem('my_uniqueid')) + + /** CONFIGURACAO NOME */ + $('#nameagent').text(status.data.nome) + + /** CONFIGURACAO FILA */ + $('#queueagente').text(status.data.fila) + + /** CONFIGURACAO DE STATUS */ + let statusagent + statusagent = status.data.status + if(status.data.status == "PAUSA"){ + $('#status_agent').attr('class','status-desconnect') + statusagent = status.data.status + " - " + status.data.motivo_pausa + } else if(status.data.status == "OCUPADO") { + $('#status_agent').attr('class','status-reconnect') + } else { + $('#status_agent').attr('class','status-connect') + } + $('#status_agent').text(statusagent) + + /** MONITORA AS CONFIGURACOES */ + setTimeout(() => { + supervisorAgente() + }, 1000); +} + +/** CONNECT TO WS */ +const connect = (wsserver, reconnect = false) => { + const ws = new WebSocket(wsserver); + + ws.onmessage = function(e) { + //console.log('Message:', e.data); + }; + + ws.onclose = function(e) { + console.log('Socket is closed. Reconnect will be attempted in 1 second.', e.reason); + setTimeout(function() { + connect(wsserver, true); + }, 1000); + }; + + ws.onerror = function(err) { + console.error('Socket encountered error: ', err.message, 'Closing socket'); + ws.close(); + }; + + ws.onopen = function() { + $("#status_agent").addClass("status-connect").text('CONECTADO'); + ws.send({message: 'success to connected!'}); + if(reconnect == true){ + reconnect = false + location.reload() + } + }; + + ws.addEventListener("open", () => { + const storage = ['my_uniqueid', 'keep_msg', 'obj_contact', 'session_uniqueid', 'session_window'] + + storage.forEach(e => { + //localStorage.removeItem(e); + }) + + ws.addEventListener("message", e => { + /** att: atualizacao do websocket */ + if(e.data != 'att'){ + const data = JSON.parse(e?.data) + + if(localStorage.getItem('session_uniqueid') == null){ + localStorage.setItem('session_uniqueid', data.event.mensagem.uniqueid) + } + + if($("#welcometomessage").is(':hidden') == false){ + localStorage.setItem('session_window', null) + } + + /** ATUALIZACAO DA SESSAO CORRENTE (VALIDA PARA NAO ENVIAR PARA TELA INICIAL)*/ + if(localStorage.getItem('session_window') !== 'null'){ + viewMessage(data) + } + + /** RECEBE AS PRIMEIRAS MENSAGENS */ + receiveNotification(data) + } + }) + }) +} \ No newline at end of file From 5dfea93d55e5c20b955b59bc173ca1e88cc86fe9 Mon Sep 17 00:00:00 2001 From: Simples IP Desenvolvimento Date: Mon, 20 Dec 2021 15:56:08 -0400 Subject: [PATCH 034/144] remove comments --- public/js/main.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/public/js/main.js b/public/js/main.js index 60115c0..5ecfe5e 100644 --- a/public/js/main.js +++ b/public/js/main.js @@ -433,8 +433,4 @@ const keepMensage = (ev) => { sendMessage({ content: file.result.replace("data:", "").replace(/^.+,/, ""), type: typefile, mimetype: rec.type, fileContent, filename }) } file.readAsDataURL(rec); -} - -/** - * EVENTO DE INTERAÇÃO COM O WEBSOCKET PARA RECURAR TODOS OS EVENTOS TRANSMITIDOS - */ +} \ No newline at end of file From 91b3c00c5aadde33759396f00c60a67fc5a0464e Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Tue, 21 Dec 2021 13:46:23 -0400 Subject: [PATCH 035/144] ajustes no metodos listarFilas e enviar msg --- app/Core/CoreMedia.php | 5 +---- app/Middleware/ApiAgente.php | 27 +++++++++++++++++++++++++-- app/Providers/Positus.php | 16 +++++++++------- app/Providers/RequestURL.php | 9 ++++++--- 4 files changed, 41 insertions(+), 16 deletions(-) diff --git a/app/Core/CoreMedia.php b/app/Core/CoreMedia.php index 32ffe7a..e2b157c 100644 --- a/app/Core/CoreMedia.php +++ b/app/Core/CoreMedia.php @@ -65,15 +65,12 @@ class CoreMedia { /** Validate $data */ $this->build($data); - + logger('debuge')->debug(var_export($data, true)); $this->api = $api; $this->api->setHook($this->request); $msg = $this->api->getIsValidMessage(); // logger('deburguer')->info(var_export($msg, true)); $this->ws->enviaMsg('att'); - if (empty($msg)) { - return; - } if (!$this->api->baixarMidia()) { $this->api->enviarMsg($this->api->getPhone(), utf8_encode("Não foi possivel entregar o aquivo para o agente. Envie novamente!")); } diff --git a/app/Middleware/ApiAgente.php b/app/Middleware/ApiAgente.php index 5068c60..d574f85 100644 --- a/app/Middleware/ApiAgente.php +++ b/app/Middleware/ApiAgente.php @@ -8,6 +8,7 @@ use app\Controllers\QueueController; use app\Models\Agent; use app\Models\Atendimento; use app\Models\Evento; +use app\Models\ListaNegraPalavras; use app\Models\Message; use app\Models\Parametros; use app\Models\Pause; @@ -255,7 +256,11 @@ class ApiAgente { $queue = new QueueController(); $search = $queue->listaAllFilas(); - echo json_encode($search); + $this->retorno( + $search ? "Sucesso" : "Erro", + $search ? $search : null, + $search ? $search : null + ); return null; } @@ -317,7 +322,10 @@ class ApiAgente $modelMensagem = new Message(); if ($mensagem['type'] == 'text') { // logger('enviaMsg')->info(print_r($mensagem, true)); - $retuno = $provider->enviarMsg($mensagem['dst'], $mensagem['content']); + $retuno = $provider->enviarMsg( + $mensagem['dst'], + $this->validaPalavroes($mensagem['content']) + ); } else { $anmeArquivo = __DIR__ . "/../../storage/files/" . $mensagem['id_provedor']; $texto = base64_decode($mensagem['content']); @@ -598,4 +606,19 @@ class ApiAgente logger('monitora')->info($th->getMessage(), debug_backtrace()); } } + + 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 (\Throwable $th) { + logger('telegram')->info(print_r($th, true), true); + } + return $msg; + } } \ No newline at end of file diff --git a/app/Providers/Positus.php b/app/Providers/Positus.php index ee2c7c2..fd4fa72 100644 --- a/app/Providers/Positus.php +++ b/app/Providers/Positus.php @@ -244,7 +244,9 @@ class Positus implements IApiMedia $this->requestType("GET"); $this->setMetodo('media/' . $name); $pathfile = $this->storage . $name; - file_put_contents($pathfile, $this->exec()); + $retorno = $this->exec(); + logger('debuge')->debug(var_export($retorno, true)); + file_put_contents($pathfile, $retorno); if (file_exists($pathfile)) { return true; } @@ -462,27 +464,27 @@ class Positus implements IApiMedia } $this->request->header($header); $this->request->method_request($this->requestType); - return $this->request->exec_request(); + $response = $this->request->exec_request(); + return $this->response($response); } function response($result) { - // logger('deburguer')->info(print_r($result, true)); + logger('deburguer')->info(gettype($result)); if ($result) { try { if (json_decode($result, true) !== null) { return json_decode($result, true); } - return json_decode($result, true); - } catch (\Exception $th) { - logger('deburguer')->info($th->getMessage()); + return $result; + } catch (\Throwable $th) { + logger('deburguer')->info(print_r($result, true)); return false; } } else { return false; } } - /** * Create file and download in browser */ diff --git a/app/Providers/RequestURL.php b/app/Providers/RequestURL.php index afe3811..9a4925d 100644 --- a/app/Providers/RequestURL.php +++ b/app/Providers/RequestURL.php @@ -71,9 +71,12 @@ class RequestURL private function response($result) { // logger('deburguer')->info(print_r($result, true)); - try { - return json_decode($result, true); - } catch (\Throwable $th) { + if ($result) { + if (json_decode($result, true) !== null) { + return json_decode($result, true); + } + return $result; + } else { return false; } } From 2b2cfbe2d7a9b18207fd030ce849a96e1f723c4c Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Tue, 21 Dec 2021 16:19:11 -0400 Subject: [PATCH 036/144] Update WsInterface.php --- websocket/WsInterface.php | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/websocket/WsInterface.php b/websocket/WsInterface.php index 59daaa3..27b1190 100644 --- a/websocket/WsInterface.php +++ b/websocket/WsInterface.php @@ -10,15 +10,15 @@ class WsInterface function enviaMsg($msg) { - if ($msg) { + if (!empty($msg)) { $this->client = new Client("ws://192.168.115.65:8090/ws"); - $this->client->text($msg); + $this->client->send($msg); $this->client->close(); return null; } } - function enviaActions($msg, $tipo, $matricula, $uniqueid) + function enviaActions($msg, $tipo, $matricula = null, $uniqueid = null) { try { $mensagem = []; @@ -29,13 +29,13 @@ class WsInterface "number" => '0' ], "mensagem" => [ - "type" => $tipo, - "dst" => $matricula, - "uniqueid" => $uniqueid, + "type" => utf8_encode($tipo), + "dst" => utf8_encode($matricula), + "uniqueid" => utf8_encode($uniqueid), "content" => utf8_encode($msg) ], ]; - return $this->enviaMsg(json_encode($mensagem)); + return json_encode($mensagem); } catch (\Throwable $th) { logger('monitora')->info($th->getMessage(), debug_backtrace()); } From c2bc409a61cffc7b037159250d67b514eb54ab34 Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Wed, 5 Jan 2022 17:52:43 -0400 Subject: [PATCH 037/144] Update composer.json --- composer.json | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/composer.json b/composer.json index 3583933..97d2742 100644 --- a/composer.json +++ b/composer.json @@ -1,12 +1,10 @@ { "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/", @@ -16,7 +14,7 @@ } }, "require": { - "cboden/ratchet": "^0.4", - "textalk/websocket": "^1.5" + "cboden/ratchet": "^0.4.4", + "textalk/websocket": "^1.3.1" } } \ No newline at end of file From bb5182c81dafc593e59e673c9df453bf758594f1 Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Wed, 5 Jan 2022 17:53:25 -0400 Subject: [PATCH 038/144] arquivos database e dados --- database/att-v3.sql | 369 ++++++++++++++++++++++++++++++++++ database/database.sql | 27 ++- database/modelo_mensagem.json | 21 +- 3 files changed, 406 insertions(+), 11 deletions(-) create mode 100644 database/att-v3.sql diff --git a/database/att-v3.sql b/database/att-v3.sql new file mode 100644 index 0000000..cdf7632 --- /dev/null +++ b/database/att-v3.sql @@ -0,0 +1,369 @@ +-- 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; + +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_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'); \ No newline at end of file diff --git a/database/database.sql b/database/database.sql index 238ec7e..eeb1567 100644 --- a/database/database.sql +++ b/database/database.sql @@ -17,4 +17,29 @@ ADD ALTER TABLE pbx_sip_ramais ADD - COLUMN midiaramal VARCHAR(1); \ No newline at end of file + 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; \ No newline at end of file diff --git a/database/modelo_mensagem.json b/database/modelo_mensagem.json index 1a2d36e..4af07f5 100644 --- a/database/modelo_mensagem.json +++ b/database/modelo_mensagem.json @@ -2,19 +2,20 @@ "event": { "type": "mensagem", "contact": { - "name": "Lucas Awade", - "number": "556581282842", - "matricula": "1022" + "name": "Lucas", + "number": "556596107663", + "matricula": "" }, "mensagem": { - "id": "425435243", - "dst": "871643214123", - "idAtendimento": "43524352345", "type": "text", - "media": "WHATSAPP", - "datetime": 1637677973, - "content": "ap", - "status": "read|delivery|envid" + "content": "ola", + "id_provedor": null, + "dst": "1040", + "uniqueid": 1638191429, + "media": "whatsapp", + "datetime": 1638191429, + "status": "sent", + "mimetype": false } } } \ No newline at end of file From 334f02d55c0f1218cee820a939faeda521352a74 Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Wed, 5 Jan 2022 17:54:41 -0400 Subject: [PATCH 039/144] add basta de testes --- .gitignore | 3 ++- tests/teste.php | 36 ++++++++++++++++++++++++++++++++++++ tests/teste2.php | 37 +++++++++++++++++++++++++++++++++++++ 3 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 tests/teste.php create mode 100644 tests/teste2.php diff --git a/.gitignore b/.gitignore index 576e4c4..c7e1704 100644 --- a/.gitignore +++ b/.gitignore @@ -17,4 +17,5 @@ yarn-error.log /public/node_modules /public/vendor /banco -/websocket/vendor \ No newline at end of file +/websocket/vendor +composer.lock diff --git a/tests/teste.php b/tests/teste.php new file mode 100644 index 0000000..f8aad9c --- /dev/null +++ b/tests/teste.php @@ -0,0 +1,36 @@ + [ + "type" => + "mensagem", "contact" => [ + "name" => "Lucas", + "number" => "1040", + "matricula" => "" + ], + "mensagem" => [ + "type" => "audio", + "content" => base64_encode($varre), + "id_provedor" => '6545243524333', + "dst" => "556596107663", + "uniqueid" => 1638196372, + "media" => "whatsapp", + "datetime" => 1638196372, + "status" => "sent", + "mimetype" => "audio/ogg" + ] +]]; +$request->setUrl($url); +$header = array(); +$header[] = 'Content-Type: application/json'; +$request->post_field(json_encode($data), true); +$request->header($header); +$request->method_request("POST"); +$response = $request->exec_request(); +// echo base64_encode($varre); \ No newline at end of file diff --git a/tests/teste2.php b/tests/teste2.php new file mode 100644 index 0000000..91b3c32 --- /dev/null +++ b/tests/teste2.php @@ -0,0 +1,37 @@ + [ + "type" => + "mensagem", "contact" => [ + "name" => "Lucas", + "number" => "1040", + "matricula" => "" + ], + "mensagem" => [ + "type" => "document", + "content" => base64_encode($varre), + "id_provedor" => '6545243524333', + "dst" => "556596107663", + "uniqueid" => 1638196372, + "file_name" => 'bruno', + "media" => "whatsapp", + "datetime" => 1638196372, + "status" => "sent", + "mimetype" => "application/pdf" + ] +]]; +$request->setUrl($url); +$header = array(); +$header[] = 'Content-Type: application/json'; +$request->post_field(json_encode($data), true); +$request->header($header); +$request->method_request("POST"); +$response = $request->exec_request(); +// echo base64_encode($varre); \ No newline at end of file From 26caf66ecc6794ea626738a4871a1e3b007b25ef Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Thu, 6 Jan 2022 08:58:21 -0400 Subject: [PATCH 040/144] =?UTF-8?q?desmigra=C3=A7=C3=A3o=20da=20vers=C3=A3?= =?UTF-8?q?o=207=20php=20para=205.6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Controllers/BilheteController.php | 1 - app/Core/Commands.php | 57 +++++++------------- app/Core/CoreMedia.php | 45 ++++++++-------- app/Interfaces/IApiMedia.php | 1 - app/Middleware/ApiAgente.php | 54 +++++++++---------- app/Middleware/Middleware.php | 8 +-- app/Models/Supervisor.php | 75 +++++++-------------------- app/Providers/Positus.php | 27 ++++------ config/app.php | 2 +- service/MonitoraAgente.php | 28 +++------- websocket/Servidorsocket.php | 72 ++++++++++++++----------- websocket/WsInterface.php | 4 +- websocket/websocket.php | 50 +++++++++++++----- 13 files changed, 193 insertions(+), 231 deletions(-) diff --git a/app/Controllers/BilheteController.php b/app/Controllers/BilheteController.php index 5f9e71c..9f6649f 100644 --- a/app/Controllers/BilheteController.php +++ b/app/Controllers/BilheteController.php @@ -164,7 +164,6 @@ 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'; diff --git a/app/Core/Commands.php b/app/Core/Commands.php index bc7be5a..50bfa0c 100644 --- a/app/Core/Commands.php +++ b/app/Core/Commands.php @@ -102,27 +102,8 @@ class Commands $messageParams = explode(" ", $message); $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(); case '/CANCELAR': return $this->cancelar($phone); default: @@ -206,25 +187,25 @@ class Commands } } - 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'; - } - $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 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'; + // } + // $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) { diff --git a/app/Core/CoreMedia.php b/app/Core/CoreMedia.php index e2b157c..0c88c95 100644 --- a/app/Core/CoreMedia.php +++ b/app/Core/CoreMedia.php @@ -14,6 +14,7 @@ use app\Models\Evento; use app\Models\ListaNegraPalavras; use app\Models\Ramal; use app\Models\Supervisor; +use app\Providers\Positus; use websocket\WsInterface; /** @@ -23,17 +24,16 @@ use websocket\WsInterface; */ class CoreMedia { - private IApiMedia $api; - private $file; + private $api; private $request; private $commands; private $queue; - private AgentController $agente; + private $agente; private $palavroes; private $ws; private $atendimento; - private Supervisor $supervisor; - private Evento $eventos; + private $supervisor; + private $eventos; private $host; private $bilheteController; private $message; @@ -61,20 +61,16 @@ class CoreMedia * * @return null */ - public function inicia($data, IApiMedia $api) + public function inicia($data, IApiMedia $providere) { /** Validate $data */ $this->build($data); - logger('debuge')->debug(var_export($data, true)); - $this->api = $api; + $this->api = $providere; $this->api->setHook($this->request); - $msg = $this->api->getIsValidMessage(); - // logger('deburguer')->info(var_export($msg, true)); - $this->ws->enviaMsg('att'); if (!$this->api->baixarMidia()) { $this->api->enviarMsg($this->api->getPhone(), utf8_encode("Não foi possivel entregar o aquivo para o agente. Envie novamente!")); } - // VERIFICA SE ? UM COMANDO + // 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())); @@ -120,7 +116,7 @@ class CoreMedia $atendiment->matricula, $this->api->getType(), $this->retornaConteudo(), - $this->api->getProfile() ?? '', + empty($this->api->getProfile()) ? '' : $this->api->getProfile(), $this->api->getchannel(), "sended", $this->api->getMimetype(), @@ -137,11 +133,12 @@ class CoreMedia $this->api->getPhone(), time(), $this->api->getId(), - $this->api->getMimetype() // + $this->api->getMimetype() ) ); return null; } + $this->ws->enviaMsg('att'); //verifica se tem atendimento em espera, se tiver ja mostra a sua posição na fila $atende = $this->atendimento->getAtendimentoByCliente($this->api->getPhone()); if ($atende) { @@ -187,20 +184,22 @@ class CoreMedia } else { $this->request['messages'][0]['text']['body'] = $msg; } - } catch (\Throwable $th) { + } catch (\Exception $th) { logger('telegram')->info(print_r($th, true), true); } return; } - public function criaAtendimento(string $fila, $numero, $uniqueid = null) + public function criaAtendimento($fila, $numero, $uniqueid = null) { try { + logger('core')->debug('criaAtendimento_1: '); 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); } } + logger('core')->debug('criaAtendimento_2: '); $agentes = $this->agente->infoAgentes($fila); if (empty($agentes)) { $this->api->enviarMsg( @@ -209,7 +208,7 @@ class CoreMedia ); return null; } - + logger('core')->debug('criaAtendimento_3: '); $agent = $this->supervisor->findAgentByQueue($fila, 'LIVRE'); if (empty($agent)) { $this->api->enviarMsg( @@ -219,6 +218,7 @@ class CoreMedia $this->mostraPosiscaoNaFila($numero, $fila); return null; } + logger('core')->debug('criaAtendimento_4: '); $retServe = $this->atendimento->updAtendimento($uniqueid, $agent[0]->matricula); if (empty($retServe)) { $this->api->enviarMsg( @@ -227,17 +227,19 @@ class CoreMedia ); return null; } - + logger('core')->debug('criaAtendimento_5: '); $protocol = $this->bilheteController->generateProtocol($uniqueid); //cria o evento de inicio de atendimento e criar o protocolo if ($protocol) { + logger('core')->debug('criaAtendimento_6: '); $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)) { + logger('core')->debug('criaAtendimento_7: '); $atendimentosAbertos = $this->atendimento->getAtendimentoAbertoByAgente($agent[0]->matricula); $sup = new Supervisor(); $param = $this->atendimento->getQuantiAtendimentSimultaneos(); $ws = new WsInterface(); - + logger('core')->debug('criaAtendimento_8: '); if ((count($atendimentosAbertos)) >= $param->prm_media_simultaneo) { $sup->updateAgent($agent[0]->matricula, CONF_AGENT_STATUS_OCUPADO); } @@ -251,13 +253,14 @@ class CoreMedia ); try { $ws->enviaMsg($ws->enviaActions('Atendimento iniciado!', 'start', $agent[0]->matricula, $uniqueid)); - } catch (\Throwable $th) { + } catch (\Exception $th) { return $ws->enviaActions('Atendimento iniciado!', 'start', $agent[0]->matricula, $uniqueid); } } } + logger('core')->debug('criaAtendimento_final: '); return; - } catch (\Throwable $th) { + } catch (\Exception $th) { logger('criaAtendimento')->info($th->getMessage()); return; } diff --git a/app/Interfaces/IApiMedia.php b/app/Interfaces/IApiMedia.php index 1696cc6..a8af7cd 100644 --- a/app/Interfaces/IApiMedia.php +++ b/app/Interfaces/IApiMedia.php @@ -34,7 +34,6 @@ interface IApiMedia function getArgs($args); function requestType($req = null); function exec(); - function response($result); public function getLinkDownload($host); public function retornaTituloDocument(); public function convertToWebsocket($content, $matricula = '', $idAtendimento, $type, $name, $number, $data, $idProvedor, $mimetype); diff --git a/app/Middleware/ApiAgente.php b/app/Middleware/ApiAgente.php index d574f85..b9daa81 100644 --- a/app/Middleware/ApiAgente.php +++ b/app/Middleware/ApiAgente.php @@ -5,7 +5,6 @@ namespace app\Middleware; use app\Controllers\AgentController; use app\Controllers\ClassificacaoController; use app\Controllers\QueueController; -use app\Models\Agent; use app\Models\Atendimento; use app\Models\Evento; use app\Models\ListaNegraPalavras; @@ -19,14 +18,15 @@ use websocket\WsInterface; class ApiAgente { - public AgentController $agentController; - public ClassificacaoController $classificacao; - public Supervisor $supervisor; - public Atendimento $atendimento; - public Evento $eventos; - public Parametros $parametros; - public Pause $pausasModel; - function __construct() + public $agentController; + public $classificacao; + public $supervisor; + public $atendimento; + public $eventos; + public $parametros; + public $pausasModel; + + public function __construct() { $this->queue = new QueueController(); $this->classificacao = new ClassificacaoController(); @@ -121,9 +121,9 @@ class ApiAgente } $this->retorno( $ret ? "Logado com sucesso" : "Erro", - $ret ? $ret : null, + $ret ? $ret : null ); - } catch (\Throwable $th) { + } catch (\Exception $th) { $this->retorno($th->getMessage()); } return null; @@ -152,9 +152,9 @@ class ApiAgente } $this->retorno( $ret ? "Deslogado com sucesso" : "Erro", - $ret ? $ret : null, + $ret ? $ret : null ); - } catch (\Throwable $th) { + } catch (\Exception $th) { $this->retorno($th->getMessage()); } return null; @@ -176,7 +176,7 @@ class ApiAgente $agentes ? $agentes : null, $agentes ? $agentes : null ); - } catch (\Throwable $th) { + } catch (\Exception $th) { $this->retorno($th->getMessage()); } return null; @@ -214,7 +214,7 @@ class ApiAgente $ws = new WsInterface(); $ws->enviaMsg($this->enviaActions('Atendimento finalizado', 'finish', $agente->matricula, $request['uniqueid'])); $provedor = new Positus(); - $provedor->enviarMsg($atendimento->cliente_id, CONF_NAME_REPONSE . " : Atendimento finalizado"); + $provedor->enviarMsg($atendimento->cliente_id, utf8_encode(CONF_NAME_REPONSE . " : Atendimento finalizado")); $messegeModel = new Message(); $messegeModel->addMessage( $request['uniqueid'], @@ -245,7 +245,7 @@ class ApiAgente $ret ? $ret : null, $ret ? ["id" => $ret] : null ); - } catch (\Throwable $th) { + } catch (\Exception $th) { $this->retorno($th->getMessage()); } @@ -355,14 +355,14 @@ class ApiAgente $this->retorno( $retuno ? "Sucesso" : "Erro", $retuno ? $retuno : null, - $retuno ? $retuno : null, + $retuno ? $retuno : null ); } else { $this->retorno('Atendimento já foi finalizado'); } return; - } catch (\Throwable $th) { + } catch (\Exception $th) { $this->retorno($th->getMessage()); return; } @@ -445,7 +445,7 @@ class ApiAgente $data['data'] = $mensagenss; echo json_encode($data); return; - } catch (\Throwable $th) { + } catch (\Exception $th) { $this->retorno($th->getMessage()); return; } @@ -498,7 +498,7 @@ class ApiAgente } else { $this->retorno( "Agente em 'indisponivel'", - $ret, + $ret ); } return; @@ -506,7 +506,7 @@ class ApiAgente $ret = $this->agentController->enterPause($request['matricula'], $pausa->motivo); $this->retorno( $ret ? "Agente em 'pausa'" : "Erro", - $ret ? $ret : null, + $ret ? $ret : null ); return null; } @@ -525,7 +525,7 @@ class ApiAgente } else { $this->retorno( "Agente 'livre'", - $ret, + $ret ); } return; @@ -543,7 +543,7 @@ class ApiAgente } else { $this->retorno( $retunr ? "Sucesso" : "Erro", - $retunr ? $retunr : null, + $retunr ? $retunr : null ); } return null; @@ -560,7 +560,7 @@ class ApiAgente $ret = $modelMensagem->markMessege($request['uniqueid'], 'read'); $this->retorno( $ret ? "Sucesso" : "Erro", - $ret ? $ret : null, + $ret ? $ret : null ); return null; } @@ -579,7 +579,7 @@ class ApiAgente $ret ? $ret : null, $ret ? $ret : null ); - } catch (\Throwable $th) { + } catch (\Exception $th) { $this->retorno($th->getMessage()); } } @@ -602,7 +602,7 @@ class ApiAgente ], ]; return json_encode($mensagem); - } catch (\Throwable $th) { + } catch (\Exception $th) { logger('monitora')->info($th->getMessage(), debug_backtrace()); } } @@ -616,7 +616,7 @@ class ApiAgente $pattern = "/\b($value->palavra)\b/i"; $msg = preg_replace($pattern, '*' . str_repeat('*', strlen($value->palavra)) . '*', $msg); } - } catch (\Throwable $th) { + } catch (\Exception $th) { logger('telegram')->info(print_r($th, true), true); } return $msg; diff --git a/app/Middleware/Middleware.php b/app/Middleware/Middleware.php index 7d86320..323444b 100644 --- a/app/Middleware/Middleware.php +++ b/app/Middleware/Middleware.php @@ -62,10 +62,10 @@ class Middleware extends Http $coremedia->inicia($this->request, new Positus()); echo json_encode(['success' => true]); return null; - case 'telegram': - $coremedia->inicia($this->request, new ApiTelegram()); - echo json_encode(['success' => $this->request]); - return null; + // case 'telegram': + // $coremedia->inicia($this->request, new ApiTelegram()); + // echo json_encode(['success' => $this->request]); + // return null; case 'api': $apiAgente->router($this->param()[2], $this->request); return null; diff --git a/app/Models/Supervisor.php b/app/Models/Supervisor.php index 0120ae5..f7b0cb1 100644 --- a/app/Models/Supervisor.php +++ b/app/Models/Supervisor.php @@ -24,19 +24,9 @@ class Supervisor extends Model count(*) FROM md_atendimento ma - INNER JOIN md_evento me ON me.uniqueid = ma.uniqueid - WHERE - me.evento = 'START' - AND 'START' = ( - SELECT - m2.evento - FROM - md_evento m2 - WHERE - ma.uniqueid = m2.uniqueid - ORDER BY id DESC - LIMIT 1 - ) + 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 @@ -47,19 +37,9 @@ class Supervisor extends Model count(*) FROM md_atendimento ma - INNER JOIN md_evento me ON me.uniqueid = ma.uniqueid - WHERE - me.evento = 'START' - AND 'START' = ( - SELECT - m2.evento - FROM - md_evento m2 - WHERE - ma.uniqueid = m2.uniqueid - ORDER BY id DESC - LIMIT 1 - ) + 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 ) @@ -77,19 +57,9 @@ class Supervisor extends Model count(*) FROM md_atendimento ma - INNER JOIN md_evento me ON me.uniqueid = ma.uniqueid - WHERE - me.evento = 'START' - AND 'START' = ( - SELECT - m2.evento - FROM - md_evento m2 - WHERE - ma.uniqueid = m2.uniqueid - ORDER BY id DESC - LIMIT 1 - ) + 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 @@ -147,28 +117,19 @@ class Supervisor extends Model $data = []; $this->query = "SELECT * FROM md_supervisor ms - WHERE ( - SELECT + WHERE ms.fila = :queue + AND ms.status = :status + ORDER BY ( + SELECT count(*) FROM md_atendimento ma - INNER JOIN md_evento me ON me.uniqueid = ma.uniqueid - WHERE - me.evento = 'START' - AND 'START' = ( - SELECT - m2.evento - FROM - md_evento m2 - WHERE - ma.uniqueid = m2.uniqueid - ORDER BY id DESC - LIMIT 1 - ) + 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 ) - AND ms.fila = :queue - AND ms.status = :status"; + ) + "; $data['queue'] = $queue; $data['status'] = $status; diff --git a/app/Providers/Positus.php b/app/Providers/Positus.php index fd4fa72..94cf463 100644 --- a/app/Providers/Positus.php +++ b/app/Providers/Positus.php @@ -15,7 +15,7 @@ class Positus implements IApiMedia private $request; private $params = array(); private $hook; - private $storage = __DIR__ . "/../../storage/files/"; + private $storage = "/var/www/html/aplicativo/media/storage/files/"; public $channel = CONF_WHATSAPP_CHANNEL; public $timeout_client_resposta = CONF_WHATSAPP_TIMEOUT_CLIENT_RESPOSTA; @@ -90,11 +90,11 @@ class Positus implements IApiMedia $this->requestType("POST"); $this->setMetodo('messages'); return $this->exec(); - //} } function enviarMsg($whatsapp, $mensagem) { + $this->debug = debug_backtrace(); $this->params = array( "to" => "+$whatsapp", @@ -185,7 +185,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", @@ -245,7 +245,6 @@ class Positus implements IApiMedia $this->setMetodo('media/' . $name); $pathfile = $this->storage . $name; $retorno = $this->exec(); - logger('debuge')->debug(var_export($retorno, true)); file_put_contents($pathfile, $retorno); if (file_exists($pathfile)) { return true; @@ -454,7 +453,7 @@ class Positus implements IApiMedia function exec() { $this->setQuery(json_encode($this->params)); //SET QUERY - $this->request->setUrl($this->url . $this->metodo); + $this->request->setUrl($this->url . $this->metodo, false); $header = array(); $header[] = "Authorization: Bearer {$this->token}"; @@ -462,25 +461,21 @@ class Positus implements IApiMedia $header[] = 'Content-Type: application/json'; $this->request->post_field($this->getQuery(), true); } + // logger('core')->debug('header: ' . var_export($header, true), debug_backtrace()); + // logger('core')->debug('body: ' . var_export($this->params, true), debug_backtrace()); $this->request->header($header); $this->request->method_request($this->requestType); - $response = $this->request->exec_request(); - return $this->response($response); + return $this->request->exec_request(); + //return $this->response($response); } function response($result) { - logger('deburguer')->info(gettype($result)); if ($result) { - try { - if (json_decode($result, true) !== null) { - return json_decode($result, true); - } - return $result; - } catch (\Throwable $th) { - logger('deburguer')->info(print_r($result, true)); - return false; + if (json_decode($result, true) !== null) { + return json_decode($result, true); } + return $result; } else { return false; } diff --git a/config/app.php b/config/app.php index 8280087..40d269f 100644 --- a/config/app.php +++ b/config/app.php @@ -18,7 +18,7 @@ define("CONF_NAME_REPONSE", 'Simples IP'); | Local para armazenar os dados referente a API utilzada. | */ -define("CONF_LOG_PATH", '/var/www/html/storage/log/'); +define("CONF_LOG_PATH", '/var/www/html/aplicativo/integracao/media/storage/log/'); /* diff --git a/service/MonitoraAgente.php b/service/MonitoraAgente.php index d02b9d7..bc0364b 100644 --- a/service/MonitoraAgente.php +++ b/service/MonitoraAgente.php @@ -2,31 +2,17 @@ namespace service; -error_reporting(E_ERROR); -ini_set('display_errors', 0); - -use app\Controllers\AgentController; -use app\Controllers\BilheteController; -use app\Controllers\MessageController; -use app\Controllers\ClassificacaoController; -use app\Controllers\SupervisorQueueController; use app\Core\Connect; use app\Core\CoreMedia; use app\Providers\Positus; -use app\Core\Media; -use app\Models\Agent; use app\Models\Atendimento; -use app\Models\Message; -use app\Models\NotificaMedia; use app\Models\Supervisor; use app\Providers\ApiTelegram; -use Exception; -use Ratchet\ConnectionInterface; class MonitoraAgente { - public Supervisor $supervisor; - public Atendimento $atendimento; + public $supervisor; + public $atendimento; public $timeout_agent_alert; public $timeout_agent_desconexao; public $timeout_agent_classificacao; @@ -55,9 +41,11 @@ class MonitoraAgente function monitora() { try { + sleep(1); $this->coremedia->setApi(new Positus()); $agentesLista = $this->supervisor->listaAgentesDisponivel(); foreach ($agentesLista as $item) { + //sleep(1); $atendimentos = $this->atendimento->getAtendFila($item->fila); if (count($atendimentos) > 0) { return $this->coremedia->criaAtendimento( @@ -67,9 +55,9 @@ class MonitoraAgente ); } } - } catch (Exception $ex) { - Connect::setInstance(null); + } catch (\Exception $ex) { logger('monitora')->info($ex->getMessage(), debug_backtrace()); + return false; } } @@ -79,7 +67,7 @@ class MonitoraAgente case CONF_TELEGRAM_CHANNEL: return new ApiTelegram(); case CONF_WHATSAPP_CHANNEL: - return new Positus();; + return new Positus(); default: break; } @@ -115,7 +103,7 @@ class MonitoraAgente ], ]; return json_encode($mensagem); - } catch (\Throwable $th) { + } catch (\Exception $th) { logger('monitora')->info($th->getMessage(), debug_backtrace()); } } diff --git a/websocket/Servidorsocket.php b/websocket/Servidorsocket.php index 6a69f51..1260c8f 100644 --- a/websocket/Servidorsocket.php +++ b/websocket/Servidorsocket.php @@ -2,63 +2,76 @@ namespace websocket; -use app\Providers\Positus; -use Ratchet\MessageComponentInterface; +include __DIR__ . '/../Providers/Positus.php'; + +use app\Middleware\ApiAgente; use Ratchet\ConnectionInterface; +use Ratchet\MessageComponentInterface; use service\MonitoraAgente; -use SplObjectStorage; class Servidorsocket implements MessageComponentInterface { - public SplObjectStorage $clients; - public array $connection = []; - public array $shell = []; - public array $conectado = []; - public array $idConexion = []; - public array $clientes = []; + public $clients; + public $connection = []; + public $shell = []; + public $conectado = []; + public $idConexion = []; + public $clientes = []; public $monitora; + public $api; public function __construct() { - $this->clients = new SplObjectStorage(); + + $this->clients = new \SplObjectStorage(); + $this->api = new ApiAgente(); $this->monitora = new MonitoraAgente(); } public function onOpen(ConnectionInterface $conn) { - $headers = $conn->httpRequest->getHeaders(); - $conn->send($conn->resourceId); - $this->clients->attach($conn); - $this->connection[$conn->resourceId] = null; - $this->shell[$conn->resourceId] = null; - $this->conectado[$conn->resourceId] = null; - $this->idConexion[$conn->resourceId] = null; try { - array_push($this->clientes, [ - "ramal" => $headers['ramal'][0], + $headers = $conn->httpRequest->getHeaders(); + $conn->send($conn->resourceId); + $this->clients->attach($conn); + $this->connection[$conn->resourceId] = null; + $this->shell[$conn->resourceId] = null; + $this->conectado[$conn->resourceId] = null; + $this->idConexion[$conn->resourceId] = null; + + $this->clientes[$conn->resourceId] = [ + "matricula" => 'testwe', "conection" => $conn - ]); - } catch (\Throwable $th) { - //throw $th; + ]; + } catch (\Exception $th) { + logger('ws')->debug(var_export($headers, true)); } } public function onMessage(ConnectionInterface $from, $msg) { try { - $retMoni = $this->monitora->monitora(); - logger('debuge')->debug($retMoni); - if ($retMoni) { + $mensagem = json_decode($msg, true); + logger('ws')->debug(var_export($mensagem, true)); + if ($mensagem['matricula']) { + $this->clientes[$from->resourceId]['matricula'] = $mensagem['matricula']; + } + if (trim($msg) != 'att') { foreach ($this->clientes as $key => $value) { - $value['conection']->send($retMoni); + if ($mensagem['event']['mensagem']['dst'] == $value['matricula']) { + $value['conection']->send($msg); + } } } - if (trim($msg) != 'att') { + $retMoni = $this->monitora->monitora(); + if ($retMoni) { foreach ($this->clientes as $key => $value) { - $value['conection']->send($msg); + if ($retMoni) { + $value['conection']->send($retMoni); + } } } - } catch (\Throwable $th) { + } catch (\Exception $th) { logger('debuge')->debug($th->getMessage()); } } @@ -69,6 +82,7 @@ class Servidorsocket implements MessageComponentInterface // The connection is closed, remove it, as we can no longer send it messages $this->conectado[$conn->resourceId] = false; $this->clients->detach($conn); + $this->api->logoff($this->clientes[$conn->resourceId]); } public function onError(ConnectionInterface $conn, \Exception $e) diff --git a/websocket/WsInterface.php b/websocket/WsInterface.php index 27b1190..83c9873 100644 --- a/websocket/WsInterface.php +++ b/websocket/WsInterface.php @@ -2,7 +2,7 @@ namespace websocket; -use WebSocket\Client; +use WebSocket\Client as Testess; class WsInterface { @@ -11,7 +11,7 @@ class WsInterface function enviaMsg($msg) { if (!empty($msg)) { - $this->client = new Client("ws://192.168.115.65:8090/ws"); + $this->client = new Testess("ws://192.168.115.65:8090/ws"); $this->client->send($msg); $this->client->close(); return null; diff --git a/websocket/websocket.php b/websocket/websocket.php index e47b4d2..b4bbe56 100644 --- a/websocket/websocket.php +++ b/websocket/websocket.php @@ -1,20 +1,42 @@ route('/ws', new Servidorsocket(), array('*')); + $app->run(); +} catch (\Exception $th) { + echo $th->getMessage(); +} + +$server->run(); + -$server = IoServer::factory( - new HttpServer( - new WsServer( - new Servidorsocket() - ) - ), - 8090 -); -$server->run(); \ No newline at end of file +// $loop = Factory::create(); +// $webSock = new SecureServer( +// new Server('0.0.0.0:8090', $loop), +// $loop, +// array( +// 'local_cert' => '/etc/letsencrypt//fullchain.pem', // path to your cert +// 'local_pk' => '/etc/letsencrypt/privkey.pem', // path to your server private key +// 'allow_self_signed' => TRUE, // Allow self signed certs (should be false in production) +// 'verify_peer' => FALSE +// ) +// ); +// // Ratchet magic +// $webServer = new IoServer( +// new HttpServer( +// new WsServer( +// new Servidorsocket() +// ) +// ), +// $webSock +// ); +// $loop->run(); \ No newline at end of file From c9183a37750b680d0bcfc2e3d00d242ea804967f Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Thu, 6 Jan 2022 15:56:38 -0400 Subject: [PATCH 041/144] ajustes de versao --- app/Controllers/AgentController.php | 5 +++-- app/Core/Commands.php | 2 +- app/Middleware/ApiAgente.php | 2 +- app/Middleware/Middleware.php | 1 + app/Models/Agent.php | 28 ++++++++++++---------------- app/Models/Supervisor.php | 19 +++++++------------ index.php | 4 ++-- websocket/WsInterface.php | 2 +- websocket/websocket.php | 2 +- 9 files changed, 29 insertions(+), 36 deletions(-) diff --git a/app/Controllers/AgentController.php b/app/Controllers/AgentController.php index 855656d..be990a9 100644 --- a/app/Controllers/AgentController.php +++ b/app/Controllers/AgentController.php @@ -26,7 +26,7 @@ use websocket\WsInterface; class AgentController extends Controller { - private Supervisor $agent; + private $agent; private $queue; private $ramal; private $pause; @@ -189,7 +189,7 @@ class AgentController extends Controller } if ($agent->status != CONF_AGENT_STATUS_LIVRE) { - throw new Exception(''); + throw new Exception('Saia da pausa para fazer logoff'); } $this->agent->updateEventoLogoffAgent($agent->matricula, '0', $queue->id); @@ -200,6 +200,7 @@ class AgentController extends Controller $this->agent->rollback(); $this->message($ex->getMessage()); logger()->error($ex->getMessage()); + return $ex->getMessage(); } return false; } diff --git a/app/Core/Commands.php b/app/Core/Commands.php index 50bfa0c..9bdb786 100644 --- a/app/Core/Commands.php +++ b/app/Core/Commands.php @@ -283,7 +283,7 @@ class Commands $this->callback = CONF_NAME_REPONSE . " : Erro ao finalizar!"; return false; } - } catch (\Throwable $th) { + } catch (\Exception $th) { $this->callback = CONF_NAME_REPONSE . " : " . $th->getMessage(); return false; } diff --git a/app/Middleware/ApiAgente.php b/app/Middleware/ApiAgente.php index b9daa81..5b24db3 100644 --- a/app/Middleware/ApiAgente.php +++ b/app/Middleware/ApiAgente.php @@ -603,7 +603,7 @@ class ApiAgente ]; return json_encode($mensagem); } catch (\Exception $th) { - logger('monitora')->info($th->getMessage(), debug_backtrace()); + logger('monitora')->info($th->getMessage()); } } diff --git a/app/Middleware/Middleware.php b/app/Middleware/Middleware.php index 323444b..2fbbaac 100644 --- a/app/Middleware/Middleware.php +++ b/app/Middleware/Middleware.php @@ -51,6 +51,7 @@ class Middleware extends Http */ private function router() { + $coremedia = new CoreMedia(); $apiAgente = new ApiAgente(); $this->baseUri(); diff --git a/app/Models/Agent.php b/app/Models/Agent.php index d441a3d..e29071e 100644 --- a/app/Models/Agent.php +++ b/app/Models/Agent.php @@ -30,22 +30,18 @@ class Agent extends Model 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); + + $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 []; } } diff --git a/app/Models/Supervisor.php b/app/Models/Supervisor.php index f7b0cb1..98cab9f 100644 --- a/app/Models/Supervisor.php +++ b/app/Models/Supervisor.php @@ -79,19 +79,14 @@ class Supervisor extends Model public function findAllAgentes($queue = null) { - try { - $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(); - } catch (\Throwable $th) { - Connect::setInstance(null); - logger('testeOff')->info(gettype(Connect::getInstance()), debug_backtrace()); - return []; + + $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 findByAgent($matricula) diff --git a/index.php b/index.php index 97071db..0fc1835 100644 --- a/index.php +++ b/index.php @@ -1,9 +1,9 @@ api(); +$middle->api(); \ No newline at end of file diff --git a/websocket/WsInterface.php b/websocket/WsInterface.php index 83c9873..eb8bc9d 100644 --- a/websocket/WsInterface.php +++ b/websocket/WsInterface.php @@ -36,7 +36,7 @@ class WsInterface ], ]; return json_encode($mensagem); - } catch (\Throwable $th) { + } catch (\Exception $th) { logger('monitora')->info($th->getMessage(), debug_backtrace()); } } diff --git a/websocket/websocket.php b/websocket/websocket.php index b4bbe56..7dfea42 100644 --- a/websocket/websocket.php +++ b/websocket/websocket.php @@ -1,7 +1,7 @@ Date: Fri, 7 Jan 2022 09:33:18 -0400 Subject: [PATCH 042/144] filtrar pausas do sistema --- app/Models/Pause.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/Models/Pause.php b/app/Models/Pause.php index 4b1dca4..85814ad 100644 --- a/app/Models/Pause.php +++ b/app/Models/Pause.php @@ -83,7 +83,8 @@ class Pause extends Model 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 d.flag = :flag"; $data['flag'] = 1; @@ -99,7 +100,7 @@ class Pause extends Model $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(); } From 9e940c4a1d72b984adcc0025afc9a27a6ca66758 Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Fri, 7 Jan 2022 11:37:35 -0400 Subject: [PATCH 043/144] add metodo listarAtendimentosFilas na apis --- app/Middleware/ApiAgente.php | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/app/Middleware/ApiAgente.php b/app/Middleware/ApiAgente.php index 5b24db3..4cff522 100644 --- a/app/Middleware/ApiAgente.php +++ b/app/Middleware/ApiAgente.php @@ -89,6 +89,9 @@ class ApiAgente case 'statusAgente': $this->statusAgente($request); break; + case 'listarAtendimentosFilas': + $this->listarAtendimentosFilas($request); + break; default: echo json_encode(['status' => '404']); break; @@ -621,4 +624,25 @@ class ApiAgente } return $msg; } + + public function listarAtendimentosFilas($request) + { + try { + $filaModel = new Queue(); + $fila = $filaModel->findQueueById($request['id_fila']); + if (empty($fila)) { + $this->retorno("Fila nã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()); + } + } } \ No newline at end of file From d94c23bd40f1a21fcf69dfedde1e58ab40328e79 Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Tue, 11 Jan 2022 08:02:55 -0400 Subject: [PATCH 044/144] add nome do agente nas mensagens --- app/Middleware/ApiAgente.php | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/app/Middleware/ApiAgente.php b/app/Middleware/ApiAgente.php index 4cff522..f4adf03 100644 --- a/app/Middleware/ApiAgente.php +++ b/app/Middleware/ApiAgente.php @@ -249,7 +249,6 @@ class ApiAgente $ret ? ["id" => $ret] : null ); } catch (\Exception $th) { - $this->retorno($th->getMessage()); } return null; @@ -323,11 +322,11 @@ class ApiAgente if ($status->evento == CONF_EVENT_START) { $provider = new Positus(); $modelMensagem = new Message(); + $msgTexto = $contact['name'] . ': ' . $this->validaPalavroes($mensagem['content']); if ($mensagem['type'] == 'text') { - // logger('enviaMsg')->info(print_r($mensagem, true)); $retuno = $provider->enviarMsg( $mensagem['dst'], - $this->validaPalavroes($mensagem['content']) + $msgTexto ); } else { $anmeArquivo = __DIR__ . "/../../storage/files/" . $mensagem['id_provedor']; @@ -347,7 +346,7 @@ class ApiAgente $contact['number'], $mensagem['dst'], $mensagem['type'], - $mensagem['type'] == 'text' ? $mensagem['content'] : $mensagem['id_provedor'], + $mensagem['type'] == 'text' ? $msgTexto : $mensagem['id_provedor'], $contact['name'], $mensagem['media'], $mensagem['status'], From 0f9f7ad56262bdb04649fd8da6fad7fef6615288 Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Tue, 11 Jan 2022 08:21:12 -0400 Subject: [PATCH 045/144] ajustes da palavra finalizado para transferido --- app/Controllers/AgentController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Controllers/AgentController.php b/app/Controllers/AgentController.php index be990a9..6a81345 100644 --- a/app/Controllers/AgentController.php +++ b/app/Controllers/AgentController.php @@ -344,7 +344,7 @@ class AgentController extends Controller $ws->enviaMsg($ws->enviaActions('Atendimento transferido', 'transfer', $agentTransf->matricula, $uniqueid)); $ws->enviaMsg($ws->enviaActions('Atendimento transferido', 'transfer', $agent->matricula, $uniqueid)); $provedor = new Positus(); - $provedor->enviarMsg($atendAtual->cliente_id, CONF_NAME_REPONSE . ": Atendimento finalizado"); + $provedor->enviarMsg($atendAtual->cliente_id, CONF_NAME_REPONSE . ": Atendimento transferido"); $messegeModel = new Message(); $messegeModel->addMessage( $uniqueid, From 4cf1dff36b119d397341697db55817c542b484d8 Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Tue, 11 Jan 2022 08:22:02 -0400 Subject: [PATCH 046/144] add modulo de SystemMessage --- app/Controllers/SystemMessageController.php | 47 +++++++++++++++++++++ app/Models/SystemMessage.php | 22 ++++++++++ config/moments.php | 16 +++++++ database/att-v3.sql | 8 ++++ includes/config.php | 11 +++++ 5 files changed, 104 insertions(+) create mode 100644 app/Controllers/SystemMessageController.php create mode 100644 app/Models/SystemMessage.php create mode 100644 config/moments.php diff --git a/app/Controllers/SystemMessageController.php b/app/Controllers/SystemMessageController.php new file mode 100644 index 0000000..3a62121 --- /dev/null +++ b/app/Controllers/SystemMessageController.php @@ -0,0 +1,47 @@ +sysMessage = new SystemMessage(); + } + public function sendMessageSystem($momento, $variavels, IApiMedia $api) + { //$variavels = [["nome" => '@cliente', "valor" => 'afonso']] + try { + $msgs = $this->sysMessage->findMessage($momento); + foreach ($msgs as $key => $msg) { + foreach ($variavels as $key => $variavel) { + logger('debuge')->info(print_r($variavel, true)); + $vari = $variavel['nome']; + $pattern = "/$vari/i"; + $msg->texto = preg_replace($pattern, $variavel['valor'], $msg->texto); + } + $api->enviarMsg($api->getPhone(), utf8_encode($msg->texto)); + } + return $msgs; + } catch (Exception $ex) { + $this->supervisorqueue->rollback(); + logger()->error($ex->getMessage()); + return false; + } + } +} \ No newline at end of file diff --git a/app/Models/SystemMessage.php b/app/Models/SystemMessage.php new file mode 100644 index 0000000..c1bc4eb --- /dev/null +++ b/app/Models/SystemMessage.php @@ -0,0 +1,22 @@ +query = "SELECT * FROM {$this->table} m WHERE m.momento = :momento ORDER BY ordem;"; + return $this->read($this->query, ['momento' => $momento])->fetchAll(); + } +} \ No newline at end of file diff --git a/config/moments.php b/config/moments.php new file mode 100644 index 0000000..8e52b0a --- /dev/null +++ b/config/moments.php @@ -0,0 +1,16 @@ + Date: Tue, 11 Jan 2022 16:01:09 -0400 Subject: [PATCH 047/144] refatorando e adicionando melhorias --- app/Controllers/SystemMessageController.php | 19 +- app/Core/Commands.php | 388 ++++---------------- app/Core/CoreMedia.php | 132 ++----- 3 files changed, 120 insertions(+), 419 deletions(-) diff --git a/app/Controllers/SystemMessageController.php b/app/Controllers/SystemMessageController.php index 3a62121..5a2d5b9 100644 --- a/app/Controllers/SystemMessageController.php +++ b/app/Controllers/SystemMessageController.php @@ -4,9 +4,6 @@ namespace app\Controllers; use app\Core\Controller; use app\Interfaces\IApiMedia; -use app\Models\Queue; -use app\Models\EventQueue; -use app\Models\SupervisorQueue; use app\Models\SystemMessage; use Exception; @@ -24,18 +21,20 @@ class SystemMessageController extends Controller { $this->sysMessage = new SystemMessage(); } - public function sendMessageSystem($momento, $variavels, IApiMedia $api) + public function sendMessageSystem($momento, $variavels, IApiMedia $api, $numero) { //$variavels = [["nome" => '@cliente', "valor" => 'afonso']] try { $msgs = $this->sysMessage->findMessage($momento); foreach ($msgs as $key => $msg) { - foreach ($variavels as $key => $variavel) { - logger('debuge')->info(print_r($variavel, true)); - $vari = $variavel['nome']; - $pattern = "/$vari/i"; - $msg->texto = preg_replace($pattern, $variavel['valor'], $msg->texto); + if ($variavels) { + foreach ($variavels as $key => $variavel) { + $vari = $variavel['nome']; + $pattern = "/$vari/i"; + $msg->texto = preg_replace($pattern, $variavel['valor'], $msg->texto); + } } - $api->enviarMsg($api->getPhone(), utf8_encode($msg->texto)); + logger('debug')->info($msg->texto); + $api->enviarMsg($numero, $msg->texto); } return $msgs; } catch (Exception $ex) { diff --git a/app/Core/Commands.php b/app/Core/Commands.php index 9bdb786..76645e8 100644 --- a/app/Core/Commands.php +++ b/app/Core/Commands.php @@ -4,6 +4,8 @@ namespace app\Core; 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\Message; @@ -21,57 +23,20 @@ class Commands * 0 - CLIENTE & AGENTE * 1 - AGENTE */ - const PERMISSION = "PERMISSION"; const DESCRIPTION = "DESCRIPTION"; - private $personType; - private $callback; private $bilheteController; - private $agente; - static $instance; public $commandsConfig = array(); + private $api; + private $systemController; public function __construct() { $this->agente = new AgentController(); $this->bilheteController = new BilheteController(); + $this->systemController = new SystemMessageController(); $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." @@ -83,11 +48,6 @@ class Commands ]; } - private function permission($method) - { - return $this->commandsConfig[strtoupper($method)][self::PERMISSION] ? $this->commandsConfig[strtoupper($method)][self::PERMISSION] : ["MIX" => 0, "MAX" => 1]; - } - /* * Verificar se é um comando valido * @param string $phone @@ -95,298 +55,108 @@ class Commands * * 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 '/FINALIZAR': - return $this->finalizar($phone, $media); + 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) - { - 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!"; - 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'; - // } - // $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) + private function finalizar($numero) { - 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"; - } - $this->callback = $agentesStatus; - + try { + $atendimentoModel = new Atendimento(); + $eventosModel = new Evento(); + $supervisorModel = new Supervisor(); + $atendimento = $atendimentoModel->getAtendimentoByCliente($numero, CONF_EVENT_START); + //verifica se existe atendimento + if (empty($atendimento)) { + $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 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__))) { - try { - $atendimentoModel = new Atendimento(); - $eventosModel = new Evento(); - $supervisorModel = new Supervisor(); - $atendimento = $atendimentoModel->getAtendimentoByCliente($numero, CONF_EVENT_START); - //verifica se existe atendimento - if (empty($atendimento)) { - $this->callback = CONF_NAME_REPONSE . " : Não foi encontrado atendimento em aberto"; - return false; - } - $ret = $eventosModel->createEvento( + $ret = $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); + $ws->enviaMsg($ws->enviaActions('Atendimento finalizado', 'finish', $agente->matricula, $atendimento->uniqueid)); + $messegeModel = new Message(); + $messegeModel->addMessage( $atendimento->uniqueid, - CONF_EVENT_TIMERMINO_CLIENTE, - date('Y-m-d H:i:s'), - date('Y-m-d H:i:s'), - $atendimento->fila + $numero, + $agente->matricula, + 'finish', + 'Atendimento finalizado', + $atendimento->nome, + $atendimento->context, + 'read' ); - if ($ret) { - $ws = new WsInterface(); - $agente = $supervisorModel->findAgentByMatricula($atendimento->matricula); - $ws->enviaMsg($ws->enviaActions('Atendimento finalizado', 'finish', $agente->matricula, $atendimento->uniqueid)); - $messegeModel = new Message(); - $messegeModel->addMessage( - $atendimento->uniqueid, - $numero, - $agente->matricula, - 'finish', - 'Atendimento finalizado', - $atendimento->nome, - $atendimento->context, - 'read' - ); - if ($agente->status == CONF_AGENT_STATUS_INDISPONIVEL) { - $atends = $atendimentoModel->getAtendimentoAbertoByAgente($atendimento->matricula); - if (empty($atends)) { - $agente->enterPause($atendimento->matricula, $agente->motivo_pausa); - } + if ($agente->status == CONF_AGENT_STATUS_INDISPONIVEL) { + $atends = $atendimentoModel->getAtendimentoAbertoByAgente($atendimento->matricula); + if (empty($atends)) { + $agente->enterPause($atendimento->matricula, $agente->motivo_pausa); } - if ($agente->status == CONF_AGENT_STATUS_OCUPADO) { - $param = $atendimentoModel->getQuantiAtendimentSimultaneos(); - $atendimentosAbertos = $atendimentoModel->getAtendimentoAbertoByAgente($agente->matricula); - if (count($atendimentosAbertos) < $param->prm_media_simultaneo) { - $supervisorModel->updateAgent($agente->matricula, CONF_AGENT_STATUS_LIVRE); - } + } + if ($agente->status == CONF_AGENT_STATUS_OCUPADO) { + $param = $atendimentoModel->getQuantiAtendimentSimultaneos(); + $atendimentosAbertos = $atendimentoModel->getAtendimentoAbertoByAgente($agente->matricula); + if (count($atendimentosAbertos) < $param->prm_media_simultaneo) { + $supervisorModel->updateAgent($agente->matricula, CONF_AGENT_STATUS_LIVRE); } - $this->callback = CONF_NAME_REPONSE . " : Atendimento finalizado"; - return true; - } else { - $this->callback = CONF_NAME_REPONSE . " : Erro ao finalizar!"; - return false; } - } catch (\Exception $th) { - $this->callback = CONF_NAME_REPONSE . " : " . $th->getMessage(); - 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->systemController->sendMessageSystem( + CONF_MOMENT_FINALIZAR_ATENDIMENTO, + [], + $this->api, + $numero + ); return true; } else { - $this->callback = CONF_NAME_REPONSE . " : Não foi possível confirmar presença!"; - return false; + $this->api->enviarMsg($numero, CONF_NAME_REPONSE . " : Erro ao finalizar!"); + return true; } - } else { - $this->callback = CONF_NAME_REPONSE . " : Comando não permitido!"; - return false; - } - } - - 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; - } - } else { - $this->callback = CONF_NAME_REPONSE . " : Comando não permitido!"; - return false; + } catch (\Exception $th) { + $this->api->enviarMsg($numero, CONF_NAME_REPONSE . " : " . $th->getMessage()); + return true; } } private function cancelar($numero) { - if ($this->isPermitted($this->personType, $this->permission(__FUNCTION__))) { - $atendimentoModel = new Atendimento(); - $eventosModel = new Evento(); - $atendimento = $atendimentoModel->getAtendimentoByCliente($numero, CONF_EVENT_ESPERA); - $ret = $eventosModel->createEvento( - $atendimento->uniqueid, - CONF_EVENT_ABANDONADA, - date('Y-m-d H:i:s'), - date('Y-m-d H:i:s'), - $atendimento->fila + $atendimentoModel = new Atendimento(); + $eventosModel = new Evento(); + $atendimento = $atendimentoModel->getAtendimentoByCliente($numero, CONF_EVENT_ESPERA); + $ret = $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) { + $this->systemController->sendMessageSystem( + CONF_MOMENT_CANCELAR_FILA, + [], + $this->api, + $numero ); - if ($ret) { - $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; - } + return true; } 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; } } \ No newline at end of file diff --git a/app/Core/CoreMedia.php b/app/Core/CoreMedia.php index 0c88c95..6c7a0c1 100644 --- a/app/Core/CoreMedia.php +++ b/app/Core/CoreMedia.php @@ -8,13 +8,13 @@ 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\Supervisor; -use app\Providers\Positus; use websocket\WsInterface; /** @@ -37,6 +37,7 @@ class CoreMedia private $host; private $bilheteController; private $message; + private $systemController; public function __construct() { @@ -51,6 +52,7 @@ class CoreMedia $this->supervisor = new Supervisor(); $this->eventos = new Evento(); $this->bilheteController = new BilheteController; + $this->systemController = new SystemMessageController(); } public function setApi($api) { @@ -68,43 +70,10 @@ class CoreMedia $this->api = $providere; $this->api->setHook($this->request); if (!$this->api->baixarMidia()) { - $this->api->enviarMsg($this->api->getPhone(), utf8_encode("Não foi possivel entregar o aquivo para o agente. Envie novamente!")); + $this->api->enviarMsg($this->api->getPhone(), "Não foi possivel entregar o aquivo para o agente. Envie novamente!"); } // 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())); - 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; - } - 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())); - } + if ($this->commands->isCommand($this->api)) { return null; } //verifica se tem atendimento em aberto, se tiver ja manda msg para o agente via ws @@ -148,7 +117,14 @@ class CoreMedia //verifica se o cliente escolheu uma fila caso não escolheu manda as filas $filas = $this->queue->listAllQueueWhatsApp($this->api->getMessage()); if ($filas['LIST']) { - $this->api->enviarMsg($this->api->getPhone(), utf8_encode($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; } // cria a atendimento @@ -185,71 +161,68 @@ class CoreMedia $this->request['messages'][0]['text']['body'] = $msg; } } catch (\Exception $th) { - logger('telegram')->info(print_r($th, true), true); } return; } public function criaAtendimento($fila, $numero, $uniqueid = null) { try { - logger('core')->debug('criaAtendimento_1: '); 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); } } - logger('core')->debug('criaAtendimento_2: '); $agentes = $this->agente->infoAgentes($fila); if (empty($agentes)) { - $this->api->enviarMsg( - $numero, - utf8_encode('Não temos nenhum atendente disponível no momento, iremos lhe atender assim que um atendente estiver disponível!') + $this->systemController->sendMessageSystem( + CONF_MOMENT_ENTRAR_FILA_SEM, + [], + $this->api, + $numero ); return null; } - logger('core')->debug('criaAtendimento_3: '); $agent = $this->supervisor->findAgentByQueue($fila, 'LIVRE'); if (empty($agent)) { - $this->api->enviarMsg( - $numero, - utf8_encode('Nossos atendentes estão ocupados, por favor aguarde que iremos lhe atender!') + $this->systemController->sendMessageSystem( + CONF_MOMENT_ENTRAR_FILA_COM, + [], + $this->api, + $numero ); $this->mostraPosiscaoNaFila($numero, $fila); return null; } - logger('core')->debug('criaAtendimento_4: '); $retServe = $this->atendimento->updAtendimento($uniqueid, $agent[0]->matricula); if (empty($retServe)) { $this->api->enviarMsg( $numero, - utf8_encode('Erro ao gerar atendimento!') + 'Erro ao gerar atendimento!' ); return null; } - logger('core')->debug('criaAtendimento_5: '); $protocol = $this->bilheteController->generateProtocol($uniqueid); //cria o evento de inicio de atendimento e criar o protocolo if ($protocol) { - logger('core')->debug('criaAtendimento_6: '); $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)) { - logger('core')->debug('criaAtendimento_7: '); $atendimentosAbertos = $this->atendimento->getAtendimentoAbertoByAgente($agent[0]->matricula); $sup = new Supervisor(); $param = $this->atendimento->getQuantiAtendimentSimultaneos(); $ws = new WsInterface(); - logger('core')->debug('criaAtendimento_8: '); if ((count($atendimentosAbertos)) >= $param->prm_media_simultaneo) { $sup->updateAgent($agent[0]->matricula, CONF_AGENT_STATUS_OCUPADO); } - $this->api->enviarMsg( - $numero, - utf8_encode("Atendimento iniciado!\n") + $this->systemController->sendMessageSystem( + CONF_MOMENT_INICIAR_ATENDIMENTO, + [], + $this->api, + $numero ); $this->api->enviarMsg( $numero, - utf8_encode("Número do protocolo do atendimento é $protocol\n") + "Número do protocolo do atendimento é $protocol\n" ); try { $ws->enviaMsg($ws->enviaActions('Atendimento iniciado!', 'start', $agent[0]->matricula, $uniqueid)); @@ -258,7 +231,6 @@ class CoreMedia } } } - logger('core')->debug('criaAtendimento_final: '); return; } catch (\Exception $th) { logger('criaAtendimento')->info($th->getMessage()); @@ -266,22 +238,11 @@ class CoreMedia } } - private function enviaLista($listas, $api, $numero, $nome, $pre = '') - { - $api->enviarMsgIterativaLista( - $numero, - "$nome disponíveis", - "$nome", - $listas, - $pre - ); - } - public function mostraPosiscaoNaFila($numero, $fila) { $clientfila = $this->queue->clientQueueVerify($numero, $fila); if ($clientfila['MESSAGE']) { - $this->api->enviarMsg($numero, utf8_encode($clientfila['MESSAGE'])); + $this->api->enviarMsg($numero, $clientfila['MESSAGE']); return null; } } @@ -299,54 +260,25 @@ class CoreMedia 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; - } - - public function retornaConteudo($atendiment = null) + 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 $this->api->getMessage(); case 'image': - //$this->api->enviaImagem($response['DATA']['ramal'], $this->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 $this->api->getLinkDownload($this->host); 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 $this->api->getLinkDownload($this->host); 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 $this->api->getLinkDownload($this->host); 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 $this->api->getLinkDownload($this->host); 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 $this->api->getLinkDownload($this->host); 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; } } From 5a1659552895104f2151eb55c4ce7df9e019aba9 Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Tue, 11 Jan 2022 16:01:33 -0400 Subject: [PATCH 048/144] add moments --- app/Middleware/ApiAgente.php | 9 ++++++++- config/moments.php | 5 ++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/app/Middleware/ApiAgente.php b/app/Middleware/ApiAgente.php index f4adf03..4650e67 100644 --- a/app/Middleware/ApiAgente.php +++ b/app/Middleware/ApiAgente.php @@ -5,6 +5,7 @@ namespace app\Middleware; use app\Controllers\AgentController; use app\Controllers\ClassificacaoController; use app\Controllers\QueueController; +use app\Controllers\SystemMessageController; use app\Models\Atendimento; use app\Models\Evento; use app\Models\ListaNegraPalavras; @@ -216,8 +217,14 @@ class ApiAgente ); $ws = new WsInterface(); $ws->enviaMsg($this->enviaActions('Atendimento finalizado', 'finish', $agente->matricula, $request['uniqueid'])); + $systemController = new SystemMessageController(); $provedor = new Positus(); - $provedor->enviarMsg($atendimento->cliente_id, utf8_encode(CONF_NAME_REPONSE . " : Atendimento finalizado")); + $systemController->sendMessageSystem( + CONF_MOMENT_FINALIZAR_ATENDIMENTO, + [], + $provedor, + $atendimento->cliente_id + ); $messegeModel = new Message(); $messegeModel->addMessage( $request['uniqueid'], diff --git a/config/moments.php b/config/moments.php index 8e52b0a..839d022 100644 --- a/config/moments.php +++ b/config/moments.php @@ -13,4 +13,7 @@ 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'); -define("CONF_MOMENT_ENTRAR_FILA", 'ENTRAR_FILA'); \ No newline at end of file +//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'); \ No newline at end of file From 7eff988e1264c0ea065429b9a019846c373194df Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Tue, 11 Jan 2022 16:01:58 -0400 Subject: [PATCH 049/144] add utf8_enconde no enviamsg --- app/Interfaces/IApiMedia.php | 2 +- app/Providers/Positus.php | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/app/Interfaces/IApiMedia.php b/app/Interfaces/IApiMedia.php index a8af7cd..d54b0ff 100644 --- a/app/Interfaces/IApiMedia.php +++ b/app/Interfaces/IApiMedia.php @@ -5,7 +5,7 @@ namespace app\Interfaces; interface IApiMedia { function getchannel(); - function enviarMsg($whatsapp, $mensagem); + function enviarMsg($whatsapp, $mensagem, $encode = true); function enviarMsgIterativaLista($whatsapp, $mensagem, $nomeButton, $sections); function enviarMsgIterativaBotao($whatsapp, $mensagem, $buttons); function enviarContato($whatsapp, $nome, $contato); diff --git a/app/Providers/Positus.php b/app/Providers/Positus.php index 94cf463..0be34ed 100644 --- a/app/Providers/Positus.php +++ b/app/Providers/Positus.php @@ -92,9 +92,11 @@ class Positus implements IApiMedia return $this->exec(); } - function enviarMsg($whatsapp, $mensagem) + function enviarMsg($whatsapp, $mensagem, $encode = true) { - + if ($encode) { + $mensagem = utf8_encode($mensagem); + } $this->debug = debug_backtrace(); $this->params = array( "to" => "+$whatsapp", From 77b7f05ed8a3191d82a915a4888e08b8ca07c109 Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Tue, 18 Jan 2022 10:04:38 -0400 Subject: [PATCH 050/144] add class de timeaout client --- app/Controllers/MessageController.php | 9 +- app/Core/Commands.php | 7 +- app/Models/Atendimento.php | 13 +- composer.lock | 1417 ------------------------- index.php | 2 +- 5 files changed, 20 insertions(+), 1428 deletions(-) delete mode 100644 composer.lock diff --git a/app/Controllers/MessageController.php b/app/Controllers/MessageController.php index 59d5012..5529571 100644 --- a/app/Controllers/MessageController.php +++ b/app/Controllers/MessageController.php @@ -27,23 +27,22 @@ class MessageController extends Controller * @param type $timer * @return boolean */ - public function timeoutTalk($uniqueid, $ramal) + public function timeoutTalk($uniqueid) { try { $message = $this->message->findLastMessage($uniqueid); - if ($message->src != $ramal) { - return null; - } 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"; } - + print('nada'); return false; } catch (Exception $ex) { $this->message->rollback(); diff --git a/app/Core/Commands.php b/app/Core/Commands.php index 76645e8..93de1a7 100644 --- a/app/Core/Commands.php +++ b/app/Core/Commands.php @@ -55,6 +55,11 @@ class Commands * * return string */ + + public function setApi(IApiMedia $api) + { + $this->api = $api; + } public function isCommand(IApiMedia $api) { $this->api = $api; @@ -70,7 +75,7 @@ class Commands } } - private function finalizar($numero) + public function finalizar($numero) { try { $atendimentoModel = new Atendimento(); diff --git a/app/Models/Atendimento.php b/app/Models/Atendimento.php index b92b436..dd40b17 100644 --- a/app/Models/Atendimento.php +++ b/app/Models/Atendimento.php @@ -61,13 +61,18 @@ class Atendimento extends Model return $this->read($this->query, ['matricula' => $matricula])->fetchAll(); } - public function findAtenEmAberto($cliente_id) + public function findAtenEmAberto($cliente_id = null) { $this->query = "SELECT ma.* FROM {$this->atendimento} ma - 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) = 'START' + WHERE (SELECT m2.evento FROM md_evento m2 WHERE ma.uniqueid = m2.uniqueid ORDER BY id DESC LIMIT 1) = 'START' AND ma.matricula notnull "; - return $this->read($this->query, ['cliente_id' => $cliente_id])->fetch(); + $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) diff --git a/composer.lock b/composer.lock deleted file mode 100644 index bc2cd74..0000000 --- a/composer.lock +++ /dev/null @@ -1,1417 +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": "55a870c2fc1078fca8d21d42928df082", - "packages": [ - { - "name": "cboden/ratchet", - "version": "v0.4.3", - "source": { - "type": "git", - "url": "https://github.com/ratchetphp/Ratchet.git", - "reference": "466a0ecc83209c75b76645eb823401b5c52e5f21" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/ratchetphp/Ratchet/zipball/466a0ecc83209c75b76645eb823401b5c52e5f21", - "reference": "466a0ecc83209c75b76645eb823401b5c52e5f21", - "shasum": "" - }, - "require": { - "guzzlehttp/psr7": "^1.0", - "php": ">=5.4.2", - "ratchet/rfc6455": "^0.3", - "react/socket": "^1.0 || ^0.8 || ^0.7 || ^0.6 || ^0.5", - "symfony/http-foundation": "^2.6|^3.0|^4.0|^5.0", - "symfony/routing": "^2.6|^3.0|^4.0|^5.0" - }, - "require-dev": { - "phpunit/phpunit": "~4.8" - }, - "type": "library", - "autoload": { - "psr-4": { - "Ratchet\\": "src/Ratchet" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Chris Boden", - "email": "cboden@gmail.com", - "role": "Developer" - }, - { - "name": "Matt Bonneau", - "role": "Developer" - } - ], - "description": "PHP WebSocket library", - "homepage": "http://socketo.me", - "keywords": [ - "Ratchet", - "WebSockets", - "server", - "sockets", - "websocket" - ], - "support": { - "chat": "https://gitter.im/reactphp/reactphp", - "issues": "https://github.com/ratchetphp/Ratchet/issues", - "source": "https://github.com/ratchetphp/Ratchet/tree/master" - }, - "time": "2020-07-07T15:50:14+00:00" - }, - { - "name": "evenement/evenement", - "version": "v3.0.1", - "source": { - "type": "git", - "url": "https://github.com/igorw/evenement.git", - "reference": "531bfb9d15f8aa57454f5f0285b18bec903b8fb7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/igorw/evenement/zipball/531bfb9d15f8aa57454f5f0285b18bec903b8fb7", - "reference": "531bfb9d15f8aa57454f5f0285b18bec903b8fb7", - "shasum": "" - }, - "require": { - "php": ">=7.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.0" - }, - "type": "library", - "autoload": { - "psr-0": { - "Evenement": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Igor Wiedler", - "email": "igor@wiedler.ch" - } - ], - "description": "Événement is a very simple event dispatching library for PHP", - "keywords": [ - "event-dispatcher", - "event-emitter" - ], - "support": { - "issues": "https://github.com/igorw/evenement/issues", - "source": "https://github.com/igorw/evenement/tree/master" - }, - "time": "2017-07-23T21:35:13+00:00" - }, - { - "name": "guzzlehttp/psr7", - "version": "1.8.3", - "source": { - "type": "git", - "url": "https://github.com/guzzle/psr7.git", - "reference": "1afdd860a2566ed3c2b0b4a3de6e23434a79ec85" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/1afdd860a2566ed3c2b0b4a3de6e23434a79ec85", - "reference": "1afdd860a2566ed3c2b0b4a3de6e23434a79ec85", - "shasum": "" - }, - "require": { - "php": ">=5.4.0", - "psr/http-message": "~1.0", - "ralouphie/getallheaders": "^2.0.5 || ^3.0.0" - }, - "provide": { - "psr/http-message-implementation": "1.0" - }, - "require-dev": { - "ext-zlib": "*", - "phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.8 || ^9.3.10" - }, - "suggest": { - "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.7-dev" - } - }, - "autoload": { - "psr-4": { - "GuzzleHttp\\Psr7\\": "src/" - }, - "files": [ - "src/functions_include.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Graham Campbell", - "email": "hello@gjcampbell.co.uk", - "homepage": "https://github.com/GrahamCampbell" - }, - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - }, - { - "name": "George Mponos", - "email": "gmponos@gmail.com", - "homepage": "https://github.com/gmponos" - }, - { - "name": "Tobias Nyholm", - "email": "tobias.nyholm@gmail.com", - "homepage": "https://github.com/Nyholm" - }, - { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com", - "homepage": "https://github.com/sagikazarmark" - }, - { - "name": "Tobias Schultze", - "email": "webmaster@tubo-world.de", - "homepage": "https://github.com/Tobion" - } - ], - "description": "PSR-7 message implementation that also provides common utility methods", - "keywords": [ - "http", - "message", - "psr-7", - "request", - "response", - "stream", - "uri", - "url" - ], - "support": { - "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/1.8.3" - }, - "funding": [ - { - "url": "https://github.com/GrahamCampbell", - "type": "github" - }, - { - "url": "https://github.com/Nyholm", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", - "type": "tidelift" - } - ], - "time": "2021-10-05T13:56:00+00:00" - }, - { - "name": "psr/http-message", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/php-fig/http-message.git", - "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", - "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Http\\Message\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" - } - ], - "description": "Common interface for HTTP messages", - "homepage": "https://github.com/php-fig/http-message", - "keywords": [ - "http", - "http-message", - "psr", - "psr-7", - "request", - "response" - ], - "support": { - "source": "https://github.com/php-fig/http-message/tree/master" - }, - "time": "2016-08-06T14:39:51+00:00" - }, - { - "name": "psr/log", - "version": "1.1.4", - "source": { - "type": "git", - "url": "https://github.com/php-fig/log.git", - "reference": "d49695b909c3b7628b6289db5479a1c204601f11" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", - "reference": "d49695b909c3b7628b6289db5479a1c204601f11", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Log\\": "Psr/Log/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common interface for logging libraries", - "homepage": "https://github.com/php-fig/log", - "keywords": [ - "log", - "psr", - "psr-3" - ], - "support": { - "source": "https://github.com/php-fig/log/tree/1.1.4" - }, - "time": "2021-05-03T11:20:27+00:00" - }, - { - "name": "ralouphie/getallheaders", - "version": "3.0.3", - "source": { - "type": "git", - "url": "https://github.com/ralouphie/getallheaders.git", - "reference": "120b605dfeb996808c31b6477290a714d356e822" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", - "reference": "120b605dfeb996808c31b6477290a714d356e822", - "shasum": "" - }, - "require": { - "php": ">=5.6" - }, - "require-dev": { - "php-coveralls/php-coveralls": "^2.1", - "phpunit/phpunit": "^5 || ^6.5" - }, - "type": "library", - "autoload": { - "files": [ - "src/getallheaders.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ralph Khattar", - "email": "ralph.khattar@gmail.com" - } - ], - "description": "A polyfill for getallheaders.", - "support": { - "issues": "https://github.com/ralouphie/getallheaders/issues", - "source": "https://github.com/ralouphie/getallheaders/tree/develop" - }, - "time": "2019-03-08T08:55:37+00:00" - }, - { - "name": "ratchet/rfc6455", - "version": "v0.3", - "source": { - "type": "git", - "url": "https://github.com/ratchetphp/RFC6455.git", - "reference": "c8651c7938651c2d55f5d8c2422ac5e57a183341" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/ratchetphp/RFC6455/zipball/c8651c7938651c2d55f5d8c2422ac5e57a183341", - "reference": "c8651c7938651c2d55f5d8c2422ac5e57a183341", - "shasum": "" - }, - "require": { - "guzzlehttp/psr7": "^1.0", - "php": ">=5.4.2" - }, - "require-dev": { - "phpunit/phpunit": "5.7.*", - "react/socket": "^1.3" - }, - "type": "library", - "autoload": { - "psr-4": { - "Ratchet\\RFC6455\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Chris Boden", - "email": "cboden@gmail.com", - "role": "Developer" - }, - { - "name": "Matt Bonneau", - "role": "Developer" - } - ], - "description": "RFC6455 WebSocket protocol handler", - "homepage": "http://socketo.me", - "keywords": [ - "WebSockets", - "rfc6455", - "websocket" - ], - "support": { - "chat": "https://gitter.im/reactphp/reactphp", - "issues": "https://github.com/ratchetphp/RFC6455/issues", - "source": "https://github.com/ratchetphp/RFC6455/tree/v0.3" - }, - "time": "2020-05-15T18:31:24+00:00" - }, - { - "name": "react/cache", - "version": "v1.1.1", - "source": { - "type": "git", - "url": "https://github.com/reactphp/cache.git", - "reference": "4bf736a2cccec7298bdf745db77585966fc2ca7e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/reactphp/cache/zipball/4bf736a2cccec7298bdf745db77585966fc2ca7e", - "reference": "4bf736a2cccec7298bdf745db77585966fc2ca7e", - "shasum": "" - }, - "require": { - "php": ">=5.3.0", - "react/promise": "^3.0 || ^2.0 || ^1.1" - }, - "require-dev": { - "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35" - }, - "type": "library", - "autoload": { - "psr-4": { - "React\\Cache\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Christian Lück", - "email": "christian@clue.engineering", - "homepage": "https://clue.engineering/" - }, - { - "name": "Cees-Jan Kiewiet", - "email": "reactphp@ceesjankiewiet.nl", - "homepage": "https://wyrihaximus.net/" - }, - { - "name": "Jan Sorgalla", - "email": "jsorgalla@gmail.com", - "homepage": "https://sorgalla.com/" - }, - { - "name": "Chris Boden", - "email": "cboden@gmail.com", - "homepage": "https://cboden.dev/" - } - ], - "description": "Async, Promise-based cache interface for ReactPHP", - "keywords": [ - "cache", - "caching", - "promise", - "reactphp" - ], - "support": { - "issues": "https://github.com/reactphp/cache/issues", - "source": "https://github.com/reactphp/cache/tree/v1.1.1" - }, - "funding": [ - { - "url": "https://github.com/WyriHaximus", - "type": "github" - }, - { - "url": "https://github.com/clue", - "type": "github" - } - ], - "time": "2021-02-02T06:47:52+00:00" - }, - { - "name": "react/dns", - "version": "v1.8.0", - "source": { - "type": "git", - "url": "https://github.com/reactphp/dns.git", - "reference": "2a5a74ab751e53863b45fb87e1d3913884f88248" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/reactphp/dns/zipball/2a5a74ab751e53863b45fb87e1d3913884f88248", - "reference": "2a5a74ab751e53863b45fb87e1d3913884f88248", - "shasum": "" - }, - "require": { - "php": ">=5.3.0", - "react/cache": "^1.0 || ^0.6 || ^0.5", - "react/event-loop": "^1.2", - "react/promise": "^3.0 || ^2.7 || ^1.2.1", - "react/promise-timer": "^1.2" - }, - "require-dev": { - "clue/block-react": "^1.2", - "phpunit/phpunit": "^9.3 || ^4.8.35" - }, - "type": "library", - "autoload": { - "psr-4": { - "React\\Dns\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Christian Lück", - "email": "christian@clue.engineering", - "homepage": "https://clue.engineering/" - }, - { - "name": "Cees-Jan Kiewiet", - "email": "reactphp@ceesjankiewiet.nl", - "homepage": "https://wyrihaximus.net/" - }, - { - "name": "Jan Sorgalla", - "email": "jsorgalla@gmail.com", - "homepage": "https://sorgalla.com/" - }, - { - "name": "Chris Boden", - "email": "cboden@gmail.com", - "homepage": "https://cboden.dev/" - } - ], - "description": "Async DNS resolver for ReactPHP", - "keywords": [ - "async", - "dns", - "dns-resolver", - "reactphp" - ], - "support": { - "issues": "https://github.com/reactphp/dns/issues", - "source": "https://github.com/reactphp/dns/tree/v1.8.0" - }, - "funding": [ - { - "url": "https://github.com/WyriHaximus", - "type": "github" - }, - { - "url": "https://github.com/clue", - "type": "github" - } - ], - "time": "2021-07-11T12:40:34+00:00" - }, - { - "name": "react/event-loop", - "version": "v1.2.0", - "source": { - "type": "git", - "url": "https://github.com/reactphp/event-loop.git", - "reference": "be6dee480fc4692cec0504e65eb486e3be1aa6f2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/reactphp/event-loop/zipball/be6dee480fc4692cec0504e65eb486e3be1aa6f2", - "reference": "be6dee480fc4692cec0504e65eb486e3be1aa6f2", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35" - }, - "suggest": { - "ext-event": "~1.0 for ExtEventLoop", - "ext-pcntl": "For signal handling support when using the StreamSelectLoop", - "ext-uv": "* for ExtUvLoop" - }, - "type": "library", - "autoload": { - "psr-4": { - "React\\EventLoop\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Christian Lück", - "email": "christian@clue.engineering", - "homepage": "https://clue.engineering/" - }, - { - "name": "Cees-Jan Kiewiet", - "email": "reactphp@ceesjankiewiet.nl", - "homepage": "https://wyrihaximus.net/" - }, - { - "name": "Jan Sorgalla", - "email": "jsorgalla@gmail.com", - "homepage": "https://sorgalla.com/" - }, - { - "name": "Chris Boden", - "email": "cboden@gmail.com", - "homepage": "https://cboden.dev/" - } - ], - "description": "ReactPHP's core reactor event loop that libraries can use for evented I/O.", - "keywords": [ - "asynchronous", - "event-loop" - ], - "support": { - "issues": "https://github.com/reactphp/event-loop/issues", - "source": "https://github.com/reactphp/event-loop/tree/v1.2.0" - }, - "funding": [ - { - "url": "https://github.com/WyriHaximus", - "type": "github" - }, - { - "url": "https://github.com/clue", - "type": "github" - } - ], - "time": "2021-07-11T12:31:24+00:00" - }, - { - "name": "react/promise", - "version": "v2.8.0", - "source": { - "type": "git", - "url": "https://github.com/reactphp/promise.git", - "reference": "f3cff96a19736714524ca0dd1d4130de73dbbbc4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/reactphp/promise/zipball/f3cff96a19736714524ca0dd1d4130de73dbbbc4", - "reference": "f3cff96a19736714524ca0dd1d4130de73dbbbc4", - "shasum": "" - }, - "require": { - "php": ">=5.4.0" - }, - "require-dev": { - "phpunit/phpunit": "^7.0 || ^6.5 || ^5.7 || ^4.8.36" - }, - "type": "library", - "autoload": { - "psr-4": { - "React\\Promise\\": "src/" - }, - "files": [ - "src/functions_include.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jan Sorgalla", - "email": "jsorgalla@gmail.com" - } - ], - "description": "A lightweight implementation of CommonJS Promises/A for PHP", - "keywords": [ - "promise", - "promises" - ], - "support": { - "issues": "https://github.com/reactphp/promise/issues", - "source": "https://github.com/reactphp/promise/tree/v2.8.0" - }, - "time": "2020-05-12T15:16:56+00:00" - }, - { - "name": "react/promise-timer", - "version": "v1.7.0", - "source": { - "type": "git", - "url": "https://github.com/reactphp/promise-timer.git", - "reference": "607dd79990e32fcb402cb0a176b4a4be12f97e7c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/reactphp/promise-timer/zipball/607dd79990e32fcb402cb0a176b4a4be12f97e7c", - "reference": "607dd79990e32fcb402cb0a176b4a4be12f97e7c", - "shasum": "" - }, - "require": { - "php": ">=5.3", - "react/event-loop": "^1.2", - "react/promise": "^3.0 || ^2.7.0 || ^1.2.1" - }, - "require-dev": { - "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35" - }, - "type": "library", - "autoload": { - "psr-4": { - "React\\Promise\\Timer\\": "src/" - }, - "files": [ - "src/functions_include.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Christian Lück", - "email": "christian@clue.engineering", - "homepage": "https://clue.engineering/" - }, - { - "name": "Cees-Jan Kiewiet", - "email": "reactphp@ceesjankiewiet.nl", - "homepage": "https://wyrihaximus.net/" - }, - { - "name": "Jan Sorgalla", - "email": "jsorgalla@gmail.com", - "homepage": "https://sorgalla.com/" - }, - { - "name": "Chris Boden", - "email": "cboden@gmail.com", - "homepage": "https://cboden.dev/" - } - ], - "description": "A trivial implementation of timeouts for Promises, built on top of ReactPHP.", - "homepage": "https://github.com/reactphp/promise-timer", - "keywords": [ - "async", - "event-loop", - "promise", - "reactphp", - "timeout", - "timer" - ], - "support": { - "issues": "https://github.com/reactphp/promise-timer/issues", - "source": "https://github.com/reactphp/promise-timer/tree/v1.7.0" - }, - "funding": [ - { - "url": "https://github.com/WyriHaximus", - "type": "github" - }, - { - "url": "https://github.com/clue", - "type": "github" - } - ], - "time": "2021-07-11T13:08:51+00:00" - }, - { - "name": "react/socket", - "version": "v1.9.0", - "source": { - "type": "git", - "url": "https://github.com/reactphp/socket.git", - "reference": "aa6e3f8ebcd6dec3ad1ee92a449b4cc341994001" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/reactphp/socket/zipball/aa6e3f8ebcd6dec3ad1ee92a449b4cc341994001", - "reference": "aa6e3f8ebcd6dec3ad1ee92a449b4cc341994001", - "shasum": "" - }, - "require": { - "evenement/evenement": "^3.0 || ^2.0 || ^1.0", - "php": ">=5.3.0", - "react/dns": "^1.8", - "react/event-loop": "^1.2", - "react/promise": "^2.6.0 || ^1.2.1", - "react/promise-timer": "^1.4.0", - "react/stream": "^1.2" - }, - "require-dev": { - "clue/block-react": "^1.2", - "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35", - "react/promise-stream": "^1.2" - }, - "type": "library", - "autoload": { - "psr-4": { - "React\\Socket\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Christian Lück", - "email": "christian@clue.engineering", - "homepage": "https://clue.engineering/" - }, - { - "name": "Cees-Jan Kiewiet", - "email": "reactphp@ceesjankiewiet.nl", - "homepage": "https://wyrihaximus.net/" - }, - { - "name": "Jan Sorgalla", - "email": "jsorgalla@gmail.com", - "homepage": "https://sorgalla.com/" - }, - { - "name": "Chris Boden", - "email": "cboden@gmail.com", - "homepage": "https://cboden.dev/" - } - ], - "description": "Async, streaming plaintext TCP/IP and secure TLS socket server and client connections for ReactPHP", - "keywords": [ - "Connection", - "Socket", - "async", - "reactphp", - "stream" - ], - "support": { - "issues": "https://github.com/reactphp/socket/issues", - "source": "https://github.com/reactphp/socket/tree/v1.9.0" - }, - "funding": [ - { - "url": "https://github.com/WyriHaximus", - "type": "github" - }, - { - "url": "https://github.com/clue", - "type": "github" - } - ], - "time": "2021-08-03T12:37:06+00:00" - }, - { - "name": "react/stream", - "version": "v1.2.0", - "source": { - "type": "git", - "url": "https://github.com/reactphp/stream.git", - "reference": "7a423506ee1903e89f1e08ec5f0ed430ff784ae9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/reactphp/stream/zipball/7a423506ee1903e89f1e08ec5f0ed430ff784ae9", - "reference": "7a423506ee1903e89f1e08ec5f0ed430ff784ae9", - "shasum": "" - }, - "require": { - "evenement/evenement": "^3.0 || ^2.0 || ^1.0", - "php": ">=5.3.8", - "react/event-loop": "^1.2" - }, - "require-dev": { - "clue/stream-filter": "~1.2", - "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35" - }, - "type": "library", - "autoload": { - "psr-4": { - "React\\Stream\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Christian Lück", - "email": "christian@clue.engineering", - "homepage": "https://clue.engineering/" - }, - { - "name": "Cees-Jan Kiewiet", - "email": "reactphp@ceesjankiewiet.nl", - "homepage": "https://wyrihaximus.net/" - }, - { - "name": "Jan Sorgalla", - "email": "jsorgalla@gmail.com", - "homepage": "https://sorgalla.com/" - }, - { - "name": "Chris Boden", - "email": "cboden@gmail.com", - "homepage": "https://cboden.dev/" - } - ], - "description": "Event-driven readable and writable streams for non-blocking I/O in ReactPHP", - "keywords": [ - "event-driven", - "io", - "non-blocking", - "pipe", - "reactphp", - "readable", - "stream", - "writable" - ], - "support": { - "issues": "https://github.com/reactphp/stream/issues", - "source": "https://github.com/reactphp/stream/tree/v1.2.0" - }, - "funding": [ - { - "url": "https://github.com/WyriHaximus", - "type": "github" - }, - { - "url": "https://github.com/clue", - "type": "github" - } - ], - "time": "2021-07-11T12:37:55+00:00" - }, - { - "name": "symfony/deprecation-contracts", - "version": "v2.4.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "5f38c8804a9e97d23e0c8d63341088cd8a22d627" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/5f38c8804a9e97d23e0c8d63341088cd8a22d627", - "reference": "5f38c8804a9e97d23e0c8d63341088cd8a22d627", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "2.4-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" - } - }, - "autoload": { - "files": [ - "function.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "A generic function and convention to trigger deprecation notices", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v2.4.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2021-03-23T23:28:01+00:00" - }, - { - "name": "symfony/http-foundation", - "version": "v5.3.10", - "source": { - "type": "git", - "url": "https://github.com/symfony/http-foundation.git", - "reference": "9f34f02e8a5fdc7a56bafe011cea1ce97300e54c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/9f34f02e8a5fdc7a56bafe011cea1ce97300e54c", - "reference": "9f34f02e8a5fdc7a56bafe011cea1ce97300e54c", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1", - "symfony/polyfill-mbstring": "~1.1", - "symfony/polyfill-php80": "^1.16" - }, - "require-dev": { - "predis/predis": "~1.0", - "symfony/cache": "^4.4|^5.0", - "symfony/expression-language": "^4.4|^5.0", - "symfony/mime": "^4.4|^5.0" - }, - "suggest": { - "symfony/mime": "To use the file extension guesser" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\HttpFoundation\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Defines an object-oriented layer for the HTTP specification", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/http-foundation/tree/v5.3.10" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2021-10-11T15:41:55+00:00" - }, - { - "name": "symfony/polyfill-mbstring", - "version": "v1.23.1", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "9174a3d80210dca8daa7f31fec659150bbeabfc6" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9174a3d80210dca8daa7f31fec659150bbeabfc6", - "reference": "9174a3d80210dca8daa7f31fec659150bbeabfc6", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "suggest": { - "ext-mbstring": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.23-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Mbstring\\": "" - }, - "files": [ - "bootstrap.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for the Mbstring extension", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "mbstring", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.23.1" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2021-05-27T12:26:48+00:00" - }, - { - "name": "symfony/polyfill-php80", - "version": "v1.23.1", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "1100343ed1a92e3a38f9ae122fc0eb21602547be" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/1100343ed1a92e3a38f9ae122fc0eb21602547be", - "reference": "1100343ed1a92e3a38f9ae122fc0eb21602547be", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.23-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Php80\\": "" - }, - "files": [ - "bootstrap.php" - ], - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ion Bazan", - "email": "ion.bazan@gmail.com" - }, - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.23.1" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2021-07-28T13:41:28+00:00" - }, - { - "name": "symfony/routing", - "version": "v5.3.7", - "source": { - "type": "git", - "url": "https://github.com/symfony/routing.git", - "reference": "be865017746fe869007d94220ad3f5297951811b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/be865017746fe869007d94220ad3f5297951811b", - "reference": "be865017746fe869007d94220ad3f5297951811b", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1", - "symfony/polyfill-php80": "^1.16" - }, - "conflict": { - "doctrine/annotations": "<1.12", - "symfony/config": "<5.3", - "symfony/dependency-injection": "<4.4", - "symfony/yaml": "<4.4" - }, - "require-dev": { - "doctrine/annotations": "^1.12", - "psr/log": "^1|^2|^3", - "symfony/config": "^5.3", - "symfony/dependency-injection": "^4.4|^5.0", - "symfony/expression-language": "^4.4|^5.0", - "symfony/http-foundation": "^4.4|^5.0", - "symfony/yaml": "^4.4|^5.0" - }, - "suggest": { - "symfony/config": "For using the all-in-one router or any loader", - "symfony/expression-language": "For using expression matching", - "symfony/http-foundation": "For using a Symfony Request object", - "symfony/yaml": "For using the YAML loader" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Routing\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Maps an HTTP request to a set of configuration variables", - "homepage": "https://symfony.com", - "keywords": [ - "router", - "routing", - "uri", - "url" - ], - "support": { - "source": "https://github.com/symfony/routing/tree/v5.3.7" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2021-08-04T21:42:42+00:00" - }, - { - "name": "textalk/websocket", - "version": "1.5.5", - "source": { - "type": "git", - "url": "https://github.com/Textalk/websocket-php.git", - "reference": "846542f82658132cd36acb7a7e8ce0f03960c295" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Textalk/websocket-php/zipball/846542f82658132cd36acb7a7e8ce0f03960c295", - "reference": "846542f82658132cd36acb7a7e8ce0f03960c295", - "shasum": "" - }, - "require": { - "php": "^7.2 | ^8.0", - "psr/log": "^1 | ^2 | ^3" - }, - "require-dev": { - "php-coveralls/php-coveralls": "^2.0", - "phpunit/phpunit": "^8.0|^9.0", - "squizlabs/php_codesniffer": "^3.5" - }, - "type": "library", - "autoload": { - "psr-4": { - "WebSocket\\": "lib" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "ISC" - ], - "authors": [ - { - "name": "Fredrik Liljegren" - }, - { - "name": "Sören Jensen", - "email": "soren@abicart.se" - } - ], - "description": "WebSocket client and server", - "support": { - "issues": "https://github.com/Textalk/websocket-php/issues", - "source": "https://github.com/Textalk/websocket-php/tree/1.5.5" - }, - "time": "2021-08-07T10:21:40+00:00" - } - ], - "packages-dev": [], - "aliases": [], - "minimum-stability": "stable", - "stability-flags": [], - "prefer-stable": false, - "prefer-lowest": false, - "platform": [], - "platform-dev": [], - "plugin-api-version": "2.1.0" -} diff --git a/index.php b/index.php index 0fc1835..c9d18ef 100644 --- a/index.php +++ b/index.php @@ -1,6 +1,6 @@ Date: Tue, 18 Jan 2022 10:04:59 -0400 Subject: [PATCH 051/144] add teste de timeout --- tests/teste_timeout.php | 85 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 tests/teste_timeout.php diff --git a/tests/teste_timeout.php b/tests/teste_timeout.php new file mode 100644 index 0000000..bb0e0de --- /dev/null +++ b/tests/teste_timeout.php @@ -0,0 +1,85 @@ +positus = new Positus(); + $this->message = new MessageController(); + $this->notificaMedia = new NotificaMedia(); + $this->mensages = new Message(); + $this->atendimentos = new Atendimento(); + $this->command = new Commands(); + $this->mensagem = [ + "ALERTA_INATIVIDADE" => "Você será desconectado em " . ($this->timeout_agent_desconexao - $this->timeout_agent_alert) . " minutos, para permanecer em atendimento clique em '/presente'", + "ALERTA_DESCONECTADO" => "Você foi desconectado por inatividade!", + "ALERTA_CLIENTE" => "Olá, como podemos lhe ajudar!", + "TIMEOUT_CLIENT_INATIVIDADE" => "O tempo da conversa foi expirado, por favor volte novamente a fila para um novo atendimento!", + "TALK_FINISHED" => "O atendimento foi finalizado!", + "TALK_ALERT_FINISH" => "Por favor, volte a comunicação com nosso atendimento em 1 minuto ou atendimento será encerrado!" + ]; + } + public function inicia() + { + $this->command->setApi($this->positus); + while (true) { + $atendiss = $this->atendimentos->findAtenEmAberto(); + foreach ($atendiss as $value) { + $this->timeoutCliente($value->uniqueid, $value->cliente_id); + } + sleep(60); + } + } + private function timeoutCliente($uniqueid, $client) + { + echo "unique: $uniqueid - client: $client \n"; + if ($this->message->timeoutTalk($uniqueid) == 'FINISH') { + $this->positus->enviarMsg($client, $this->mensagem['TIMEOUT_CLIENT_INATIVIDADE']); + $this->command->finalizar($client); + return null; + } + if ($this->message->timeoutTalk($uniqueid, $client) == 'ALERT') { + $msg = $this->mensages->findLastMessage($uniqueid); + $retorno = $this->notificaMedia->verificaNotifica( + $uniqueid, + $client, + utf8_encode($this->mensagem['TALK_ALERT_FINISH']) . $msg->id + ); + if ($retorno->quant == 0) { + $this->notificaMedia->addNotifica( + $uniqueid, + $client, + utf8_encode($this->mensagem['TALK_ALERT_FINISH']) . $msg->id + ); + $this->positus->enviarMsg( + $client, + $this->mensagem['TALK_ALERT_FINISH'] + ); + } + } + } +} + +$sevice_timeout = new ServiceTimeout(); +$sevice_timeout->inicia(); \ No newline at end of file From ea1eb8f600d23215f90bc5f10f854846de2bf0d6 Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Tue, 25 Jan 2022 08:09:19 -0400 Subject: [PATCH 052/144] add evento de pausa no ws --- app/Middleware/ApiAgente.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/Middleware/ApiAgente.php b/app/Middleware/ApiAgente.php index 4650e67..d178dbc 100644 --- a/app/Middleware/ApiAgente.php +++ b/app/Middleware/ApiAgente.php @@ -240,6 +240,7 @@ class ApiAgente $atends = $this->atendimento->getAtendimentoAbertoByAgente($request['matricula']); if (empty($atends)) { $this->agentController->enterPause($request['matricula'], $agente->motivo_pausa); + $ws->enviaMsg($this->enviaActions('Agente em pausa', 'pausa', $agente->matricula, $request['uniqueid'])); } } if ($agente->status == CONF_AGENT_STATUS_OCUPADO) { From 03a056ef104ead34249d5daa92c097b20b174374 Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Tue, 25 Jan 2022 09:25:10 -0400 Subject: [PATCH 053/144] add interface de api --- app/Interfaces/IApi.php | 9 +++++++++ app/Middleware/ApiAgente.php | 3 ++- 2 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 app/Interfaces/IApi.php diff --git a/app/Interfaces/IApi.php b/app/Interfaces/IApi.php new file mode 100644 index 0000000..30cbab2 --- /dev/null +++ b/app/Interfaces/IApi.php @@ -0,0 +1,9 @@ + Date: Tue, 25 Jan 2022 09:25:37 -0400 Subject: [PATCH 054/144] add api de supervisor --- app/Middleware/ApiSupervisor.php | 63 ++++++++++++++++++++++++++++++++ app/Middleware/Middleware.php | 4 ++ 2 files changed, 67 insertions(+) create mode 100644 app/Middleware/ApiSupervisor.php diff --git a/app/Middleware/ApiSupervisor.php b/app/Middleware/ApiSupervisor.php new file mode 100644 index 0000000..b75461c --- /dev/null +++ b/app/Middleware/ApiSupervisor.php @@ -0,0 +1,63 @@ +supervisor = new Supervisor(); + } + + 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); + } +} \ No newline at end of file diff --git a/app/Middleware/Middleware.php b/app/Middleware/Middleware.php index 2fbbaac..1fd5320 100644 --- a/app/Middleware/Middleware.php +++ b/app/Middleware/Middleware.php @@ -54,6 +54,7 @@ class Middleware extends Http $coremedia = new CoreMedia(); $apiAgente = new ApiAgente(); + $apiSupervisor = new ApiSupervisor(); $this->baseUri(); // $ws = new WsInterface(); // $ws->enviaMsg(json_encode($this->request)); @@ -70,6 +71,9 @@ class Middleware extends Http case 'api': $apiAgente->router($this->param()[2], $this->request); return null; + case 'supervisor': + $apiSupervisor->router($this->param()[2], $this->request); + return null; case 'link': $this->header->fileTransfer($this->param()[2], "/var/www/html/storage/files/" . $this->param()[2], base64_decode($this->param()[3])); exit(0); From bcc344a7e48edcd143b4fdfceb0505ac919e97cd Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Tue, 25 Jan 2022 10:51:31 -0400 Subject: [PATCH 055/144] add path files --- app/Middleware/ApiAgente.php | 4 ++-- app/Middleware/Middleware.php | 9 +-------- app/Providers/Positus.php | 2 +- config/app.php | 6 ++++-- 4 files changed, 8 insertions(+), 13 deletions(-) diff --git a/app/Middleware/ApiAgente.php b/app/Middleware/ApiAgente.php index dbde10b..9f739fc 100644 --- a/app/Middleware/ApiAgente.php +++ b/app/Middleware/ApiAgente.php @@ -338,12 +338,12 @@ class ApiAgente implements IApi $msgTexto ); } else { - $anmeArquivo = __DIR__ . "/../../storage/files/" . $mensagem['id_provedor']; + $anmeArquivo = CONF_PATH_FILES . $mensagem['id_provedor']; $texto = base64_decode($mensagem['content']); file_put_contents($anmeArquivo, $texto); $retuno = $provider->enviarMedia( $mensagem['dst'], - 'http://voip.simplesip.com.br:8090/link/' . + CONF_MIDDLEWARE_LINKUPLOAD . $mensagem['id_provedor'] . '/' . base64_encode($mensagem['mimetype']), $mensagem['type'], diff --git a/app/Middleware/Middleware.php b/app/Middleware/Middleware.php index 1fd5320..8674dad 100644 --- a/app/Middleware/Middleware.php +++ b/app/Middleware/Middleware.php @@ -3,14 +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 websocket\WsInterface; /** * Description of WppController @@ -56,9 +52,6 @@ class Middleware extends Http $apiAgente = new ApiAgente(); $apiSupervisor = new ApiSupervisor(); $this->baseUri(); - // $ws = new WsInterface(); - // $ws->enviaMsg(json_encode($this->request)); - switch (strtolower($this->param()[1])) { case 'whatsapp': $coremedia->inicia($this->request, new Positus()); @@ -75,7 +68,7 @@ class Middleware extends Http $apiSupervisor->router($this->param()[2], $this->request); return null; case 'link': - $this->header->fileTransfer($this->param()[2], "/var/www/html/storage/files/" . $this->param()[2], base64_decode($this->param()[3])); + $this->header->fileTransfer($this->param()[2], CONF_PATH_FILES . $this->param()[2], base64_decode($this->param()[3])); exit(0); default: $this->header->redirect(); diff --git a/app/Providers/Positus.php b/app/Providers/Positus.php index 0be34ed..385f97b 100644 --- a/app/Providers/Positus.php +++ b/app/Providers/Positus.php @@ -15,7 +15,7 @@ class Positus implements IApiMedia private $request; private $params = array(); private $hook; - private $storage = "/var/www/html/aplicativo/media/storage/files/"; + private $storage = CONF_PATH_FILES; public $channel = CONF_WHATSAPP_CHANNEL; public $timeout_client_resposta = CONF_WHATSAPP_TIMEOUT_CLIENT_RESPOSTA; diff --git a/config/app.php b/config/app.php index 40d269f..447a377 100644 --- a/config/app.php +++ b/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/'); /* @@ -56,4 +56,6 @@ define('CONF_DB_PASSWD', ''); | Configuração geral de protocolo | */ -define('CONF_PROTOCOL_TENTATIVAS_GERAR', 5); \ No newline at end of file +define('CONF_PROTOCOL_TENTATIVAS_GERAR', 5); + +define('CONF_PATH_FILES', '/var/www/html/aplicativo/integracao/media/storage/files/'); \ No newline at end of file From 44058a1ffe5187d30575d33dfe9132be049123e8 Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Tue, 25 Jan 2022 11:26:55 -0400 Subject: [PATCH 056/144] add script para rodar testes --- tests/test_services.php | 16 ++++++++ tests/teste_timeout.php | 85 ----------------------------------------- 2 files changed, 16 insertions(+), 85 deletions(-) create mode 100644 tests/test_services.php delete mode 100644 tests/teste_timeout.php diff --git a/tests/test_services.php b/tests/test_services.php new file mode 100644 index 0000000..b8c2252 --- /dev/null +++ b/tests/test_services.php @@ -0,0 +1,16 @@ +run(); + $servce_queue->run(); + sleep(30); +} \ No newline at end of file diff --git a/tests/teste_timeout.php b/tests/teste_timeout.php deleted file mode 100644 index bb0e0de..0000000 --- a/tests/teste_timeout.php +++ /dev/null @@ -1,85 +0,0 @@ -positus = new Positus(); - $this->message = new MessageController(); - $this->notificaMedia = new NotificaMedia(); - $this->mensages = new Message(); - $this->atendimentos = new Atendimento(); - $this->command = new Commands(); - $this->mensagem = [ - "ALERTA_INATIVIDADE" => "Você será desconectado em " . ($this->timeout_agent_desconexao - $this->timeout_agent_alert) . " minutos, para permanecer em atendimento clique em '/presente'", - "ALERTA_DESCONECTADO" => "Você foi desconectado por inatividade!", - "ALERTA_CLIENTE" => "Olá, como podemos lhe ajudar!", - "TIMEOUT_CLIENT_INATIVIDADE" => "O tempo da conversa foi expirado, por favor volte novamente a fila para um novo atendimento!", - "TALK_FINISHED" => "O atendimento foi finalizado!", - "TALK_ALERT_FINISH" => "Por favor, volte a comunicação com nosso atendimento em 1 minuto ou atendimento será encerrado!" - ]; - } - public function inicia() - { - $this->command->setApi($this->positus); - while (true) { - $atendiss = $this->atendimentos->findAtenEmAberto(); - foreach ($atendiss as $value) { - $this->timeoutCliente($value->uniqueid, $value->cliente_id); - } - sleep(60); - } - } - private function timeoutCliente($uniqueid, $client) - { - echo "unique: $uniqueid - client: $client \n"; - if ($this->message->timeoutTalk($uniqueid) == 'FINISH') { - $this->positus->enviarMsg($client, $this->mensagem['TIMEOUT_CLIENT_INATIVIDADE']); - $this->command->finalizar($client); - return null; - } - if ($this->message->timeoutTalk($uniqueid, $client) == 'ALERT') { - $msg = $this->mensages->findLastMessage($uniqueid); - $retorno = $this->notificaMedia->verificaNotifica( - $uniqueid, - $client, - utf8_encode($this->mensagem['TALK_ALERT_FINISH']) . $msg->id - ); - if ($retorno->quant == 0) { - $this->notificaMedia->addNotifica( - $uniqueid, - $client, - utf8_encode($this->mensagem['TALK_ALERT_FINISH']) . $msg->id - ); - $this->positus->enviarMsg( - $client, - $this->mensagem['TALK_ALERT_FINISH'] - ); - } - } - } -} - -$sevice_timeout = new ServiceTimeout(); -$sevice_timeout->inicia(); \ No newline at end of file From c0c3c731f4bd99394e2b70988085c1c735a2f8ef Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Tue, 25 Jan 2022 11:27:26 -0400 Subject: [PATCH 057/144] add services e interface de service --- service/IService.php | 8 ++ .../{MonitoraAgente.php => ServiceQueue.php} | 9 +-- service/ServiceTimeout.php | 80 +++++++++++++++++++ 3 files changed, 91 insertions(+), 6 deletions(-) create mode 100644 service/IService.php rename service/{MonitoraAgente.php => ServiceQueue.php} (95%) create mode 100644 service/ServiceTimeout.php diff --git a/service/IService.php b/service/IService.php new file mode 100644 index 0000000..771e5f8 --- /dev/null +++ b/service/IService.php @@ -0,0 +1,8 @@ +coremedia->setApi(new Positus()); $agentesLista = $this->supervisor->listaAgentesDisponivel(); foreach ($agentesLista as $item) { - //sleep(1); $atendimentos = $this->atendimento->getAtendFila($item->fila); if (count($atendimentos) > 0) { - return $this->coremedia->criaAtendimento( + $this->coremedia->criaAtendimento( $atendimentos[0]->fila, $atendimentos[0]->cliente_id, $atendimentos[0]->uniqueid @@ -57,7 +55,6 @@ class MonitoraAgente } } catch (\Exception $ex) { logger('monitora')->info($ex->getMessage(), debug_backtrace()); - return false; } } diff --git a/service/ServiceTimeout.php b/service/ServiceTimeout.php new file mode 100644 index 0000000..7d78922 --- /dev/null +++ b/service/ServiceTimeout.php @@ -0,0 +1,80 @@ +positus = new Positus(); + $this->message = new MessageController(); + $this->notificaMedia = new NotificaMedia(); + $this->mensages = new Message(); + $this->atendimentos = new Atendimento(); + $this->command = new Commands(); + $this->mensagem = [ + "ALERTA_INATIVIDADE" => "Você será desconectado em " . ($this->timeout_agent_desconexao - $this->timeout_agent_alert) . " minutos, para permanecer em atendimento clique em '/presente'", + "ALERTA_DESCONECTADO" => "Você foi desconectado por inatividade!", + "ALERTA_CLIENTE" => "Olá, como podemos lhe ajudar!", + "TIMEOUT_CLIENT_INATIVIDADE" => "O tempo da conversa foi expirado, por favor volte novamente a fila para um novo atendimento!", + "TALK_FINISHED" => "O atendimento foi finalizado!", + "TALK_ALERT_FINISH" => "Por favor, volte a comunicação com nosso atendimento em 1 minuto ou atendimento será encerrado!" + ]; + } + public function run() + { + $this->command->setApi($this->positus); + // while (true) { + $atendiss = $this->atendimentos->findAtenEmAberto(); + foreach ($atendiss as $value) { + $this->timeoutCliente($value->uniqueid, $value->cliente_id); + } + sleep(60); + // } + } + private function timeoutCliente($uniqueid, $client) + { + echo "unique: $uniqueid - client: $client \n"; + if ($this->message->timeoutTalk($uniqueid) == 'FINISH') { + $this->positus->enviarMsg($client, $this->mensagem['TIMEOUT_CLIENT_INATIVIDADE']); + $this->command->finalizar($client); + return null; + } + if ($this->message->timeoutTalk($uniqueid, $client) == 'ALERT') { + $msg = $this->mensages->findLastMessage($uniqueid); + $retorno = $this->notificaMedia->verificaNotifica( + $uniqueid, + $client, + utf8_encode($this->mensagem['TALK_ALERT_FINISH']) . $msg->id + ); + if ($retorno->quant == 0) { + $this->notificaMedia->addNotifica( + $uniqueid, + $client, + utf8_encode($this->mensagem['TALK_ALERT_FINISH']) . $msg->id + ); + $this->positus->enviarMsg( + $client, + $this->mensagem['TALK_ALERT_FINISH'] + ); + } + } + } +} \ No newline at end of file From 53a8427995618712d3bb1ea2c2514f76c88596b2 Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Tue, 25 Jan 2022 11:28:15 -0400 Subject: [PATCH 058/144] remove fila do websocket --- app/Core/CoreMedia.php | 1 - websocket/Servidorsocket.php | 18 +++--------------- 2 files changed, 3 insertions(+), 16 deletions(-) diff --git a/app/Core/CoreMedia.php b/app/Core/CoreMedia.php index 6c7a0c1..38aa95c 100644 --- a/app/Core/CoreMedia.php +++ b/app/Core/CoreMedia.php @@ -107,7 +107,6 @@ class CoreMedia ); return null; } - $this->ws->enviaMsg('att'); //verifica se tem atendimento em espera, se tiver ja mostra a sua posição na fila $atende = $this->atendimento->getAtendimentoByCliente($this->api->getPhone()); if ($atende) { diff --git a/websocket/Servidorsocket.php b/websocket/Servidorsocket.php index 1260c8f..14c0105 100644 --- a/websocket/Servidorsocket.php +++ b/websocket/Servidorsocket.php @@ -7,7 +7,6 @@ include __DIR__ . '/../Providers/Positus.php'; use app\Middleware\ApiAgente; use Ratchet\ConnectionInterface; use Ratchet\MessageComponentInterface; -use service\MonitoraAgente; class Servidorsocket implements MessageComponentInterface { @@ -17,7 +16,6 @@ class Servidorsocket implements MessageComponentInterface public $conectado = []; public $idConexion = []; public $clientes = []; - public $monitora; public $api; public function __construct() @@ -25,7 +23,6 @@ class Servidorsocket implements MessageComponentInterface $this->clients = new \SplObjectStorage(); $this->api = new ApiAgente(); - $this->monitora = new MonitoraAgente(); } public function onOpen(ConnectionInterface $conn) @@ -44,7 +41,7 @@ class Servidorsocket implements MessageComponentInterface "conection" => $conn ]; } catch (\Exception $th) { - logger('ws')->debug(var_export($headers, true)); + logger('ws:onOpen')->debug(var_export($headers, true)); } } @@ -55,24 +52,15 @@ class Servidorsocket implements MessageComponentInterface logger('ws')->debug(var_export($mensagem, true)); if ($mensagem['matricula']) { $this->clientes[$from->resourceId]['matricula'] = $mensagem['matricula']; - } - if (trim($msg) != 'att') { + } else { foreach ($this->clientes as $key => $value) { if ($mensagem['event']['mensagem']['dst'] == $value['matricula']) { $value['conection']->send($msg); } } } - $retMoni = $this->monitora->monitora(); - if ($retMoni) { - foreach ($this->clientes as $key => $value) { - if ($retMoni) { - $value['conection']->send($retMoni); - } - } - } } catch (\Exception $th) { - logger('debuge')->debug($th->getMessage()); + logger('ws:onMessage')->debug($th->getMessage()); } } From a4583f9e2aeb9e2a0391fc5b8df36efd72e66df2 Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Fri, 28 Jan 2022 10:30:02 -0400 Subject: [PATCH 059/144] ajuste do tempo de envio do evento de transferencia --- app/Controllers/AgentController.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/Controllers/AgentController.php b/app/Controllers/AgentController.php index 6a81345..9188e91 100644 --- a/app/Controllers/AgentController.php +++ b/app/Controllers/AgentController.php @@ -340,9 +340,7 @@ class AgentController extends Controller $atendAtual->fila, $agentTransf->matricula ); - $ws = new WsInterface(); - $ws->enviaMsg($ws->enviaActions('Atendimento transferido', 'transfer', $agentTransf->matricula, $uniqueid)); - $ws->enviaMsg($ws->enviaActions('Atendimento transferido', 'transfer', $agent->matricula, $uniqueid)); + //$ws->enviaMsg($ws->enviaActions('Atendimento transferido', 'transfer', $agent->matricula, $uniqueid)); $provedor = new Positus(); $provedor->enviarMsg($atendAtual->cliente_id, CONF_NAME_REPONSE . ": Atendimento transferido"); $messegeModel = new Message(); @@ -357,6 +355,8 @@ class AgentController extends Controller 'read' ); $this->agent->commit(); + $ws = new WsInterface(); + $ws->enviaMsg($ws->enviaActions('Atendimento transferido', 'transfer', $agentTransf->matricula, $uniqueid)); return true; } catch (Exception $ex) { $this->agent->rollback(); From c1b585ea619d6b65b84aa8c7289789386b537544 Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Fri, 28 Jan 2022 10:30:29 -0400 Subject: [PATCH 060/144] =?UTF-8?q?valida=C3=A7=C3=A3o=20da=20ultima=20men?= =?UTF-8?q?sagem=20do=20cliente?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Controllers/MessageController.php | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/app/Controllers/MessageController.php b/app/Controllers/MessageController.php index 5529571..d7872ae 100644 --- a/app/Controllers/MessageController.php +++ b/app/Controllers/MessageController.php @@ -27,20 +27,21 @@ class MessageController extends Controller * @param type $timer * @return boolean */ - public function timeoutTalk($uniqueid) + public function timeoutTalk($uniqueid, $cliente) { try { $message = $this->message->findLastMessage($uniqueid); + if ($cliente == $message->dst) { + if (strtotime($message->msg_date . '+' . CONF_WHATSAPP_TIMEOUT_CLIENT_RESPOSTA . ' seconds') < time()) { + print('FINISH'); + return "FINISH"; + } - 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"; + $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"; + } } print('nada'); return false; From d29017467d6d55d371374a1b07a69923e18ac5d3 Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Fri, 28 Jan 2022 10:31:54 -0400 Subject: [PATCH 061/144] add convert aquivo pra iphone, novo parametro no agente disponivel --- app/Controllers/SystemMessageController.php | 1 - app/Middleware/ApiAgente.php | 6 ++++-- app/Middleware/Middleware.php | 15 ++++++++++----- app/Models/Supervisor.php | 14 +++++++++----- app/Providers/Positus.php | 12 +++++++++++- app/Providers/WebHeader.php | 4 +++- service/ServiceQueue.php | 2 +- service/ServiceTimeout.php | 4 ++-- 8 files changed, 40 insertions(+), 18 deletions(-) diff --git a/app/Controllers/SystemMessageController.php b/app/Controllers/SystemMessageController.php index 5a2d5b9..9b78040 100644 --- a/app/Controllers/SystemMessageController.php +++ b/app/Controllers/SystemMessageController.php @@ -33,7 +33,6 @@ class SystemMessageController extends Controller $msg->texto = preg_replace($pattern, $variavel['valor'], $msg->texto); } } - logger('debug')->info($msg->texto); $api->enviarMsg($numero, $msg->texto); } return $msgs; diff --git a/app/Middleware/ApiAgente.php b/app/Middleware/ApiAgente.php index 9f739fc..f48d70d 100644 --- a/app/Middleware/ApiAgente.php +++ b/app/Middleware/ApiAgente.php @@ -169,7 +169,7 @@ class ApiAgente implements IApi {; try { $param = $this->atendimento->getQuantiAtendimentSimultaneos(); - $ret = $this->supervisor->listaAgentesDisponivel(); + $ret = $this->supervisor->listaAgentesDisponivel('LIVRE'); $agentes = []; foreach ($ret as $key => $value) { if ($value->countAtendimentos < $param->prm_media_simultaneo) { @@ -191,6 +191,7 @@ class ApiAgente implements IApi { 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ão encontrado"); @@ -341,6 +342,7 @@ class ApiAgente implements IApi $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 . @@ -607,7 +609,7 @@ class ApiAgente implements IApi ], "mensagem" => [ "type" => $tipo, - "destino" => $destino, + "dst" => $destino, "uniqueid" => $uniqueid, "content" => utf8_encode($msg) ], diff --git a/app/Middleware/Middleware.php b/app/Middleware/Middleware.php index 8674dad..4c8297b 100644 --- a/app/Middleware/Middleware.php +++ b/app/Middleware/Middleware.php @@ -62,11 +62,16 @@ class Middleware extends Http // echo json_encode(['success' => $this->request]); // return null; case 'api': - $apiAgente->router($this->param()[2], $this->request); - return null; - case 'supervisor': - $apiSupervisor->router($this->param()[2], $this->request); - return null; + 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: + $this->header->redirect(); + } case 'link': $this->header->fileTransfer($this->param()[2], CONF_PATH_FILES . $this->param()[2], base64_decode($this->param()[3])); exit(0); diff --git a/app/Models/Supervisor.php b/app/Models/Supervisor.php index 98cab9f..ae2db73 100644 --- a/app/Models/Supervisor.php +++ b/app/Models/Supervisor.php @@ -16,8 +16,9 @@ class Supervisor extends Model private $supervisor = 'md_supervisor'; private $atendimento = 'md_atendimento'; - public function listaAgentesDisponivel() + public function listaAgentesDisponivel($status = null) { + $data = []; $this->query = " SELECT *, ( SELECT @@ -31,8 +32,7 @@ class Supervisor extends Model ) AS countAtendimentos FROM md_supervisor ms - WHERE ms.status = 'LIVRE' - AND ( + WHERE ( SELECT count(*) FROM @@ -43,9 +43,13 @@ class Supervisor extends Model AND ma.matricula = ms.matricula ) < (SELECT prm_media_simultaneo FROM pbx_parametros pp LIMIT 1 ) - ORDER BY countAtendimentos "; - return $this->read($this->query)->fetchAll(); + 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) diff --git a/app/Providers/Positus.php b/app/Providers/Positus.php index 385f97b..fc2bda2 100644 --- a/app/Providers/Positus.php +++ b/app/Providers/Positus.php @@ -254,7 +254,17 @@ class Positus implements IApiMedia } return false; } - + 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; + } + } function setStorage($storage) { $this->storage = $storage; diff --git a/app/Providers/WebHeader.php b/app/Providers/WebHeader.php index 9794b87..9cad500 100644 --- a/app/Providers/WebHeader.php +++ b/app/Providers/WebHeader.php @@ -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,6 +173,7 @@ class WebHeader "Content-Length" => filesize($file), "Content-Disposition" => "attachment; filename=" . basename($name . "." . explode('/', $mimetype)[1]), "Expires" => 0, + "Connection" => 'close', "Pragma" => 'public' ]); $this->bootstrap(); @@ -254,4 +256,4 @@ class WebHeader { return $this->methods; } -} +} \ No newline at end of file diff --git a/service/ServiceQueue.php b/service/ServiceQueue.php index d8c445e..3958fac 100644 --- a/service/ServiceQueue.php +++ b/service/ServiceQueue.php @@ -42,7 +42,7 @@ class ServiceQueue implements IService { try { $this->coremedia->setApi(new Positus()); - $agentesLista = $this->supervisor->listaAgentesDisponivel(); + $agentesLista = $this->supervisor->listaAgentesDisponivel('LIVRE'); foreach ($agentesLista as $item) { $atendimentos = $this->atendimento->getAtendFila($item->fila); if (count($atendimentos) > 0) { diff --git a/service/ServiceTimeout.php b/service/ServiceTimeout.php index 7d78922..a039870 100644 --- a/service/ServiceTimeout.php +++ b/service/ServiceTimeout.php @@ -46,13 +46,13 @@ class ServiceTimeout implements IService foreach ($atendiss as $value) { $this->timeoutCliente($value->uniqueid, $value->cliente_id); } - sleep(60); + //sleep(60); // } } private function timeoutCliente($uniqueid, $client) { echo "unique: $uniqueid - client: $client \n"; - if ($this->message->timeoutTalk($uniqueid) == 'FINISH') { + if ($this->message->timeoutTalk($uniqueid, $client) == 'FINISH') { $this->positus->enviarMsg($client, $this->mensagem['TIMEOUT_CLIENT_INATIVIDADE']); $this->command->finalizar($client); return null; From bd9be3f1dcafcdea68be1e81d6b090cbe802dd32 Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Fri, 28 Jan 2022 10:32:14 -0400 Subject: [PATCH 062/144] alterando a porta 8090 para 8080 --- websocket/WsInterface.php | 2 +- websocket/websocket.php | 36 ++++-------------------------------- 2 files changed, 5 insertions(+), 33 deletions(-) diff --git a/websocket/WsInterface.php b/websocket/WsInterface.php index eb8bc9d..4ad8e74 100644 --- a/websocket/WsInterface.php +++ b/websocket/WsInterface.php @@ -11,7 +11,7 @@ class WsInterface function enviaMsg($msg) { if (!empty($msg)) { - $this->client = new Testess("ws://192.168.115.65:8090/ws"); + $this->client = new Testess("ws://192.168.115.65:8080/ws"); $this->client->send($msg); $this->client->close(); return null; diff --git a/websocket/websocket.php b/websocket/websocket.php index 7dfea42..f3b0b9c 100644 --- a/websocket/websocket.php +++ b/websocket/websocket.php @@ -1,42 +1,14 @@ route('/ws', new Servidorsocket(), array('*')); $app->run(); } catch (\Exception $th) { echo $th->getMessage(); -} - -$server->run(); - - - -// $loop = Factory::create(); -// $webSock = new SecureServer( -// new Server('0.0.0.0:8090', $loop), -// $loop, -// array( -// 'local_cert' => '/etc/letsencrypt//fullchain.pem', // path to your cert -// 'local_pk' => '/etc/letsencrypt/privkey.pem', // path to your server private key -// 'allow_self_signed' => TRUE, // Allow self signed certs (should be false in production) -// 'verify_peer' => FALSE -// ) -// ); -// // Ratchet magic -// $webServer = new IoServer( -// new HttpServer( -// new WsServer( -// new Servidorsocket() -// ) -// ), -// $webSock -// ); -// $loop->run(); \ No newline at end of file +} \ No newline at end of file From 844aa71524c89bec25348ed7ce211e4d871b1f7d Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Fri, 28 Jan 2022 10:32:31 -0400 Subject: [PATCH 063/144] Update test_services.php --- tests/test_services.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/test_services.php b/tests/test_services.php index b8c2252..a8fe9dd 100644 --- a/tests/test_services.php +++ b/tests/test_services.php @@ -2,7 +2,6 @@ use service\ServiceQueue; use service\ServiceTimeout; -use websocket\WsInterface; require __DIR__ . '/../vendor/autoload.php'; include __DIR__ . '/../includes/config.php'; @@ -12,5 +11,5 @@ $servce_queue = new ServiceQueue(); while (true) { $sevice_timeout->run(); $servce_queue->run(); - sleep(30); + sleep(10); } \ No newline at end of file From 6b49267d6324ab3cc4a3061bcd67c7a57bba5fc0 Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Fri, 28 Jan 2022 10:32:48 -0400 Subject: [PATCH 064/144] =?UTF-8?q?valida=C3=A7=C3=A3o=20da=20mensagem=20f?= =?UTF-8?q?antasma?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Models/Message.php | 45 +++++++++++++++++++++++++++++------------- 1 file changed, 31 insertions(+), 14 deletions(-) diff --git a/app/Models/Message.php b/app/Models/Message.php index f4f37ea..33d8f8a 100644 --- a/app/Models/Message.php +++ b/app/Models/Message.php @@ -16,21 +16,38 @@ class Message extends Model 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, media, status, mimetype, file_name, id_provedor) + 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 - ]); + 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) From 19d3b446961b8c8b6ca8105872d007395c684f78 Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Fri, 28 Jan 2022 17:20:43 -0400 Subject: [PATCH 065/144] valida mensagens de retorno da positus --- app/Core/CoreMedia.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/Core/CoreMedia.php b/app/Core/CoreMedia.php index 38aa95c..f74a7d6 100644 --- a/app/Core/CoreMedia.php +++ b/app/Core/CoreMedia.php @@ -69,6 +69,9 @@ class CoreMedia $this->build($data); $this->api = $providere; $this->api->setHook($this->request); + if (!$this->api->getIsValidMessage()) { + return null; + } if (!$this->api->baixarMidia()) { $this->api->enviarMsg($this->api->getPhone(), "Não foi possivel entregar o aquivo para o agente. Envie novamente!"); } From f231217618c8b268b12f0ceadadc01ad28028634 Mon Sep 17 00:00:00 2001 From: Simples IP Desenvolvimento Date: Wed, 2 Feb 2022 11:32:44 -0400 Subject: [PATCH 066/144] =?UTF-8?q?correcoes=20e=20adi=C3=A7=C3=A3o=20de?= =?UTF-8?q?=20recursos?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/css/.DS_Store | Bin 6160 -> 0 bytes public/css/styles.css | 109 +++++++++++++++- public/index.html | 21 ++-- public/js/config.js | 5 +- public/js/main.js | 51 ++++---- public/js/requests.js | 173 ++++++++++++++++++-------- public/js/util.js | 228 +++++++++++++++++++++------------- public/sound/notification.mp3 | Bin 0 -> 7567 bytes 8 files changed, 402 insertions(+), 185 deletions(-) delete mode 100644 public/css/.DS_Store create mode 100644 public/sound/notification.mp3 diff --git a/public/css/.DS_Store b/public/css/.DS_Store deleted file mode 100644 index 3cbbb78abcdcde85389bc155d61419dd2827f906..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6160 zcmeI0u?@mN3`K275s4-x0?mJHGB(KPw zV`e^H&U>@L%z6x}?Kmxs=Q$R(dWm?}cnQfB0TB=Z5fA|p_(1|#vuXV%RI><(fCxMZ z@cU3Os5SL|yN1_39++Aa0PPTN2ETO^(82(;rbaR%h`~IPp^@y_W_T1pyySH?H4B(W z0rPA@Jn!z=r+9k;+@Z8Y^BSsI1Vms?U>%18cL4{CH-7=i_@% diff --git a/public/css/styles.css b/public/css/styles.css index bbcddc2..240c4ab 100644 --- a/public/css/styles.css +++ b/public/css/styles.css @@ -30,8 +30,8 @@ a { /* Variables */ :root { - --background-green: #464646; - --background-beige: #464646; + --background-green: #fdfdfd; + --background-beige: #fdfdfd; --sidebar-header: #ededed; --notifications-text-color: rgba(48, 48, 48, 0.96); --notifications-link-color: rgba(48, 48, 48, 0.85); @@ -93,10 +93,13 @@ a { grid-template-columns: 42rem 1fr; grid-column: 1 / -1; grid-row: 1 / -1; - width: 139.6rem; + width: 90%; + max-width: 1250px; margin: 2rem auto; border: 1px solid rgba(0, 0, 0, 0.1); box-shadow: 0 0 1rem 0.05rem rgba(0, 0, 0, 0.2); + border-top-left-radius: 20px; + border-bottom-left-radius: 20px; } /*********** @@ -240,7 +243,6 @@ a { /* Chats */ .chats { - height: 80rem; overflow-y: scroll; overflow-x: hidden; } @@ -499,7 +501,7 @@ a { height: calc(100vh - 100px); overflow: scroll; overflow-x: hidden; - background-color: var(--backround-chat-panel); + background-image: url('../images/wallpaper_simpleschat.png'); background-repeat: repeat; } @@ -693,6 +695,7 @@ input[type="range"] { /* Type message bar */ .type-window-image { + max-width: 900px; width: 100%; display: flex; flex-direction: column; @@ -1018,10 +1021,104 @@ input[type="range"] { color: #D8D8D8 } +.select-notification { + background-color: var(--chat-active-color); +} + .opacity-3 { opacity: 0.3 } .cursor-pointer { cursor: pointer; -} \ No newline at end of file +} + +@media (max-width: 1310px) { + .modal-content{ + width: 42%; + } +} + +@media (max-width: 1240px) { + + #welcometomessage h1,h2{ + font-size: 30px; + } + + #welcometomessage img { + width: 300px; + } + + .chat-window { + padding: 5rem 5rem 3rem 4rem; + } + + .chat-window-contact-name{ + font-size: 1.3rem; + } + + .chat-window-contact-status { + font-size: 0.9rem; + } + + .type-message-bar-icons-upload{ + margin-left: 20px + } + + .chats { + height: 85vh; + } +} + +@media (max-width: 1100px) { + .chats { + height: 82vh; + } +} + +@media (max-width: 990px) { + #welcometomessage h1,h2{ + font-size: 20px; + } + + #welcometomessage img { + width: 200px; + } + + .chat-window-contact-name{ + font-size: 1rem; + } + + .chat-window-contact-status { + font-size: 0.8rem; + } + + .chats { + height: 80vh; + } + + .modal-content{ + width: 50%; + } +} + +@media (max-height: 820px){ + .sidebar{ + height: 78vh; + } + + + #welcometomessage h1,h2{ + font-size: 30px; + } + + #welcometomessage img { + width: 300px; + } +} + +@media (max-height: 1380px){ + .chats { + height: 82vh; + } +} diff --git a/public/index.html b/public/index.html index 7bf4f59..84a2432 100644 --- a/public/index.html +++ b/public/index.html @@ -27,11 +27,11 @@
- - - + +
- +
diff --git a/public/js/config.js b/public/js/config.js index 4d1ff03..27e9c43 100644 --- a/public/js/config.js +++ b/public/js/config.js @@ -1,7 +1,6 @@ -const ws = 'ws://192.168.115.65:8090/ws' -const server_api = '192.168.115.65:8081' +const ws = 'ws://192.168.115.65/wss' +const server_api = '192.168.115.65' const URLApi = `http://${server_api}` let mediaRecorder -localStorage.setItem('my_uniqueid', 1020) const icontypes = ['csv', 'doc', 'pdf', 'txt', 'xls', 'zip', 'ppt'] diff --git a/public/js/main.js b/public/js/main.js index 5ecfe5e..d1660b1 100644 --- a/public/js/main.js +++ b/public/js/main.js @@ -1,23 +1,3 @@ -// var wserror -// /** SOCKET CONNECT */ -// ws.onopen = function (error) { -// $("#status_agent").addClass("status-connect").text('CONECTADO'); -// if(wserror == 'teste'){ -// location.reload(); -// } -// }; - -// ws.onclose = function(e) { -// console.log('socket closed try again'); -// } - -// /** SOCKET ERROR */ -// ws.onerror = function (error) { -// $("#status_agent").addClass("status-desconnect").text('DESCONECTADO'); -// wserror = 'teste' -// }; - - /** * EVENTOS GERADOS PELO USUÁRIO DA APLICAÇÃO */ @@ -48,6 +28,7 @@ $(function(){ startPause() startTransfer() startFinalizar() + exitSystem() /** INICIA COM O HEADER DO CONTATO VAZIO */ startChannelMessage() @@ -84,6 +65,7 @@ $(function(){ $('#modalselect').css({display: 'none'}) }) supervisorAgente() + }) /** @@ -104,6 +86,14 @@ const selectNotification = (id) => { const allNotifications = JSON.parse(localStorage.getItem('obj_notification')) hideButtons(false) + allNotifications.data.forEach(e => { + $('#' + e.uniqueid.replace('.', `\\.`)).removeClass('select-notification') + if(e.uniqueid == id && e.status == 0){ + hideButtons(true) + } + }) + $('#' + id.replace('.', `\\.`)).addClass('select-notification') + allNotifications.data.forEach(e => { if(e.uniqueid == id && e.status == 0){ hideButtons(true) @@ -198,8 +188,8 @@ const selectNotification = (id) => { $('.chat-window').append(`
- - + + ${e.filename} @@ -232,9 +222,10 @@ const selectNotification = (id) => { const sendMessage = (obj = {}) => { const sendNumber = localStorage.getItem('session_window') const myUniqueid = localStorage.getItem('my_uniqueid') + const agent = JSON.parse(localStorage.getItem('obj_status')) let sendContent = (typeof obj.fileContent === "undefined") ? $('#fieldsendmessage').val() : obj.content - let name = obj.name ? obj.name : 'DESCONHECIDO' + let name = agent.data.nome ? agent.data.nome : 'Atendente' let uniqueid = localStorage.getItem('session_uniqueid') let media = obj.media ? obj.media : 'whatsapp' let type = obj.type ? obj.type : 'text' @@ -337,6 +328,15 @@ const receiveNotification = (data) => { switch(data.event?.type){ case "mensagem": + if(data.event.mensagem.uniqueid != localStorage.getItem('session_uniqueid')){ + notifyMe(data.event.contact.name, { + body: data.event.mensagem.content, + icon: `images/${data.event.mensagem.media}.png`, + silent: true + }) + soundNotification(`integracao/simpleschat/sound/notification.mp3`) + } + /** VALIDA O NUMERO, VERIFICA SE O TEM ALGMA MSG INICIAL, SE JÁ TEVE UM NUMERO NA VERIFICACAO */ listarAtendimentoAgente(localStorage.getItem('my_uniqueid')) validate = JSON.parse(localStorage.getItem('obj_notification')) @@ -367,16 +367,17 @@ const receiveNotification = (data) => { let obj switch(data.event.mensagem.type){ case 'start': + case 'transfer': + case 'pause': obj = {} break case 'finish': obj = { uniqueid: data.event?.mensagem.uniqueid, - action: 'finish' + action: data.event.mensagem.type, } break } - console.log(obj) notifications(obj) break } diff --git a/public/js/requests.js b/public/js/requests.js index 27d1ea9..6928172 100644 --- a/public/js/requests.js +++ b/public/js/requests.js @@ -1,12 +1,12 @@ const enviarMensagem = (dataSend) => { $.ajax({ - url: `http://${server_api}/api/enviarMensagem`, + url: `http://${server_api}/integracao/media/api/agente/enviarMensagem`, type: "POST", data: JSON.stringify(dataSend), - success: function(res){ - //console.log(res) + success: function (res) { + //console.log(res) }, - error: function(res){ + error: function (res) { $('.chat-window').append(`
MENSAGEM NÃO FOI ENVIADA!
`) } }); @@ -14,15 +14,17 @@ const enviarMensagem = (dataSend) => { const listaMensagem = (uniqueid) => new Promise((resolve) => { $.ajax({ - url: `http://${server_api}/api/listarMensagem`, + url: `http://${server_api}/integracao/media/api/agente/listarMensagem`, type: "POST", - data: JSON.stringify({uniqueid}), - success: function(res){ + data: JSON.stringify({ + uniqueid + }), + success: function (res) { localStorage.removeItem('obj_contact') localStorage.setItem('obj_contact', JSON.stringify(res)) resolve() }, - error: function(res){ + error: function (res) { alert('Nao foi possivel carregar as listas de mensagens.') } }) @@ -30,14 +32,14 @@ const listaMensagem = (uniqueid) => new Promise((resolve) => { const listarAgentesDisponivel = () => new Promise((resolve) => { $.ajax({ - url: `http://${server_api}/api/listarAgentesDisponivel`, + url: `http://${server_api}/integracao/media/api/agente/listarAgentesDisponivel`, type: "GET", - success: function(res){ + success: function (res) { localStorage.removeItem('obj_agentes_disponivel') localStorage.setItem('obj_agentes_disponivel', JSON.stringify(res)) resolve(res) }, - error: function(res){ + error: function (res) { alert('Nao foi possivel carregar as listas de agentes.') } }); @@ -45,15 +47,17 @@ const listarAgentesDisponivel = () => new Promise((resolve) => { const listarAtendimentoAgente = (matricula) => new Promise((resolve) => { $.ajax({ - url: `http://${server_api}/api/listarAtendimentoAgente`, + url: `http://${server_api}/integracao/media/api/agente/listarAtendimentoAgente`, type: "POST", - data: JSON.stringify({matricula}), - success: function(res){ + data: JSON.stringify({ + matricula + }), + success: function (res) { localStorage.removeItem('obj_notification') localStorage.setItem('obj_notification', JSON.stringify(res)) resolve(res) }, - error: function(res){ + error: function (res) { alert('Nao foi possivel carregar as listas de atendimento.') } }); @@ -61,65 +65,120 @@ const listarAtendimentoAgente = (matricula) => new Promise((resolve) => { const listarPausasAgente = (matricula) => new Promise((resolve) => { $.ajax({ - url: `http://${server_api}/api/listarPausasAgente`, + url: `http://${server_api}/integracao/media/api/agente/listarPausasAgente`, type: "POST", - data: JSON.stringify({matricula}), - success: function(res){ + data: JSON.stringify({ + matricula + }), + success: function (res) { localStorage.removeItem('obj_pauses') localStorage.setItem('obj_pauses', JSON.stringify(res)) resolve(res) }, - error: function(res){ + error: function (res) { alert('Nao foi possivel carregar as listas de pausa.') } }); }) -const entrarPausa = (id_pausa, matricula) => { +const entrarPausa = (id_pausa, matricula) => new Promise((resolve) => { $.ajax({ - url: `http://${server_api}/api/entrarPausa`, + url: `http://${server_api}/integracao/media/api/agente/entrarPausa`, type: "POST", - data: JSON.stringify({ id_pausa, matricula }), - success: function(res){ - if(res.status == 'success'){ + data: JSON.stringify({ + id_pausa, + matricula + }), + success: function (res) { + if (res.status == 'success') { alert('Agente em Pausa!') + resolve(res) } else { alert(res.message) } }, - error: function(res){ + error: function (res) { alert('Erro ao carregar as listas de pausa.') } }); -} +}) -const sairPausa = (matricula) => { +const sairPausa = (matricula) => new Promise((resolve) => { $.ajax({ - url: `http://${server_api}/api/sairPausa`, + url: `http://${server_api}/integracao/media/api/agente/sairPausa`, type: "POST", - data: JSON.stringify({matricula}), - success: function(res){ + data: JSON.stringify({ + matricula + }), + success: function (res) { alert('Pausa removida do Agente!') + resolve(res) }, - error: function(res){ + error: function (res) { alert('Nao foi possivel carregar as listas de pausa.') } }); +}) + +const entrar = (matricula, queue) => new Promise((resolve) => { + $.ajax({ + url: `http://${server_api}/integracao/media/api/agente/entrar`, + type: "POST", + data: JSON.stringify({ + id_fila: queue, + matricula + }), + success: function (res) { + resolve(res) + }, + error: function (res) { + resolve(res) + } + }); +}) + + +const sair = (matricula) => { + $.ajax({ + url: `http://${server_api}/integracao/media/api/agente/sair`, + type: "POST", + data: JSON.stringify({ + matricula + }), + success: function (res) { + if (res.status == 'success') { + alert('Desconectado do sistema!') + window.close() + } else { + alert(res.message) + } + }, + error: function (res) { + alert('Nao foi possivel desconectar do sistema.') + } + }); } const finalizarAtendimento = (matricula, uniqueid) => new Promise((resolve) => { $.ajax({ - url: `http://${server_api}/api/finalizarAtendimento`, + url: `http://${server_api}/integracao/media/api/agente/finalizarAtendimento`, type: "POST", - data: JSON.stringify({matricula, uniqueid}), - success: function(res){ - if(res.status == "success"){ - alert('Atendimento foi finalizado!') - notifications({ matricula, uniqueid, action: 'finish' }) + data: JSON.stringify({ + matricula, + uniqueid + }), + success: function (res) { + if (res.status == "success") { + alert('Atendimento foi finalizado!') + notifications({ + matricula, + uniqueid, + action: 'finish' + }) } resolve(res) }, - error: function(res){ + error: function (res) { alert('Nao foi possivel carregar as listas de pausa.') } }); @@ -127,15 +186,17 @@ const finalizarAtendimento = (matricula, uniqueid) => new Promise((resolve) => { const statusAgente = (matricula) => new Promise((resolve) => { $.ajax({ - url: `http://${server_api}/api/statusAgente`, + url: `http://${server_api}/integracao/media/api/agente/statusAgente`, type: "POST", - data: JSON.stringify({matricula}), - success: function(res){ + data: JSON.stringify({ + matricula + }), + success: function (res) { localStorage.removeItem('obj_status') localStorage.setItem('obj_status', JSON.stringify(res)) resolve(res) }, - error: function(res){ + error: function (res) { reconnectWS() console.log('Recarregando ...') //alert('Não foi possível carregar as infoemações do agente.') @@ -143,24 +204,32 @@ const statusAgente = (matricula) => new Promise((resolve) => { }); }) +const atualizaAgente = () => { + $.ajax({ + url: `http://${server_api}/index.php?idProg=14&idSubProg=3&ajax=1&acao=atualiza`, + type: "GET", + }); +} + const transferirAtendimento = (origem, destino, uniqueid) => new Promise((resolve) => { $.ajax({ - url: `http://${server_api}/api/transferirAtendimento`, + url: `http://${server_api}/integracao/media/api/agente/transferirAtendimento`, type: "POST", - data: JSON.stringify({ + data: JSON.stringify({ matricula_origem: origem, matricula_destino: destino, uniqueid }), - success: function(res){ - if(res.status == 'success'){ + success: function (res) { + if (res.status == 'success') { alert('Atendimento foi transferido!') + resolve(res) } else { alert(res.message) } resolve(res) }, - error: function(res){ + error: function (res) { alert('Nao foi possivel carregar as infoemacoes do agente.') } }); @@ -168,13 +237,15 @@ const transferirAtendimento = (origem, destino, uniqueid) => new Promise((resolv const marcarMensagemVista = (uniqueid) => { $.ajax({ - url: `http://${server_api}/api/marcarMensagemVista`, + url: `http://${server_api}/integracao/media/api/agente/marcarMensagemVista`, type: "POST", - data: JSON.stringify({ uniqueid }), - success: function(res){ + data: JSON.stringify({ + uniqueid + }), + success: function (res) { //console.log('success') }, - error: function(res){ + error: function (res) { alert('Nao foi possivel carregar as informacoes do agente.') } }); diff --git a/public/js/util.js b/public/js/util.js index 97b0db2..f8b1795 100644 --- a/public/js/util.js +++ b/public/js/util.js @@ -57,7 +57,7 @@ const reconnectWS = () => { modalStart() $(this).css({'align-items': 'center'}) $('.modal-content-body').append(() => - `

RECONECTANDO, AGUARDE  

` + `

RECONECTANDO, AGUARDE  

` ); $('.modal-header-title').append(`Por favor, aguarde alguns instantes!`) $('#modalselect').show() @@ -79,7 +79,7 @@ const reconnectWS = () => { $('.modal-content-body').append(``) $('#footer-content-left').append(``) - $('#footer-content-right').append(``) + $('#footer-content-right').append(``) } $('#modalselect').show() }) @@ -96,20 +96,20 @@ const startSendFile = () => { $('#myImg').remove() icontypes.forEach(e => { if(typefile.indexOf(e) >= 0){ - $('.modal-content-body').append(``) + $('.modal-content-body').append(``) return } }) if(!$('#myImg')[0]){ - $('.modal-content-body').append(``) + $('.modal-content-body').append(``) } file.onload = function(e) { $('#footername').remove() $('#footersend').remove() $('#footer-content-left').append(``) - $('#footer-content-right').append(``) + $('#footer-content-right').append(``) } $('#modalselect').show() }) @@ -121,28 +121,38 @@ const startPause = () => { $("#modalselect").show() listarPausasAgente(localStorage.getItem('my_uniqueid')) const pausas = JSON.parse(localStorage.getItem('obj_pauses')) + $('.modal-content-body').append(() => { - let selectPause = `` + if(selectPause.length > 0){ + selectPause = `` + } else { + selectPause = '

Nenhuma pausa foi encontrado!

' + } return selectPause }); $('.modal-header-title').append(`Selecione uma Pausa:`) - $('#footer-content-right').append(``) + $('#footer-content-right').append(``) $('#modalselect').show() }) $("#exitPause").on('click', () => { - sairPausa(localStorage.getItem('my_uniqueid')) - supervisorAgente() + sairPausa(localStorage.getItem('my_uniqueid')).then(() => { + monitorPausaAgente() + }) }) $('#footer-content-right').on('click', '#pausesend', () => { - entrarPausa($("#selectpause").val(), localStorage.getItem('my_uniqueid')) - $('#modalselect').css({display: 'none'}) + entrarPausa($("#selectpause").val(), localStorage.getItem('my_uniqueid')).then(() => { + $('#modalselect').css({display: 'none'}) + monitorPausaAgente() + }) }) } @@ -163,16 +173,28 @@ const startTransfer = () => { }); $('.modal-header-title').append(`Selecione um agente para transferir:`) - $('#footer-content-right').append(``) + $('#footer-content-right').append(``) $('#modalselect').show() }) $('#footer-content-right').on('click', '#transfersend', () => { - transferirAtendimento(localStorage.getItem('my_uniqueid'), $("#selectranfer").val(), localStorage.getItem('session_uniqueid')) - $('#modalselect').css({display: 'none'}) + transferirAtendimento(localStorage.getItem('my_uniqueid'), $("#selectranfer").val(), localStorage.getItem('session_uniqueid')).then(() => { + hideButtons(true) + notifications({ matricula: localStorage.getItem('my_uniqueid'), uniqueid: localStorage.getItem('session_uniqueid'), action: 'finish' }) + $('#modalselect').css({display: 'none'}) + }) + }) +} + +const exitSystem = () => { + $("#exitSystem").on('click', function(){ + if(confirm('Deseja realmente desconectar do sistema?')){ + sair(localStorage.getItem('my_uniqueid')) + } }) } + const startFinalizar = () => { $("#finalizaratendimento").on('click', function(){ if(confirm('Deseja realmente finalizar o atendimento?')){ @@ -187,7 +209,7 @@ const startFinalizar = () => { function recorderVoice () { $('#modalselect').show() modalStart() - $('.modal-content-body').append(` + $('.modal-content-body').append(` `) - $('#footer-content-right').append(``) + $('#footer-content-right').append(``) - navigator.mediaDevices.getUserMedia({audio: true}).then( + navigator.mediaDevices.getUserMedia({video: false, audio: true}).then( stream => { + let option = { + type: 'audio/mpeg', + }; + mediaRecorder = new MediaRecorder(stream) let chunks = [] - mediaRecorder.ondataavailable = data => { + mediaRecorder.ondataavailable = (data) => { chunks.push(data.data) } mediaRecorder.onstop = () => { - const blob = new Blob(chunks, {type : 'audio/mpeg'}) + const blob = new Blob(chunks, option) const reader = new FileReader() reader.readAsDataURL(blob) reader.onloadend = () => { @@ -235,7 +261,7 @@ function recorderVoice () { * @returns */ const messageTypeMedia = (obj) => { - const fileDownload = URLApi + "/link/" + obj.id_provedor + "/" + window.btoa(obj.mimetype) + const fileDownload = URLApi + "/integracao/media/link/" + obj.id_provedor + "/" + window.btoa(obj.mimetype) if(obj.type == 'voice' || obj.type == 'audio'){ $('.chat-window').append(`
@@ -266,9 +292,9 @@ function recorderVoice () { const typefile = obj.filename.split('.')[1] let icon if(icontypes.indexOf(typefile) >= 0){ - icon = `` + icon = `` } else { - icon = `` + icon = `` } $('.chat-window').append(` @@ -314,7 +340,7 @@ const buildNotification = (data = {}) => { const status = data.status == 0 ? 'opacity-3' : '' return `
- +
@@ -343,22 +369,49 @@ const alertNotification = (uniqueid, type = 'add') => { } }) - console.log(countMsg) let notf = countMsg.length + 1 $('#' + uniqueid.replace('.', `\\.`) + " .chat-right-bottom-right").append(`${notf}`) - - } + } +} + +function soundNotification(url) { + const audio = new Audio(url); + audio.play(); +} + +const notifyMe = (title, content) => { + if (!("Notification" in window)) { + console.log("This browser does not support desktop notification"); + } else if (Notification.permission === "granted") { + let notification = new Notification(title,{ + body: content.body, + icon: content.icon, + silent: true + }) + } else if (Notification.permission !== 'denied' || Notification.permission === "default") { + Notification.requestPermission(function (permission) { + if (permission === "granted") { + let notification = new Notification(title, { + body: content.body, + icon: content.icon, + silent: true + }) + } + }) + } } /** * CRIA AS NOTIFICACOES DE TODOS OS ATENDIMENTOS NA INICIALIZACAO DO SISTEMA OU ATUALIZACAO */ const notifications = (obj = {}) => { - - listarAtendimentoAgente(localStorage.getItem('my_uniqueid')).then(() => { + + /** STATUS DO AGENTE */ + monitorPausaAgente() + + listarAtendimentoAgente(localStorage.getItem('my_uniqueid')).then((notification) => { let chatList = '' $('#chats').empty() - const notification = JSON.parse(localStorage.getItem('obj_notification')) if(Object.values(obj).length > 0) { if(obj.action == "mensagem"){ @@ -368,16 +421,16 @@ const notifications = (obj = {}) => { notification.data.forEach(el => { if(el.uniqueid == obj.uniqueid){ /** MARCA ATENDIMENTO COMO FINALIZADO */ - el.status = 0 - /** REMOVE OS BOTÕES E CAIXA DE TEXTO DEPOIS DA FINALIZACAO */ + obj.action == "finish" ? el.status = 0 : null + /** REMOVE OS BOTÕES E CAIXA DE TEXTO DEPOIS DA FINALIZACAO */ if(el.uniqueid == localStorage.getItem('session_uniqueid')){ - hideButtons(true) + hideButtons(true) } } }) } } - + notification.data.sort((a, b) => b.status - a.status) notification.data.forEach(e => { chatList += buildNotification({ @@ -393,88 +446,85 @@ const notifications = (obj = {}) => { }) } -const supervisorAgente = () => { - statusAgente(localStorage.getItem('my_uniqueid')) - const status = JSON.parse(localStorage.getItem('obj_status')) - - /** CONFIGURACAO DO BOTAO STATUS */ - if(status.data.status == 'LIVRE'){ - $('#exitPause').hide() - $('#entrePause').show() - } else { - $('#entrePause').hide() - $('#exitPause').show() - } - - /** CONFIGURACAO DE MATRICULA */ - $('#myuniqueid').text(localStorage.getItem('my_uniqueid')) +const monitorPausaAgente = () => { + statusAgente(localStorage.getItem('my_uniqueid')).then((agente) => { + let statusagent + if(agente.data.status){ + statusagent = agente.data.status + if(agente.data.status == 'LIVRE'){ + $('#exitPause').hide() + $('#entrePause').show() + $('#status_agent').attr('class','status-connect') + } else if(agente.data.status == 'PAUSA') { + $('#entrePause').hide() + $('#exitPause').show() + $('#status_agent').attr('class','status-desconnect') + statusagent = agente.data.status + " - " + agente.data.motivo_pausa + } else if (agente.data.status == 'OCUPADO'){ + $('#exitPause').hide() + $('#status_agent').attr('class','status-reconnect') + } else { + $('#status_agent').attr('class','status-connect') + } - /** CONFIGURACAO NOME */ - $('#nameagent').text(status.data.nome) + $('#status_agent').text(statusagent) - /** CONFIGURACAO FILA */ - $('#queueagente').text(status.data.fila) + $('#myuniqueid').text(localStorage.getItem('my_uniqueid')) + /** CONFIGURACAO NOME */ + $('#nameagent').text(agente.data.nome) + /** CONFIGURACAO FILA */ + $('#queueagente').text(agente.data.fila) + } + }) +} - /** CONFIGURACAO DE STATUS */ - let statusagent - statusagent = status.data.status - if(status.data.status == "PAUSA"){ - $('#status_agent').attr('class','status-desconnect') - statusagent = status.data.status + " - " + status.data.motivo_pausa - } else if(status.data.status == "OCUPADO") { - $('#status_agent').attr('class','status-reconnect') - } else { - $('#status_agent').attr('class','status-connect') - } - $('#status_agent').text(statusagent) +const supervisorAgente = () => { + atualizaAgente() /** MONITORA AS CONFIGURACOES */ setTimeout(() => { supervisorAgente() - }, 1000); + }, 90000); } /** CONNECT TO WS */ -const connect = (wsserver, reconnect = false) => { +const connect = (wsserver) => { const ws = new WebSocket(wsserver); - - ws.onmessage = function(e) { - //console.log('Message:', e.data); - }; - + + ws.onmessage = function(e) {}; ws.onclose = function(e) { - console.log('Socket is closed. Reconnect will be attempted in 1 second.', e.reason); setTimeout(function() { - connect(wsserver, true); - }, 1000); + connect(wsserver); + }, 3000); }; ws.onerror = function(err) { - console.error('Socket encountered error: ', err.message, 'Closing socket'); + $("#status_agent").addClass("status-desconnect").text('DESCONECTADO'); ws.close(); }; - ws.onopen = function() { - $("#status_agent").addClass("status-connect").text('CONECTADO'); - ws.send({message: 'success to connected!'}); - if(reconnect == true){ - reconnect = false - location.reload() - } + ws.onopen = function wsconnect() { + $("#status_agent").addClass("status-reconnect").text('CONECTANDO ...'); + + entrar(localStorage.getItem('my_uniqueid'), localStorage.getItem('obj_queue')).then((login) => { + if(login.status == 'success' || login.status.indexOf('autenticado') === -1){ + monitorPausaAgente() + ws.send(JSON.stringify({matricula: localStorage.getItem('my_uniqueid')})); + notifications() + } else { + wsconnect() + } + }) }; ws.addEventListener("open", () => { const storage = ['my_uniqueid', 'keep_msg', 'obj_contact', 'session_uniqueid', 'session_window'] - storage.forEach(e => { - //localStorage.removeItem(e); - }) - ws.addEventListener("message", e => { /** att: atualizacao do websocket */ if(e.data != 'att'){ const data = JSON.parse(e?.data) - + if(localStorage.getItem('session_uniqueid') == null){ localStorage.setItem('session_uniqueid', data.event.mensagem.uniqueid) } diff --git a/public/sound/notification.mp3 b/public/sound/notification.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..8d50ebac6fb971fe8b7c0d4c207acec7d5da53fd GIT binary patch literal 7567 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 S5?BBr_&4wUf7Soi4Ez_CzaJa` literal 0 HcmV?d00001 From c1cbd7f2726f4c21081409c23bc69452e3159389 Mon Sep 17 00:00:00 2001 From: Simples IP Desenvolvimento Date: Wed, 2 Feb 2022 14:21:03 -0400 Subject: [PATCH 067/144] ajuste de conexao --- public/js/config.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/public/js/config.js b/public/js/config.js index 27e9c43..b85f106 100644 --- a/public/js/config.js +++ b/public/js/config.js @@ -1,6 +1,8 @@ -const ws = 'ws://192.168.115.65/wss' -const server_api = '192.168.115.65' -const URLApi = `http://${server_api}` +const server = localStorage.getItem('obj_server') +const ws = `ws://${server}/wss` + +const server_api = server +const URLApi = `http://${server}` let mediaRecorder -const icontypes = ['csv', 'doc', 'pdf', 'txt', 'xls', 'zip', 'ppt'] +const icontypes = ['csv', 'doc', 'pdf', 'txt', 'xls', 'zip', 'ppt'] \ No newline at end of file From 4bdc49b18db973fa8d5d86ee8ecdba0c2c703643 Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Thu, 3 Feb 2022 10:10:55 -0400 Subject: [PATCH 068/144] =?UTF-8?q?ajuste=20da=20codifica=C3=A7=C3=A3o=20d?= =?UTF-8?q?as=20mensagens=20do=20sistema?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Controllers/SystemMessageController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Controllers/SystemMessageController.php b/app/Controllers/SystemMessageController.php index 9b78040..93d5eda 100644 --- a/app/Controllers/SystemMessageController.php +++ b/app/Controllers/SystemMessageController.php @@ -30,7 +30,7 @@ class SystemMessageController extends Controller foreach ($variavels as $key => $variavel) { $vari = $variavel['nome']; $pattern = "/$vari/i"; - $msg->texto = preg_replace($pattern, $variavel['valor'], $msg->texto); + $msg->texto = preg_replace($pattern, utf8_decode($variavel['valor']), $msg->texto); } } $api->enviarMsg($numero, $msg->texto); From ed2030046974b857ecf298a7f1961debd00d375b Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Thu, 3 Feb 2022 10:15:27 -0400 Subject: [PATCH 069/144] ajustes na pausa de agente --- app/Middleware/ApiAgente.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/Middleware/ApiAgente.php b/app/Middleware/ApiAgente.php index f48d70d..dcc04b6 100644 --- a/app/Middleware/ApiAgente.php +++ b/app/Middleware/ApiAgente.php @@ -336,7 +336,8 @@ class ApiAgente implements IApi if ($mensagem['type'] == 'text') { $retuno = $provider->enviarMsg( $mensagem['dst'], - $msgTexto + $msgTexto, + false ); } else { $anmeArquivo = CONF_PATH_FILES . $mensagem['id_provedor']; @@ -500,8 +501,9 @@ class ApiAgente implements IApi $this->retorno("Pausa não encontrado"); return; } - if ($agente->status != 'LIVRE') { + 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)) { From 854060492023c33419778306dc1b4c4d1d004bfe Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Thu, 3 Feb 2022 10:16:47 -0400 Subject: [PATCH 070/144] Update att-v3.sql --- database/att-v3.sql | 46 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/database/att-v3.sql b/database/att-v3.sql index 845adfd..3e587a7 100644 --- a/database/att-v3.sql +++ b/database/att-v3.sql @@ -75,6 +75,52 @@ CREATE TABLE md_system_message ( 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!', + 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' + ); + CREATE TABLE md_supervisor ( id SERIAL NOT NULL PRIMARY KEY, ramal varchar NULL, From b0ddfc65d52a6616bb1c716f504913178114a9d0 Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Thu, 3 Feb 2022 10:17:15 -0400 Subject: [PATCH 071/144] =?UTF-8?q?ajuste=20na=20valida=C3=A7=C3=A3o=20de?= =?UTF-8?q?=20mensagens=20da=20positus?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Providers/Positus.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Providers/Positus.php b/app/Providers/Positus.php index fc2bda2..2792363 100644 --- a/app/Providers/Positus.php +++ b/app/Providers/Positus.php @@ -333,7 +333,7 @@ class Positus implements IApiMedia */ public function getIsValidMessage() { - return $this->hook['messages']; + return $this->hook['contacts']; } /** From 2282f5b37b1609a17bfd17aadf876cecf1136c1f Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Thu, 3 Feb 2022 10:17:35 -0400 Subject: [PATCH 072/144] add namespace de testes --- composer.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 97d2742..2be7919 100644 --- a/composer.json +++ b/composer.json @@ -10,7 +10,8 @@ "app\\": "app/", "scripts\\": "scripts/", "websocket\\": "websocket/", - "service\\": "service/" + "service\\": "service/", + "tests\\": "tests/" } }, "require": { From 2f0cfcad10df430bc11f8637771606083506ec6a Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Thu, 3 Feb 2022 10:18:01 -0400 Subject: [PATCH 073/144] =?UTF-8?q?valida=C3=A7=C3=A3o=20de=20conex=C3=A3o?= =?UTF-8?q?=20de=20banco?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Core/Model.php | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/app/Core/Model.php b/app/Core/Model.php index 0156ca0..1d18940 100644 --- a/app/Core/Model.php +++ b/app/Core/Model.php @@ -62,10 +62,6 @@ abstract class Model } } - ################################################################################################ - ################################################################################################ - ################################################################################################ - protected function create($query, $data) { try { @@ -75,7 +71,9 @@ abstract class Model return Connect::getInstance()->lastInsertId(); } catch (PDOException $ex) { Connect::setInstance(null); + sleep(5); logger('PDOExcep')->error('PDOException: ' . $ex->getMessage(), debug_backtrace()); + $this->create($query, $data); return null; } } @@ -89,7 +87,9 @@ abstract class Model return $stmt; } catch (PDOException $ex) { Connect::setInstance(null); + sleep(5); logger('PDOExcep')->error('PDOException: ' . $ex->getMessage(), debug_backtrace()); + $this->read($query, $data); return null; } } @@ -103,7 +103,9 @@ abstract class Model return ($stmt->rowCount() ? 1 : 0); } catch (PDOException $ex) { Connect::setInstance(null); + sleep(5); logger('PDOExcep')->error('PDOException: ' . $ex->getMessage(), debug_backtrace()); + $this->delete($query, $data); return null; } } @@ -118,7 +120,9 @@ abstract class Model return ($stmt->rowCount() ? 1 : 0); } catch (PDOException $ex) { Connect::setInstance(null); + sleep(5); logger('PDOExcep')->error('PDOException: ' . $ex->getMessage(), debug_backtrace()); + $this->update($query, $data); return null; } } From f6f9242ccbc595ad3f3ce2306e8acd7307c4aef2 Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Thu, 3 Feb 2022 10:34:15 -0400 Subject: [PATCH 074/144] Update Parametros.php --- app/Models/Parametros.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Models/Parametros.php b/app/Models/Parametros.php index 1fa7053..7c07f31 100644 --- a/app/Models/Parametros.php +++ b/app/Models/Parametros.php @@ -16,7 +16,7 @@ class Parametros extends Model public function findProtocolByParams() { - $this->query = "SELECT prm_agente_proto, prm_use_proto_parceiro, prm_pausa_grupo FROM " . self::TABLE . " WHERE id = :id;"; + $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(); } } \ No newline at end of file From eb2497aea84dbf5a7194606adc394087a754a413 Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Thu, 3 Feb 2022 17:13:59 -0400 Subject: [PATCH 075/144] =?UTF-8?q?add=20servi=C3=A7o=20de=20supervisor=20?= =?UTF-8?q?pbx?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- service/ServiceSupervisorPBx.php | 68 ++++++++++++++++++++++++++++++++ service/ServiceTimeout.php | 2 +- tests/test_services.php | 6 +++ 3 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 service/ServiceSupervisorPBx.php diff --git a/service/ServiceSupervisorPBx.php b/service/ServiceSupervisorPBx.php new file mode 100644 index 0000000..1e95cd9 --- /dev/null +++ b/service/ServiceSupervisorPBx.php @@ -0,0 +1,68 @@ +supervisor = new Supervisor; + $agentes = $this->supervisor->listaAgentesDisponivel(); + foreach ($agentes as $key => $agente) { + $agentPbx = $this->validaAgentCriado($agente->matricula); + if (!$agentPbx) { + $this->criaRegistroSupervisor($agente); + } else { + $this->atualizaTabelaSupervisor($agente, $agentPbx); + } + } + } + + function atualizaTabelaSupervisor($agente, $agentePbx) + { + + $this->supervisor->updateAgent2( + $agente->matricula, + $agente->ramal, + $agente->fila, + $agente->status, + $agente->motivo_pausa, + $agente->status != $agentePbx->status, + $this->retornaQuantidadeAtendimento($agente->matricula) + ); + } + + function criaRegistroSupervisor($agente) + { + $this->supervisor->addAgent2( + $agente->nome, + $agente->matricula, + $agente->ramal, + $agente->fila, + $agente->tempo_login, + $agente->status, + $agente->motivo_pausa, + $this->retornaQuantidadeAtendimento($agente->matricula) + ); + } + + function retornaQuantidadeAtendimento($matricula) + { + $atendimentoModel = new Atendimento(); + $paratroModel = new Parametros(); + $atendimentos = $atendimentoModel->getAtendimentoAbertoByAgente($matricula); + $parametros = $paratroModel->findProtocolByParams(); + $count = count($atendimentos); + return "$count/{$parametros->prm_media_simultaneo}"; + } + + function validaAgentCriado($matricula) + { + return $this->supervisor->findAgentByMatriculaPbx($matricula); + } +} \ No newline at end of file diff --git a/service/ServiceTimeout.php b/service/ServiceTimeout.php index a039870..926a37e 100644 --- a/service/ServiceTimeout.php +++ b/service/ServiceTimeout.php @@ -51,7 +51,7 @@ class ServiceTimeout implements IService } private function timeoutCliente($uniqueid, $client) { - echo "unique: $uniqueid - client: $client \n"; + // echo "unique: $uniqueid - client: $client \n"; if ($this->message->timeoutTalk($uniqueid, $client) == 'FINISH') { $this->positus->enviarMsg($client, $this->mensagem['TIMEOUT_CLIENT_INATIVIDADE']); $this->command->finalizar($client); diff --git a/tests/test_services.php b/tests/test_services.php index a8fe9dd..3f2df8f 100644 --- a/tests/test_services.php +++ b/tests/test_services.php @@ -1,6 +1,7 @@ run(); + echo "executar sevice_timeout \n"; $sevice_timeout->run(); + echo "executar servce_queue \n"; $servce_queue->run(); sleep(10); } \ No newline at end of file From 663edc516149a632ddd1b85cd3e16dd9502c0a0d Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Thu, 3 Feb 2022 17:14:25 -0400 Subject: [PATCH 076/144] =?UTF-8?q?add=20fun=C3=A7=C3=B5es=20de=20update?= =?UTF-8?q?=20e=20add=20no=20supervisor=20pbx?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Models/Supervisor.php | 89 +++++++++++++++++++++++++++++++++++---- 1 file changed, 80 insertions(+), 9 deletions(-) diff --git a/app/Models/Supervisor.php b/app/Models/Supervisor.php index ae2db73..8bb7769 100644 --- a/app/Models/Supervisor.php +++ b/app/Models/Supervisor.php @@ -111,6 +111,12 @@ class Supervisor extends Model 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 = []; @@ -172,6 +178,64 @@ class Supervisor extends Model ]); } + public function addAgent2( + $nome, + $matricula, + $ramal, + $dac, + $tempo_login, + $status = 'LIVRE', + $motivoPausa = null, + $origemDestino = null, + $modo_atendimento = 'Automatico', + $uniqueid = null, + $chamada_classificado = 1 + ) { + $data = []; + $this->query = "INSERT INTO pbx_supervisor_agentes ( + tempo_login, + nome, + ramal, + matricula, + origem_destino, + status, + motivo_pausa, + uniqueid, + chamada_classificado, + modo_atendimento, + dac, + duracao, + logado) VALUES( + :tempo_login, + :nome, + :ramal, + :matricula, + :origem_destino, + :status, + :motivo_pausa, + :uniqueid, + :chamada_classificado, + :modo_atendimento, + :dac, + :duracao, + :logado) "; + $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['uniqueid'] = $uniqueid; + $data['modo_atendimento'] = $modo_atendimento; + $data['chamada_classificado'] = ($chamada_classificado ? '1' : '0'); + return $this->create($this->query, $data); + } + + public function updateAgent( $matricula, $status = 'LIVRE', @@ -190,24 +254,31 @@ class Supervisor extends Model public function updateAgent2( $matricula, $ramal, + $dac, $status = 'LIVRE', - $origemDestino = null, + $atualizaDuracao = false, $motivoPausa = null, - $duracao = null, + $origemDestino = null, $uniqueid = null, $chamada_classificado = 1 ) { $data = []; - $this->query = "UPDATE " . self::SUPERVISOR_AGENTE . " SET logado = :logado, origem_destino = :origem_destino, status = :status, motivo_pausa = :motivo_pausa, uniqueid = :uniqueid, chamada_classificado = :chamada_classificado"; - if ($duracao) { - $this->query .= ", duracao = :duracao "; + $this->query = "UPDATE pbx_supervisor_agentes SET + origem_destino = :origem_destino, + status = :status, + motivo_pausa = :motivo_pausa, + uniqueid = :uniqueid, + chamada_classificado = :chamada_classificado, + dac = :dac, + ramal = :ramal "; + if ($atualizaDuracao) { + $data['duracao'] = 'now()'; + $this->query .= " , duracao = :duracao"; } - $this->query .= " WHERE matricula = :matricula AND ramal = :ramal;"; - + $this->query .= " WHERE matricula = :matricula"; $data['matricula'] = $matricula; $data['ramal'] = $ramal; - $data['logado'] = 'now()'; - $data['duracao'] = 'now()'; + $data['dac'] = $dac; $data['origem_destino'] = $origemDestino; $data['status'] = $status; $data['motivo_pausa'] = $motivoPausa; From 9bfe58259041b449466efbfcb8e683da153de1b8 Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Fri, 4 Feb 2022 10:23:44 -0400 Subject: [PATCH 077/144] validar quantidade de atendimentos quando tirar de pausa. --- app/Controllers/AgentController.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/app/Controllers/AgentController.php b/app/Controllers/AgentController.php index 9188e91..98f8d19 100644 --- a/app/Controllers/AgentController.php +++ b/app/Controllers/AgentController.php @@ -32,6 +32,7 @@ class AgentController extends Controller private $pause; private $bilhete; private $eventqueue; + private $atendModel; public function __construct() { @@ -41,6 +42,7 @@ class AgentController extends Controller $this->pause = new Pause(); $this->bilhete = new Bilhete(); $this->eventqueue = new EventQueue(); + $this->atendModel = new Atendimento(); } public function openService($ramal, $origemDest, $uniqueid) @@ -291,7 +293,13 @@ class AgentController extends Controller 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, CONF_AGENT_STATUS_LIVRE); + $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; From fedede9ba11bb944f1ca63541e7c27f359afcab6 Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Fri, 4 Feb 2022 17:00:31 -0400 Subject: [PATCH 078/144] =?UTF-8?q?add=20evento=20de=20perda=20de=20conex?= =?UTF-8?q?=C3=A3o?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/event.php | 1 + config/moments.php | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/config/event.php b/config/event.php index 2e35163..570ab4d 100644 --- a/config/event.php +++ b/config/event.php @@ -19,4 +19,5 @@ 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'); \ No newline at end of file diff --git a/config/moments.php b/config/moments.php index 839d022..06c9702 100644 --- a/config/moments.php +++ b/config/moments.php @@ -16,4 +16,5 @@ 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'); \ No newline at end of file +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 \ No newline at end of file From 0e1c305f7337096d3d5c78e616d8bb0814410c71 Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Fri, 4 Feb 2022 17:32:20 -0400 Subject: [PATCH 079/144] add numero de protocolo a consulta de atendimentos --- app/Models/Atendimento.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/Models/Atendimento.php b/app/Models/Atendimento.php index dd40b17..5d877a2 100644 --- a/app/Models/Atendimento.php +++ b/app/Models/Atendimento.php @@ -49,12 +49,14 @@ class Atendimento extends Model { $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 WHERE ma.matricula = :matricula ORDER BY status DESC, data_reg DESC LIMIT 10"; From 8dd6255ebb8f1b9e36b93cd315ca49332dd74a8f Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Mon, 7 Feb 2022 16:23:13 -0400 Subject: [PATCH 080/144] =?UTF-8?q?erro=20de=20conex=C3=A3o=20com=20atende?= =?UTF-8?q?nte=20com=20evento?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Controllers/AgentController.php | 11 ++---- app/Controllers/BilheteController.php | 3 -- app/Controllers/MessageController.php | 1 - app/Middleware/ApiAgente.php | 49 ++++++++++++++++++++++++--- app/Models/Atendimento.php | 20 ++++++++--- websocket/Servidorsocket.php | 5 ++- 6 files changed, 67 insertions(+), 22 deletions(-) diff --git a/app/Controllers/AgentController.php b/app/Controllers/AgentController.php index 98f8d19..67d736a 100644 --- a/app/Controllers/AgentController.php +++ b/app/Controllers/AgentController.php @@ -171,13 +171,13 @@ class AgentController extends Controller return false; } - public function logoff($matricula) + public function logoff($matricula, $valida = true) { try { $this->agent->begin(); $agent = $this->agent->findAgentByMatricula($matricula); if (!$agent) { - throw new Exception('Telefone não encontrado!'); + throw new Exception('Agente não encontrado!'); } $queue = $this->queue->findQueueByName($agent->fila); @@ -185,12 +185,7 @@ class AgentController extends Controller throw new Exception('Agente não conectado!'); } - if ($agent->status == CONF_AGENT_STATUS_PAUSA) { - $this->message('Agente removido de pausa!'); - $this->pause->updateEventoOutPause($agent->matricula, $queue->id); - } - - if ($agent->status != CONF_AGENT_STATUS_LIVRE) { + if ($agent->status != CONF_AGENT_STATUS_LIVRE && $valida) { throw new Exception('Saia da pausa para fazer logoff'); } diff --git a/app/Controllers/BilheteController.php b/app/Controllers/BilheteController.php index 9f6649f..ef1ecd3 100644 --- a/app/Controllers/BilheteController.php +++ b/app/Controllers/BilheteController.php @@ -136,7 +136,6 @@ class BilheteController extends Controller { try { $this->protocol->begin(); - $param = $this->parametros->findProtocolByParams($uniqueid); $useProtocolo = $param->prm_agente_proto; $useProtoParceiro = $param->prm_use_proto_parceiro; @@ -145,10 +144,8 @@ class BilheteController extends Controller $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) { diff --git a/app/Controllers/MessageController.php b/app/Controllers/MessageController.php index d7872ae..c1f093b 100644 --- a/app/Controllers/MessageController.php +++ b/app/Controllers/MessageController.php @@ -43,7 +43,6 @@ class MessageController extends Controller return "ALERT"; } } - print('nada'); return false; } catch (Exception $ex) { $this->message->rollback(); diff --git a/app/Middleware/ApiAgente.php b/app/Middleware/ApiAgente.php index dcc04b6..d5d10d7 100644 --- a/app/Middleware/ApiAgente.php +++ b/app/Middleware/ApiAgente.php @@ -134,14 +134,24 @@ class ApiAgente implements IApi return null; } - function logoff($request) + function logoff($request, $valida = true) { try { $agente = $this->supervisor->findAgentByMatricula($request['matricula']); //verifica se existe agente $atends = $this->atendimento->getAtendimentoAbertoByAgente($request['matricula']); - if (!empty($atends)) { - $this->retorno("Atendimentos em aberto, finalize os atendimentos para fazer logoff'"); + 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)) { @@ -149,7 +159,8 @@ class ApiAgente implements IApi return; } $ret = $this->agentController->logoff( - $request['matricula'] + $request['matricula'], + $valida ); if (is_string($ret)) { $this->retorno($ret); @@ -264,6 +275,36 @@ class ApiAgente implements IApi 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 + ); + $systemController = new SystemMessageController(); + $provedor = new Positus(); + $systemController->sendMessageSystem( + CONF_MOMENT_ERRO_ATEND, + [], + $provedor, + $client_id + ); + $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(); diff --git a/app/Models/Atendimento.php b/app/Models/Atendimento.php index 5d877a2..3423d6e 100644 --- a/app/Models/Atendimento.php +++ b/app/Models/Atendimento.php @@ -85,15 +85,25 @@ class Atendimento extends Model return $this->read($this->query, ['matricula' => $matricula])->fetchAll(); } - public function getAtendFila($fila, $evento = 'EMESPERA') + // 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 - 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) = 'EMESPERA' "; - return $this->read($this->query, ['evento' => $evento, 'fila' => $fila])->fetchAll(); + 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 createAtendimento($matricula, $cliente_id, $direcao, $context, $nome) diff --git a/websocket/Servidorsocket.php b/websocket/Servidorsocket.php index 14c0105..41d8e53 100644 --- a/websocket/Servidorsocket.php +++ b/websocket/Servidorsocket.php @@ -5,6 +5,7 @@ namespace websocket; include __DIR__ . '/../Providers/Positus.php'; use app\Middleware\ApiAgente; +use app\Models\Atendimento; use Ratchet\ConnectionInterface; use Ratchet\MessageComponentInterface; @@ -17,12 +18,14 @@ class Servidorsocket implements MessageComponentInterface public $idConexion = []; public $clientes = []; public $api; + public $atendimento; public function __construct() { $this->clients = new \SplObjectStorage(); $this->api = new ApiAgente(); + $this->atendimento = new Atendimento(); } public function onOpen(ConnectionInterface $conn) @@ -70,7 +73,7 @@ class Servidorsocket implements MessageComponentInterface // The connection is closed, remove it, as we can no longer send it messages $this->conectado[$conn->resourceId] = false; $this->clients->detach($conn); - $this->api->logoff($this->clientes[$conn->resourceId]); + $this->api->logoff($this->clientes[$conn->resourceId], false); } public function onError(ConnectionInterface $conn, \Exception $e) From 0095fcf1f039eae851d9c45274a4327413723500 Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Mon, 7 Feb 2022 16:24:41 -0400 Subject: [PATCH 081/144] =?UTF-8?q?remove=20valida=C3=A7=C3=A3o=20de=20err?= =?UTF-8?q?o=20na=20model?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Core/Model.php | 8 -------- 1 file changed, 8 deletions(-) diff --git a/app/Core/Model.php b/app/Core/Model.php index 1d18940..7a8d43b 100644 --- a/app/Core/Model.php +++ b/app/Core/Model.php @@ -71,9 +71,7 @@ abstract class Model return Connect::getInstance()->lastInsertId(); } catch (PDOException $ex) { Connect::setInstance(null); - sleep(5); logger('PDOExcep')->error('PDOException: ' . $ex->getMessage(), debug_backtrace()); - $this->create($query, $data); return null; } } @@ -87,9 +85,7 @@ abstract class Model return $stmt; } catch (PDOException $ex) { Connect::setInstance(null); - sleep(5); logger('PDOExcep')->error('PDOException: ' . $ex->getMessage(), debug_backtrace()); - $this->read($query, $data); return null; } } @@ -103,9 +99,7 @@ abstract class Model return ($stmt->rowCount() ? 1 : 0); } catch (PDOException $ex) { Connect::setInstance(null); - sleep(5); logger('PDOExcep')->error('PDOException: ' . $ex->getMessage(), debug_backtrace()); - $this->delete($query, $data); return null; } } @@ -120,9 +114,7 @@ abstract class Model return ($stmt->rowCount() ? 1 : 0); } catch (PDOException $ex) { Connect::setInstance(null); - sleep(5); logger('PDOExcep')->error('PDOException: ' . $ex->getMessage(), debug_backtrace()); - $this->update($query, $data); return null; } } From f3341736c4ea23fb64985e55896a74fc66357f53 Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Mon, 7 Feb 2022 17:30:46 -0400 Subject: [PATCH 082/144] =?UTF-8?q?add=20m=C3=A9todo=20listar=20atendiment?= =?UTF-8?q?os=20abandonados?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Middleware/ApiAgente.php | 26 ++++++++++++++++++++++++++ app/Models/Atendimento.php | 16 ++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/app/Middleware/ApiAgente.php b/app/Middleware/ApiAgente.php index d5d10d7..ff13bee 100644 --- a/app/Middleware/ApiAgente.php +++ b/app/Middleware/ApiAgente.php @@ -94,6 +94,9 @@ class ApiAgente implements IApi case 'listarAtendimentosFilas': $this->listarAtendimentosFilas($request); break; + case 'listarAtendimentosAbandonado': + $this->listarAtendimentosAbandonado($request); + break; default: echo json_encode(['status' => '404']); break; @@ -698,4 +701,27 @@ class ApiAgente implements IApi 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ã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()); + } + } } \ No newline at end of file diff --git a/app/Models/Atendimento.php b/app/Models/Atendimento.php index 3423d6e..eb4ee7e 100644 --- a/app/Models/Atendimento.php +++ b/app/Models/Atendimento.php @@ -106,6 +106,22 @@ class Atendimento extends Model 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); From e0a545d38a37d7938e61363844f5cc2c026581b2 Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Tue, 8 Feb 2022 09:27:08 -0400 Subject: [PATCH 083/144] add log de mensagens da positus --- app/Core/CoreMedia.php | 1 + app/Providers/Positus.php | 3 --- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/app/Core/CoreMedia.php b/app/Core/CoreMedia.php index f74a7d6..228b3e9 100644 --- a/app/Core/CoreMedia.php +++ b/app/Core/CoreMedia.php @@ -72,6 +72,7 @@ class CoreMedia if (!$this->api->getIsValidMessage()) { return null; } + logger('positus')->info(print_r($data, true)); if (!$this->api->baixarMidia()) { $this->api->enviarMsg($this->api->getPhone(), "Não foi possivel entregar o aquivo para o agente. Envie novamente!"); } diff --git a/app/Providers/Positus.php b/app/Providers/Positus.php index 2792363..ac88aab 100644 --- a/app/Providers/Positus.php +++ b/app/Providers/Positus.php @@ -129,7 +129,6 @@ class Positus implements IApiMedia $this->requestType("POST"); $this->setMetodo('messages'); $ret = $this->exec(); - logger('positus')->info(print_r($ret, true)); return $ret; } return false; @@ -177,7 +176,6 @@ class Positus implements IApiMedia $this->requestType("POST"); $this->setMetodo('messages'); $ret = $this->exec(); - logger('positus')->info(print_r($ret, true)); return $ret; } return false; @@ -342,7 +340,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']; From 788aae293aaf92419144d9aca1ab9c33f21d60cd Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Tue, 8 Feb 2022 09:27:35 -0400 Subject: [PATCH 084/144] add parametro de quantidade e listar todos no listaatendimentos agente --- app/Middleware/ApiAgente.php | 15 ++++++++------- app/Models/Atendimento.php | 21 ++++++++++++++------- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/app/Middleware/ApiAgente.php b/app/Middleware/ApiAgente.php index ff13bee..d069462 100644 --- a/app/Middleware/ApiAgente.php +++ b/app/Middleware/ApiAgente.php @@ -446,14 +446,15 @@ class ApiAgente implements IApi function listaAtendimentoAgent($request) { - $agente = $this->supervisor->findAgentByMatricula($request['matricula']); - //verifica se existe agente - if (empty($agente)) { - $this->retorno("Agente não encontrado"); - return; + if ($request['matricula']) { + $agente = $this->supervisor->findAgentByMatricula($request['matricula']); + //verifica se existe agente + if (empty($agente)) { + $this->retorno("Agente não encontrado"); + return; + } } - - $ret = $this->atendimento->findAtendAgent($request['matricula']); + $ret = $this->atendimento->findAtendAgent($request['matricula'], $request['quantidade']); $this->retorno( $ret ? "Sucesso" : "Erro", $ret ? $ret : null, diff --git a/app/Models/Atendimento.php b/app/Models/Atendimento.php index eb4ee7e..14646ba 100644 --- a/app/Models/Atendimento.php +++ b/app/Models/Atendimento.php @@ -45,8 +45,9 @@ class Atendimento extends Model return $this->read($this->query, ['evento' => $evento, 'fila' => $fila])->fetchAll(); } - public function findAtendAgent($matricula) + public function findAtendAgent($matricula, $quantidade = 10) { + $data = []; $this->query = "SELECT ma.*, ma.nome AS profile_name, ppr.protocolo as protocolo, @@ -56,11 +57,17 @@ class Atendimento extends Model ELSE 0 END AS status FROM {$this->atendimento} ma - INNER JOIN pbx_protocolo_reg ppr ON ma.uniqueid = ppr.uniqueid - WHERE ma.matricula = :matricula - ORDER BY status DESC, data_reg DESC - LIMIT 10"; - return $this->read($this->query, ['matricula' => $matricula])->fetchAll(); + INNER JOIN pbx_protocolo_reg ppr ON ma.uniqueid = ppr.uniqueid "; + if ($matricula) { + $this->query .= " WHERE ma.matricula = :matricula "; + $data['matricula'] = $matricula; + } + + $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) @@ -116,7 +123,7 @@ class Atendimento extends Model 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'; + $this->query .= ' AND me.fila = :fila'; $data['fila'] = $fila; } return $this->read($this->query, $data)->fetchAll(); From 96a9c298927ff3a12be3f500cd6884593df94dd1 Mon Sep 17 00:00:00 2001 From: lucascardo12 Date: Tue, 8 Feb 2022 10:24:51 -0400 Subject: [PATCH 085/144] =?UTF-8?q?valida=C3=A7=C3=A3o=20no=20atendimento?= =?UTF-8?q?=20e=20api=20de=20listar=20atendimentos?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Core/CoreMedia.php | 3 +++ app/Models/Atendimento.php | 6 +++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/app/Core/CoreMedia.php b/app/Core/CoreMedia.php index 228b3e9..b364a5e 100644 --- a/app/Core/CoreMedia.php +++ b/app/Core/CoreMedia.php @@ -113,6 +113,9 @@ class CoreMedia } //verifica se tem atendimento em espera, se tiver ja mostra a sua posiçã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; diff --git a/app/Models/Atendimento.php b/app/Models/Atendimento.php index 14646ba..6feb57b 100644 --- a/app/Models/Atendimento.php +++ b/app/Models/Atendimento.php @@ -62,8 +62,12 @@ class Atendimento extends Model $this->query .= " WHERE ma.matricula = :matricula "; $data['matricula'] = $matricula; } + if (empty($quantidade)) { + $data['quantidade'] = 10; + } else { + $data['quantidade'] = $quantidade; + } - $data['quantidade'] = $quantidade; $this->query .= " ORDER BY status DESC, data_reg DESC LIMIT :quantidade "; From 2592338f560c26d598c6cf6947f17404c44ef042 Mon Sep 17 00:00:00 2001 From: lucas cardoso Date: Tue, 8 Feb 2022 16:23:55 -0400 Subject: [PATCH 086/144] ordenar lista de filas --- app/Models/Queue.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Models/Queue.php b/app/Models/Queue.php index 427c3e6..72dfb50 100644 --- a/app/Models/Queue.php +++ b/app/Models/Queue.php @@ -25,8 +25,8 @@ class Queue extends Model $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) From e8984f821eae611dea85ac538a2e772442c3140c Mon Sep 17 00:00:00 2001 From: lucas cardoso Date: Wed, 9 Feb 2022 09:29:13 -0400 Subject: [PATCH 087/144] clean code in servicequeue --- service/ServiceQueue.php | 35 -------------------------------- service/ServiceSupervisorPBx.php | 1 - 2 files changed, 36 deletions(-) diff --git a/service/ServiceQueue.php b/service/ServiceQueue.php index 3958fac..3c1dfff 100644 --- a/service/ServiceQueue.php +++ b/service/ServiceQueue.php @@ -69,39 +69,4 @@ class ServiceQueue implements IService break; } } - - function timeoutAgente($item, $conn) - { - $freetime = $item['FREETIME'] / 60; - if (($freetime >= $this->timeout_agent_alert) && ($freetime < $this->timeout_agent_desconexao) && !$item['SALA2'] && $item['STATUS'] == CONF_AGENT_STATUS_LIVRE) { - $this->agente->timeFinishAgente($item['MATRICULA'], $item['RAMAL'], ($this->timeout_agent_desconexao - $this->timeout_agent_alert)); - $conn->send($this->enviaActions($this->mensagem['ALERTA_INATIVIDADE'], 'ALERTA_INATIVIDADE')); - } else if ($freetime >= $this->timeout_agent_desconexao && $item['STATUS'] == CONF_AGENT_STATUS_LIVRE) { - $this->agente->logoff($item['RAMAL']); - $conn->send($this->enviaActions($this->mensagem['ALERTA_DESCONECTADO'], 'ALERTA_DESCONECTADO')); - } else { - $this->agente->refreshAgent($item['RAMAL'], $item['SALA2']); - } - } - - function enviaActions($msg, $tipo) - { - try { - $mensagem = []; - $mensagem["event"] = [ - "type" => 'actions', - "contact" => [ - "name" => 'Sistema', - "number" => '0' - ], - "mensagem" => [ - "type" => $tipo, - "content" => utf8_encode($msg) - ], - ]; - return json_encode($mensagem); - } catch (\Exception $th) { - logger('monitora')->info($th->getMessage(), debug_backtrace()); - } - } } \ No newline at end of file diff --git a/service/ServiceSupervisorPBx.php b/service/ServiceSupervisorPBx.php index 1e95cd9..c48b816 100644 --- a/service/ServiceSupervisorPBx.php +++ b/service/ServiceSupervisorPBx.php @@ -25,7 +25,6 @@ class ServiceSupervisorPBx implements IService function atualizaTabelaSupervisor($agente, $agentePbx) { - $this->supervisor->updateAgent2( $agente->matricula, $agente->ramal, From 858156a249ba988ad338480b042f0f9df7c46754 Mon Sep 17 00:00:00 2001 From: lucas cardoso Date: Wed, 9 Feb 2022 10:19:26 -0400 Subject: [PATCH 088/144] mudando a model supervisor para supervisorModel --- app/Controllers/AgentController.php | 6 +++--- app/Controllers/SystemMessageController.php | 1 + app/Core/Commands.php | 6 +++--- app/Core/CoreMedia.php | 8 ++++---- app/Middleware/ApiAgente.php | 6 +++--- app/Middleware/ApiSupervisor.php | 6 +++--- .../{Supervisor.php => SupervisorModel.php} | 3 +-- service/ServiceQueue.php | 6 +++--- service/ServiceSupervisorPBx.php | 16 +++++++++------- 9 files changed, 30 insertions(+), 28 deletions(-) rename app/Models/{Supervisor.php => SupervisorModel.php} (99%) diff --git a/app/Controllers/AgentController.php b/app/Controllers/AgentController.php index 67d736a..b911c13 100644 --- a/app/Controllers/AgentController.php +++ b/app/Controllers/AgentController.php @@ -13,7 +13,7 @@ use app\Controllers\ClassificacaoController; use app\Models\Atendimento; use app\Models\Evento; use app\Models\Message; -use app\Models\Supervisor; +use app\Models\SupervisorModel; use app\Providers\Positus; use Exception; use websocket\WsInterface; @@ -36,7 +36,7 @@ class AgentController extends Controller public function __construct() { - $this->agent = new Supervisor(); + $this->agent = new SupervisorModel(); $this->queue = new Queue(); $this->ramal = new Ramal(); $this->pause = new Pause(); @@ -567,4 +567,4 @@ class AgentController extends Controller return false; } } -} \ No newline at end of file +} diff --git a/app/Controllers/SystemMessageController.php b/app/Controllers/SystemMessageController.php index 93d5eda..10c4177 100644 --- a/app/Controllers/SystemMessageController.php +++ b/app/Controllers/SystemMessageController.php @@ -17,6 +17,7 @@ class SystemMessageController extends Controller private $sysMessage; private $supervisorqueue; + public function __construct() { $this->sysMessage = new SystemMessage(); diff --git a/app/Core/Commands.php b/app/Core/Commands.php index 93de1a7..65fd22d 100644 --- a/app/Core/Commands.php +++ b/app/Core/Commands.php @@ -9,7 +9,7 @@ use app\Interfaces\IApiMedia; use app\Models\Atendimento; use app\Models\Evento; use app\Models\Message; -use app\Models\Supervisor; +use app\Models\SupervisorModel; use websocket\WsInterface; /** @@ -80,7 +80,7 @@ class Commands try { $atendimentoModel = new Atendimento(); $eventosModel = new Evento(); - $supervisorModel = new Supervisor(); + $supervisorModel = new SupervisorModel(); $atendimento = $atendimentoModel->getAtendimentoByCliente($numero, CONF_EVENT_START); //verifica se existe atendimento if (empty($atendimento)) { @@ -164,4 +164,4 @@ class Commands return true; } } -} \ No newline at end of file +} diff --git a/app/Core/CoreMedia.php b/app/Core/CoreMedia.php index b364a5e..023bb75 100644 --- a/app/Core/CoreMedia.php +++ b/app/Core/CoreMedia.php @@ -14,7 +14,7 @@ use app\Models\Atendimento; use app\Models\Evento; use app\Models\ListaNegraPalavras; use app\Models\Ramal; -use app\Models\Supervisor; +use app\Models\SupervisorModel; use websocket\WsInterface; /** @@ -49,7 +49,7 @@ class CoreMedia $this->atendimento = new Atendimento(); $this->message = new Message(); $this->palavroes = new ListaNegraPalavras(); - $this->supervisor = new Supervisor(); + $this->supervisor = new SupervisorModel(); $this->eventos = new Evento(); $this->bilheteController = new BilheteController; $this->systemController = new SystemMessageController(); @@ -214,7 +214,7 @@ class CoreMedia $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)) { $atendimentosAbertos = $this->atendimento->getAtendimentoAbertoByAgente($agent[0]->matricula); - $sup = new Supervisor(); + $sup = new SupervisorModel(); $param = $this->atendimento->getQuantiAtendimentSimultaneos(); $ws = new WsInterface(); if ((count($atendimentosAbertos)) >= $param->prm_media_simultaneo) { @@ -288,4 +288,4 @@ class CoreMedia return null; } } -} \ No newline at end of file +} diff --git a/app/Middleware/ApiAgente.php b/app/Middleware/ApiAgente.php index d069462..4546747 100644 --- a/app/Middleware/ApiAgente.php +++ b/app/Middleware/ApiAgente.php @@ -14,7 +14,7 @@ use app\Models\Message; use app\Models\Parametros; use app\Models\Pause; use app\Models\Queue; -use app\Models\Supervisor; +use app\Models\SupervisorModel; use app\Providers\Positus; use websocket\WsInterface; @@ -33,7 +33,7 @@ class ApiAgente implements IApi $this->queue = new QueueController(); $this->classificacao = new ClassificacaoController(); $this->agentController = new AgentController(); - $this->supervisor = new Supervisor(); + $this->supervisor = new SupervisorModel(); $this->atendimento = new Atendimento(); $this->eventos = new Evento(); $this->pausasModel = new Pause(); @@ -725,4 +725,4 @@ class ApiAgente implements IApi return $this->retorno($th->getMessage()); } } -} \ No newline at end of file +} diff --git a/app/Middleware/ApiSupervisor.php b/app/Middleware/ApiSupervisor.php index b75461c..b166609 100644 --- a/app/Middleware/ApiSupervisor.php +++ b/app/Middleware/ApiSupervisor.php @@ -3,7 +3,7 @@ namespace app\Middleware; use app\Interfaces\IApi; -use app\Models\Supervisor; +use app\Models\SupervisorModel; class ApiSupervisor implements IApi @@ -12,7 +12,7 @@ class ApiSupervisor implements IApi public function __construct() { - $this->supervisor = new Supervisor(); + $this->supervisor = new SupervisorModel(); } function router($rota, $request) @@ -60,4 +60,4 @@ class ApiSupervisor implements IApi } echo json_encode($data); } -} \ No newline at end of file +} diff --git a/app/Models/Supervisor.php b/app/Models/SupervisorModel.php similarity index 99% rename from app/Models/Supervisor.php rename to app/Models/SupervisorModel.php index 8bb7769..859d7a9 100644 --- a/app/Models/Supervisor.php +++ b/app/Models/SupervisorModel.php @@ -2,10 +2,9 @@ namespace app\Models; -use app\Core\Connect; use app\Core\Model; -class Supervisor extends Model +class SupervisorModel extends Model { const USUARIOS = "pbx_usuarios"; diff --git a/service/ServiceQueue.php b/service/ServiceQueue.php index 3c1dfff..3180736 100644 --- a/service/ServiceQueue.php +++ b/service/ServiceQueue.php @@ -6,7 +6,7 @@ use app\Core\Connect; use app\Core\CoreMedia; use app\Providers\Positus; use app\Models\Atendimento; -use app\Models\Supervisor; +use app\Models\SupervisorModel; use app\Providers\ApiTelegram; class ServiceQueue implements IService @@ -21,7 +21,7 @@ class ServiceQueue implements IService function __construct() { - $this->supervisor = new Supervisor(); + $this->supervisor = new SupervisorModel(); $this->atendimento = new Atendimento(); $this->coremedia = new CoreMedia(); $this->timeout_agent_alert = (CONF_WHATSAPP_TIMEOUT_AGENT_AVISO / 60); @@ -69,4 +69,4 @@ class ServiceQueue implements IService break; } } -} \ No newline at end of file +} diff --git a/service/ServiceSupervisorPBx.php b/service/ServiceSupervisorPBx.php index c48b816..33d22d1 100644 --- a/service/ServiceSupervisorPBx.php +++ b/service/ServiceSupervisorPBx.php @@ -4,15 +4,17 @@ namespace service; use app\Models\Atendimento; use app\Models\Parametros; -use app\Models\Supervisor; +use app\Models\SupervisorModel; class ServiceSupervisorPBx implements IService { - private $supervisor; + /** @var SupervisorModel $supervisorModel model do supervisor */ + private $supervisorModel; + function run() { - $this->supervisor = new Supervisor; - $agentes = $this->supervisor->listaAgentesDisponivel(); + $this->supervisorModel = new SupervisorModel; + $agentes = $this->supervisorModel->listaAgentesDisponivel(); foreach ($agentes as $key => $agente) { $agentPbx = $this->validaAgentCriado($agente->matricula); if (!$agentPbx) { @@ -25,7 +27,7 @@ class ServiceSupervisorPBx implements IService function atualizaTabelaSupervisor($agente, $agentePbx) { - $this->supervisor->updateAgent2( + $this->supervisorModel->updateAgent2( $agente->matricula, $agente->ramal, $agente->fila, @@ -38,7 +40,7 @@ class ServiceSupervisorPBx implements IService function criaRegistroSupervisor($agente) { - $this->supervisor->addAgent2( + $this->supervisorModel->addAgent2( $agente->nome, $agente->matricula, $agente->ramal, @@ -62,6 +64,6 @@ class ServiceSupervisorPBx implements IService function validaAgentCriado($matricula) { - return $this->supervisor->findAgentByMatriculaPbx($matricula); + return $this->supervisorModel->findAgentByMatriculaPbx($matricula); } } \ No newline at end of file From 1dd343bb8ae969fbcb63384f93f56e7fa01a38d4 Mon Sep 17 00:00:00 2001 From: lucas cardoso Date: Wed, 9 Feb 2022 16:01:47 -0400 Subject: [PATCH 089/144] documentando vars no ServiceTimeout --- service/ServiceTimeout.php | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/service/ServiceTimeout.php b/service/ServiceTimeout.php index 926a37e..b1890fd 100644 --- a/service/ServiceTimeout.php +++ b/service/ServiceTimeout.php @@ -11,16 +11,29 @@ use app\Providers\Positus; class ServiceTimeout implements IService { - private $atendimentos; - private $timeout_agent_alert = (CONF_WHATSAPP_TIMEOUT_AGENT_AVISO / 60); - private $timeout_agent_desconexao = (CONF_WHATSAPP_TIMEOUT_AGENT_DESCONEXAO / 60); - private $timeout_agent_classificacao = (CONF_WHATSAPP_TIMEOUT_AGENT_CLASSIFICACAO / 60); + /** @var String[] $mensagem lista de mensagens para o sistema*/ private $mensagem; + + /** @var Positus $positus provider positus*/ private $positus; + + /** @var MessageController $message controller de message*/ private $message; + + /** @var NotificaMedia $notificaMedia model de NotificaMedia*/ private $notificaMedia; + + /** @var Message $mensages model de Message*/ private $mensages; + + /** @var Commands $command core Commands */ private $command; + + /** @var Atendimento $atendimentos model de atendimento*/ + private $atendimentos; + private $timeout_agent_alert = (CONF_WHATSAPP_TIMEOUT_AGENT_AVISO / 60); + private $timeout_agent_desconexao = (CONF_WHATSAPP_TIMEOUT_AGENT_DESCONEXAO / 60); + function __construct() { $this->positus = new Positus(); @@ -41,13 +54,10 @@ class ServiceTimeout implements IService public function run() { $this->command->setApi($this->positus); - // while (true) { $atendiss = $this->atendimentos->findAtenEmAberto(); foreach ($atendiss as $value) { $this->timeoutCliente($value->uniqueid, $value->cliente_id); } - //sleep(60); - // } } private function timeoutCliente($uniqueid, $client) { From a935ca3527970f3bd44f645abe1307cabe94ce44 Mon Sep 17 00:00:00 2001 From: lucas cardoso Date: Wed, 9 Feb 2022 16:08:52 -0400 Subject: [PATCH 090/144] documentando vars no ServiceQueue --- service/ServiceQueue.php | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/service/ServiceQueue.php b/service/ServiceQueue.php index 3180736..d2df5d0 100644 --- a/service/ServiceQueue.php +++ b/service/ServiceQueue.php @@ -2,7 +2,6 @@ namespace service; -use app\Core\Connect; use app\Core\CoreMedia; use app\Providers\Positus; use app\Models\Atendimento; @@ -11,22 +10,27 @@ use app\Providers\ApiTelegram; class ServiceQueue implements IService { + /** @var SupervisorModel $supervisor model de supervisor */ public $supervisor; + + /** @var Atendimento $atendimento model de Atendimento */ public $atendimento; - public $timeout_agent_alert; - public $timeout_agent_desconexao; - public $timeout_agent_classificacao; + + /** @var String[] $mensagem lista de mensagens do sistema */ public $mensagem; + + /** @var CoreMedia $coremedia core de atendimento */ public $coremedia; + public $timeout_agent_alert = (CONF_WHATSAPP_TIMEOUT_AGENT_AVISO / 60); + public $timeout_agent_desconexao = (CONF_WHATSAPP_TIMEOUT_AGENT_DESCONEXAO / 60); + public $timeout_agent_classificacao = (CONF_WHATSAPP_TIMEOUT_AGENT_CLASSIFICACAO / 60); + function __construct() { $this->supervisor = new SupervisorModel(); $this->atendimento = new Atendimento(); $this->coremedia = new CoreMedia(); - $this->timeout_agent_alert = (CONF_WHATSAPP_TIMEOUT_AGENT_AVISO / 60); - $this->timeout_agent_desconexao = (CONF_WHATSAPP_TIMEOUT_AGENT_DESCONEXAO / 60); - $this->timeout_agent_classificacao = (CONF_WHATSAPP_TIMEOUT_AGENT_CLASSIFICACAO / 60); $this->mensagem = [ "ALERTA_INATIVIDADE" => "Você será desconectado em " . ($this->timeout_agent_desconexao - $this->timeout_agent_alert) . " minutos, para permanecer em atendimento clique em '/presente'", @@ -69,4 +73,4 @@ class ServiceQueue implements IService break; } } -} +} \ No newline at end of file From 5ad3560c5920db06287e898ea5dd1eea5a540c98 Mon Sep 17 00:00:00 2001 From: lucas cardoso Date: Wed, 9 Feb 2022 16:21:43 -0400 Subject: [PATCH 091/144] documentando vars no ApiAgente --- app/Controllers/AgentController.php | 3 +-- app/Middleware/ApiAgente.php | 30 ++++++++++++++++++++--------- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/app/Controllers/AgentController.php b/app/Controllers/AgentController.php index b911c13..f23d8fc 100644 --- a/app/Controllers/AgentController.php +++ b/app/Controllers/AgentController.php @@ -25,7 +25,6 @@ use websocket\WsInterface; */ class AgentController extends Controller { - private $agent; private $queue; private $ramal; @@ -567,4 +566,4 @@ class AgentController extends Controller return false; } } -} +} \ No newline at end of file diff --git a/app/Middleware/ApiAgente.php b/app/Middleware/ApiAgente.php index 4546747..ba2a5ad 100644 --- a/app/Middleware/ApiAgente.php +++ b/app/Middleware/ApiAgente.php @@ -20,17 +20,29 @@ use websocket\WsInterface; class ApiAgente implements IApi { - public $agentController; - public $classificacao; - public $supervisor; - public $atendimento; - public $eventos; - public $parametros; - public $pausasModel; + /** @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->queue = new QueueController(); $this->classificacao = new ClassificacaoController(); $this->agentController = new AgentController(); $this->supervisor = new SupervisorModel(); @@ -725,4 +737,4 @@ class ApiAgente implements IApi return $this->retorno($th->getMessage()); } } -} +} \ No newline at end of file From fe3d0a7004b1b7a96bebfde6be810df9f11a2585 Mon Sep 17 00:00:00 2001 From: lucas cardoso Date: Wed, 9 Feb 2022 16:49:02 -0400 Subject: [PATCH 092/144] =?UTF-8?q?documentando=20vars=20no=20agentControl?= =?UTF-8?q?ler=20e=20removendo=20fun=C3=A7=C3=B5es=20n=C3=A3o=20=20utiliza?= =?UTF-8?q?da?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Controllers/AgentController.php | 243 +++------------------------- app/Middleware/ApiAgente.php | 15 -- app/Models/Evento.php | 3 +- 3 files changed, 21 insertions(+), 240 deletions(-) diff --git a/app/Controllers/AgentController.php b/app/Controllers/AgentController.php index f23d8fc..31930d4 100644 --- a/app/Controllers/AgentController.php +++ b/app/Controllers/AgentController.php @@ -3,7 +3,6 @@ namespace app\Controllers; use app\Core\Controller; -use app\Models\Agent; use app\Models\Queue; use app\Models\Ramal; use app\Models\Pause; @@ -25,13 +24,26 @@ use websocket\WsInterface; */ class AgentController extends Controller { - private $agent; - private $queue; - private $ramal; - private $pause; - private $bilhete; - private $eventqueue; - private $atendModel; + /** @var SupervisorModel $agent model de supervisor */ + protected $agent; + + /** @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() { @@ -44,84 +56,6 @@ class AgentController extends Controller $this->atendModel = new Atendimento(); } - public function openService($ramal, $origemDest, $uniqueid) - { - try { - $this->agent->begin(); - $agent = $this->agent->findAgentByRamal($ramal); - $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; - } - - public function closeService($telefone, $motivo, $media) - { - try { - $this->agent->begin(); - $atendimento = $this->agent->findByMatricula($telefone); - $agent = $this->agent->findAgentByRamal($atendimento->ramal); - if (!$atendimento || !$agent->origem_destino) { - throw new Exception('Você precisa estar em um atendimento para finalizar!'); - } - - $scr = $agent->origem_destino; - $dst = $agent->ramal; - $unique = ($agent->uniqueid) ? $agent->uniqueid : null; - - $billsec = strtotime($agent->logado) - strtotime($agent->duracao); - $duration = $billsec; - - $disposition = 'ANSWERED'; - $calldate = date('Y-m-d H:i:s'); - - $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_TIMERMINO_CLIENTE : CONF_EVENT_TIMERMINO_AGENTE; - } - $this->eventqueue->addEventQueue($uniqueid, $agent->dac, $agent->matricula, $motivo, $media); - /* - * VERIFICA CLASSIFICACAO ATENDIMENTO - */ - $classificacao = new ClassificacaoController(); - $chamadaSemClassificacao = $classificacao->agentClassificacaoPending($agent->matricula, $agent->dac); - - $this->agent->updateAgent($agent->matricula, $agent->ramal, CONF_AGENT_STATUS_LIVRE, null, null, 1, null, ($chamadaSemClassificacao) ? 0 : 1); - - $this->agent->commit(); - return [$dst, $scr]; - } catch (Exception $ex) { - $this->agent->rollback(); - $this->message($ex->getMessage()); - logger()->error($ex->getMessage()); - } - return false; - } - - public function inService($telefone) - { - try { - $atendimento = $this->agent->findByMatricula($telefone); - if (!$atendimento && $this->ramal->findRamal($telefone)) { - return false; - } else { - return ($atendimento ? $atendimento : false); - } - } catch (Exception $ex) { - logger()->error($ex->getMessage()); - return $ex->getMessage(); - } - return false; - } - public function login($fila, $matricula) { try { @@ -401,130 +335,6 @@ class AgentController extends Controller return false; } - public function priority($queue) - { - 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; - } - - 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; - } - 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) - { - 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; - } catch (Exception $ex) { - $this->agent->rollback(); - $this->message($ex->getMessage()); - logger()->error($ex->getMessage()); - } - return false; - } - - public function agentVerify($ramal) - { - 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; - } - - $this->agent->updateSala2Agent($matricula, $ramal, $timerFinish); - $this->agent->commit(); - return true; - } catch (Exception $ex) { - $this->agent->rollback(); - $this->message($ex->getMessage()); - logger()->error($ex->getMessage()); - } - return false; - } - public function getAgente($ramal) { try { @@ -553,17 +363,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; - } - } } \ No newline at end of file diff --git a/app/Middleware/ApiAgente.php b/app/Middleware/ApiAgente.php index ba2a5ad..aa3b4ba 100644 --- a/app/Middleware/ApiAgente.php +++ b/app/Middleware/ApiAgente.php @@ -61,9 +61,6 @@ class ApiAgente implements IApi case 'listarFilas': $this->listaFilas($request); break; - case 'refreshAgent': - $this->refreshAgent($request); - break; case 'classificarAtendimento': $this->classificarAtendimento($request); break; @@ -332,18 +329,6 @@ class ApiAgente implements IApi return null; } - public function refreshAgent($request) - { - $retorno = $this->agentController->refreshAgent($request['ramal'], $request['option']); - if (empty($retorno)) { - echo json_encode(['status' => "false"]); - } else { - echo json_encode(['status' => "Mensagem: " . utf8_encode($retorno)]); - } - - return null; - } - public function classificarAtendimento($request) { $agent = $this->agentController->getAgente($request['ramal']); diff --git a/app/Models/Evento.php b/app/Models/Evento.php index 28e7253..1fe7c17 100644 --- a/app/Models/Evento.php +++ b/app/Models/Evento.php @@ -2,7 +2,6 @@ namespace app\Models; -use app\Core\Connect; use app\Core\Model; class Evento extends Model @@ -42,4 +41,4 @@ class Evento extends Model AND evento in ('COMPLETE_AGENT', 'COMPLETE_AGENT');"; return $this->read($this->query, ['uniqueid' => $uniqueid])->fetch(); } -} +} \ No newline at end of file From 91551b735784f613e8b24936bb44d66e7837b4fc Mon Sep 17 00:00:00 2001 From: lucas cardoso Date: Wed, 9 Feb 2022 16:56:39 -0400 Subject: [PATCH 093/144] =?UTF-8?q?doc=20vars=20no=20BilheteControllere=20?= =?UTF-8?q?removendo=20fun=C3=A7=C3=B5es=20n=C3=A3o=20utilizada?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Controllers/BilheteController.php | 99 ++------------------------- 1 file changed, 4 insertions(+), 95 deletions(-) diff --git a/app/Controllers/BilheteController.php b/app/Controllers/BilheteController.php index ef1ecd3..683eb67 100644 --- a/app/Controllers/BilheteController.php +++ b/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_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_ESPERA); - if (!$bilhetesForaHorario) { - throw new Exception('Não foram encontrados atendimentos em espera!'); - } - - $this->bilhete->updateBilheteForaHorario($bilhetesForaHorario->uniqueid, $ramal, '0', CONF_EVENT_ABANDONADA); - $this->eventqueue->updateEventQueue($bilhetesForaHorario->uniqueid, $bilhetesForaHorario->fila, $bilhetesForaHorario->agente, CONF_EVENT_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) From 08151d12f2c945de3eeea445aa7ccc1f4455366f Mon Sep 17 00:00:00 2001 From: lucas cardoso Date: Wed, 9 Feb 2022 17:05:29 -0400 Subject: [PATCH 094/144] doc vars no ClassificacaoController --- app/Controllers/ClassificacaoController.php | 14 ++++++++------ app/Models/Agent.php | 1 - 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/app/Controllers/ClassificacaoController.php b/app/Controllers/ClassificacaoController.php index 15674e0..26c671d 100644 --- a/app/Controllers/ClassificacaoController.php +++ b/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; } -} +} \ No newline at end of file diff --git a/app/Models/Agent.php b/app/Models/Agent.php index e29071e..36ec898 100644 --- a/app/Models/Agent.php +++ b/app/Models/Agent.php @@ -2,7 +2,6 @@ namespace app\Models; -use app\Core\Connect; use app\Core\Model; /** From 32bfbd1577f6c28bef2e3a1202696d1bbad51fef Mon Sep 17 00:00:00 2001 From: lucas cardoso Date: Thu, 10 Feb 2022 14:33:10 -0400 Subject: [PATCH 095/144] add novo campo na tabela de supervisor agente --- database/att-v3.sql | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/database/att-v3.sql b/database/att-v3.sql index 3e587a7..7194ece 100644 --- a/database/att-v3.sql +++ b/database/att-v3.sql @@ -1,3 +1,8 @@ +ALTER TABLE + pbx_supervisor_agentes +ADD + media int NOT NULL DEFAULT 0; + -- pbx_eventos_agentes ALTER TABLE pbx_eventos_agentes From 72b181b0eeac9c48eb6520fe9269d40c24d54db5 Mon Sep 17 00:00:00 2001 From: lucas cardoso Date: Thu, 10 Feb 2022 14:39:27 -0400 Subject: [PATCH 096/144] atualizando status e o tipo media no supervisor agente --- app/Models/SupervisorModel.php | 36 +++++++++++++++++--------------- service/ServiceSupervisorPBx.php | 19 +++++++++++++---- 2 files changed, 34 insertions(+), 21 deletions(-) diff --git a/app/Models/SupervisorModel.php b/app/Models/SupervisorModel.php index 859d7a9..28ea055 100644 --- a/app/Models/SupervisorModel.php +++ b/app/Models/SupervisorModel.php @@ -15,7 +15,7 @@ class SupervisorModel extends Model private $supervisor = 'md_supervisor'; private $atendimento = 'md_atendimento'; - public function listaAgentesDisponivel($status = null) + public function listaAgentesDisponivel($status = null, $somenteLivre = true) { $data = []; $this->query = " SELECT *, @@ -31,7 +31,10 @@ class SupervisorModel extends Model ) AS countAtendimentos FROM md_supervisor ms - WHERE ( + WHERE 1=1 + "; + if ($somenteLivre) { + $this->query .= " AND ( SELECT count(*) FROM @@ -41,8 +44,8 @@ class SupervisorModel extends Model ORDER BY m2.id DESC LIMIT 1) AND ma.matricula = ms.matricula - ) < (SELECT prm_media_simultaneo FROM pbx_parametros pp LIMIT 1 ) - "; + ) < (SELECT prm_media_simultaneo FROM pbx_parametros pp LIMIT 1 )"; + } if ($status) { $this->query .= " AND ms.status = :status "; $data['status'] = $status; @@ -186,8 +189,8 @@ class SupervisorModel extends Model $status = 'LIVRE', $motivoPausa = null, $origemDestino = null, + $media = 1, $modo_atendimento = 'Automatico', - $uniqueid = null, $chamada_classificado = 1 ) { $data = []; @@ -199,12 +202,12 @@ class SupervisorModel extends Model origem_destino, status, motivo_pausa, - uniqueid, chamada_classificado, modo_atendimento, dac, duracao, - logado) VALUES( + logado, + media) VALUES( :tempo_login, :nome, :ramal, @@ -212,12 +215,12 @@ class SupervisorModel extends Model :origem_destino, :status, :motivo_pausa, - :uniqueid, :chamada_classificado, :modo_atendimento, :dac, :duracao, - :logado) "; + :logado, + :media) "; $data['tempo_login'] = $tempo_login; $data['nome'] = $nome; $data['matricula'] = $matricula; @@ -228,7 +231,7 @@ class SupervisorModel extends Model $data['origem_destino'] = $origemDestino; $data['status'] = $status; $data['motivo_pausa'] = $motivoPausa; - $data['uniqueid'] = $uniqueid; + $data['media'] = $media; $data['modo_atendimento'] = $modo_atendimento; $data['chamada_classificado'] = ($chamada_classificado ? '1' : '0'); return $this->create($this->query, $data); @@ -258,18 +261,17 @@ class SupervisorModel extends Model $atualizaDuracao = false, $motivoPausa = null, $origemDestino = null, - $uniqueid = null, - $chamada_classificado = 1 + $disponivel_atendimento = 1 ) { $data = []; $this->query = "UPDATE pbx_supervisor_agentes SET origem_destino = :origem_destino, status = :status, motivo_pausa = :motivo_pausa, - uniqueid = :uniqueid, - chamada_classificado = :chamada_classificado, dac = :dac, - ramal = :ramal "; + ramal = :ramal, + logado = :logado, + disponivel_atendimento = :disponivel_atendimento "; if ($atualizaDuracao) { $data['duracao'] = 'now()'; $this->query .= " , duracao = :duracao"; @@ -280,9 +282,9 @@ class SupervisorModel extends Model $data['dac'] = $dac; $data['origem_destino'] = $origemDestino; $data['status'] = $status; + $data['logado'] = 'now()'; $data['motivo_pausa'] = $motivoPausa; - $data['uniqueid'] = $uniqueid; - $data['chamada_classificado'] = ($chamada_classificado ? '1' : '0'); + $data['disponivel_atendimento'] = $disponivel_atendimento; return $this->update($this->query, $data); } diff --git a/service/ServiceSupervisorPBx.php b/service/ServiceSupervisorPBx.php index 33d22d1..a61195f 100644 --- a/service/ServiceSupervisorPBx.php +++ b/service/ServiceSupervisorPBx.php @@ -14,7 +14,7 @@ class ServiceSupervisorPBx implements IService function run() { $this->supervisorModel = new SupervisorModel; - $agentes = $this->supervisorModel->listaAgentesDisponivel(); + $agentes = $this->supervisorModel->listaAgentesDisponivel(null, false); foreach ($agentes as $key => $agente) { $agentPbx = $this->validaAgentCriado($agente->matricula); if (!$agentPbx) { @@ -32,9 +32,10 @@ class ServiceSupervisorPBx implements IService $agente->ramal, $agente->fila, $agente->status, - $agente->motivo_pausa, $agente->status != $agentePbx->status, - $this->retornaQuantidadeAtendimento($agente->matricula) + $agente->motivo_pausa, + $this->retornaQuantidadeAtendimento($agente->matricula), + $this->statusDisponivelFila($agente) ); } @@ -52,13 +53,16 @@ class ServiceSupervisorPBx implements IService ); } - function retornaQuantidadeAtendimento($matricula) + function retornaQuantidadeAtendimento($matricula, $retornaBool = false) { $atendimentoModel = new Atendimento(); $paratroModel = new Parametros(); $atendimentos = $atendimentoModel->getAtendimentoAbertoByAgente($matricula); $parametros = $paratroModel->findProtocolByParams(); $count = count($atendimentos); + if ($retornaBool) { + return $count < $parametros->prm_media_simultaneo; + } return "$count/{$parametros->prm_media_simultaneo}"; } @@ -66,4 +70,11 @@ class ServiceSupervisorPBx implements IService { return $this->supervisorModel->findAgentByMatriculaPbx($matricula); } + function statusDisponivelFila($agente) + { + if ($agente->status == 'LIVRE' && $this->retornaQuantidadeAtendimento($agente->matricula, true)) { + return 1; + } + return 0; + } } \ No newline at end of file From 36954c09a547eb4c6de09fa16cc6d83c1ae5bef3 Mon Sep 17 00:00:00 2001 From: lucas cardoso Date: Thu, 10 Feb 2022 14:46:24 -0400 Subject: [PATCH 097/144] limpando codigo e add doc nas vars --- app/Controllers/ClientController.php | 1 - app/Controllers/MessageController.php | 3 ++- app/Controllers/QueueController.php | 30 +++++---------------- app/Controllers/SystemMessageController.php | 6 ++--- app/Core/Commands.php | 3 +-- 5 files changed, 12 insertions(+), 31 deletions(-) diff --git a/app/Controllers/ClientController.php b/app/Controllers/ClientController.php index d8d6f42..8f9c866 100644 --- a/app/Controllers/ClientController.php +++ b/app/Controllers/ClientController.php @@ -3,7 +3,6 @@ namespace app\Controllers; use app\Core\Controller; -use app\Controllers\BilheteController; use app\Models\Atendimento; /** diff --git a/app/Controllers/MessageController.php b/app/Controllers/MessageController.php index c1f093b..740ba7c 100644 --- a/app/Controllers/MessageController.php +++ b/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() { diff --git a/app/Controllers/QueueController.php b/app/Controllers/QueueController.php index 2f550e9..a3a0d89 100644 --- a/app/Controllers/QueueController.php +++ b/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() { @@ -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. diff --git a/app/Controllers/SystemMessageController.php b/app/Controllers/SystemMessageController.php index 10c4177..2d7ccb3 100644 --- a/app/Controllers/SystemMessageController.php +++ b/app/Controllers/SystemMessageController.php @@ -14,9 +14,8 @@ use Exception; */ class SystemMessageController extends Controller { - - private $sysMessage; - private $supervisorqueue; + /** @var SystemMessage $sysMessage model de mensagens do sistema */ + protected $sysMessage; public function __construct() { @@ -38,7 +37,6 @@ class SystemMessageController extends Controller } return $msgs; } catch (Exception $ex) { - $this->supervisorqueue->rollback(); logger()->error($ex->getMessage()); return false; } diff --git a/app/Core/Commands.php b/app/Core/Commands.php index 65fd22d..da3ee61 100644 --- a/app/Core/Commands.php +++ b/app/Core/Commands.php @@ -55,7 +55,6 @@ class Commands * * return string */ - public function setApi(IApiMedia $api) { $this->api = $api; @@ -164,4 +163,4 @@ class Commands return true; } } -} +} \ No newline at end of file From ad908764ebe93e997f3e2993c8543fcc86bcf918 Mon Sep 17 00:00:00 2001 From: lucas cardoso Date: Thu, 10 Feb 2022 16:27:00 -0400 Subject: [PATCH 098/144] =?UTF-8?q?doc=20nas=20vars,=20remo=C3=A7=C3=A3o?= =?UTF-8?q?=20de=20codigo=20n=C3=A3o=20utilizado=20e=20add=20transection?= =?UTF-8?q?=20no=20finalizar?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Core/Commands.php | 74 ++++++++++++++++++++----------------------- 1 file changed, 34 insertions(+), 40 deletions(-) diff --git a/app/Core/Commands.php b/app/Core/Commands.php index da3ee61..f810cc2 100644 --- a/app/Core/Commands.php +++ b/app/Core/Commands.php @@ -18,43 +18,30 @@ use websocket\WsInterface; */ class Commands { - /* - * Permissoes - * 0 - CLIENTE & AGENTE - * 1 - AGENTE - */ - const PERMISSION = "PERMISSION"; - const DESCRIPTION = "DESCRIPTION"; + /** @var IApiMedia $api provider de mensagens */ + protected $api; - private $bilheteController; - public $commandsConfig = array(); - private $api; - private $systemController; + /** @var SystemMessageController $systemController controller de mensagens de sistemas */ + protected $systemController; + + /** @var BilheteController $bilheteController controller de billhete */ + protected $bilheteController; + + /** @var Atendimento $atendimentoModel model de atendimento*/ + protected $atendimentoModel; + + /** @var Evento $eventosModel model de eventos */ + protected $eventosModel; public function __construct() { + $this->eventosModel = new Evento(); + $this->atendimentoModel = new Atendimento(); $this->agente = new AgentController(); $this->bilheteController = new BilheteController(); $this->systemController = new SystemMessageController(); - $this->commandsConfig = [ - "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." - ] - ]; } - /* - * Verificar se é um comando valido - * @param string $phone - * @param string $message - * - * return string - */ public function setApi(IApiMedia $api) { $this->api = $api; @@ -77,16 +64,17 @@ class Commands public function finalizar($numero) { try { - $atendimentoModel = new Atendimento(); - $eventosModel = new Evento(); + $this->atendimentoModel->begin(); $supervisorModel = new SupervisorModel(); - $atendimento = $atendimentoModel->getAtendimentoByCliente($numero, CONF_EVENT_START); + $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; } - $ret = $eventosModel->createEvento( + //cria o evento pra finalizar + $ret = $this->eventosModel->createEvento( $atendimento->uniqueid, CONF_EVENT_TIMERMINO_CLIENTE, date('Y-m-d H:i:s'), @@ -96,7 +84,6 @@ class Commands if ($ret) { $ws = new WsInterface(); $agente = $supervisorModel->findAgentByMatricula($atendimento->matricula); - $ws->enviaMsg($ws->enviaActions('Atendimento finalizado', 'finish', $agente->matricula, $atendimento->uniqueid)); $messegeModel = new Message(); $messegeModel->addMessage( $atendimento->uniqueid, @@ -108,31 +95,39 @@ class Commands $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 = $atendimentoModel->getAtendimentoAbertoByAgente($atendimento->matricula); + $atends = $this->atendimentoModel->getAtendimentoAbertoByAgente($atendimento->matricula); if (empty($atends)) { $agente->enterPause($atendimento->matricula, $agente->motivo_pausa); } } + //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 = $atendimentoModel->getQuantiAtendimentSimultaneos(); - $atendimentosAbertos = $atendimentoModel->getAtendimentoAbertoByAgente($agente->matricula); + $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); } } + //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->atendimentoModel->rollback(); $this->api->enviarMsg($numero, CONF_NAME_REPONSE . " : Erro ao finalizar!"); return true; } } catch (\Exception $th) { + $this->atendimentoModel->rollback(); $this->api->enviarMsg($numero, CONF_NAME_REPONSE . " : " . $th->getMessage()); return true; } @@ -140,10 +135,8 @@ class Commands private function cancelar($numero) { - $atendimentoModel = new Atendimento(); - $eventosModel = new Evento(); - $atendimento = $atendimentoModel->getAtendimentoByCliente($numero, CONF_EVENT_ESPERA); - $ret = $eventosModel->createEvento( + $atendimento = $this->atendimentoModel->getAtendimentoByCliente($numero, CONF_EVENT_ESPERA); + $ret = $this->eventosModel->createEvento( $atendimento->uniqueid, CONF_EVENT_ABANDONADA, date('Y-m-d H:i:s'), @@ -151,6 +144,7 @@ class Commands $atendimento->fila ); if ($ret) { + //envia as mensagens do sistema do momento atual para o cliente $this->systemController->sendMessageSystem( CONF_MOMENT_CANCELAR_FILA, [], From a7468f68b69f795a2e6240786682ff3fa6822ef3 Mon Sep 17 00:00:00 2001 From: lucas cardoso Date: Mon, 14 Feb 2022 08:32:16 -0400 Subject: [PATCH 099/144] =?UTF-8?q?ajustando=20indenta=C3=A7=C3=A3o?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Core/Controller.php | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/app/Core/Controller.php b/app/Core/Controller.php index 0cc7908..e1619f8 100644 --- a/app/Core/Controller.php +++ b/app/Core/Controller.php @@ -1,23 +1,24 @@ 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; } - } \ No newline at end of file + $this->message = $message; + } +} \ No newline at end of file From 71c6be0ddec0a8db99a1c28ea25c004b2c8f3dd4 Mon Sep 17 00:00:00 2001 From: lucas cardoso Date: Mon, 14 Feb 2022 08:35:16 -0400 Subject: [PATCH 100/144] add numero de atendimentos do dia no supervisor --- app/Models/SupervisorModel.php | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/app/Models/SupervisorModel.php b/app/Models/SupervisorModel.php index 28ea055..cf1fb7d 100644 --- a/app/Models/SupervisorModel.php +++ b/app/Models/SupervisorModel.php @@ -29,7 +29,16 @@ class SupervisorModel extends Model ORDER BY m2.id DESC LIMIT 1) AND ma.matricula = ms.matricula - ) AS countAtendimentos + ) 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 "; @@ -67,11 +76,10 @@ class SupervisorModel extends Model 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"; + WHERE ms.matricula = :matricula "; return $this->read($this->query, ['matricula' => $matricula])->fetch(); } From af970c53a48fac142bcdba6b856271a656b8129c Mon Sep 17 00:00:00 2001 From: lucas cardoso Date: Mon, 14 Feb 2022 08:45:04 -0400 Subject: [PATCH 101/144] add classe de requests --- app/Providers/Requests.php | 133 +++++++++++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 app/Providers/Requests.php diff --git a/app/Providers/Requests.php b/app/Providers/Requests.php new file mode 100644 index 0000000..239db85 --- /dev/null +++ b/app/Providers/Requests.php @@ -0,0 +1,133 @@ +request = new RequestURL(); + $this->setToken(); + $this->setUrl(); + } + + 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; + } + + function setUrl() + { + $this->url = CONF_WHATSAPP_AUTH_URL; + } +} \ No newline at end of file From 385b62b1fb8b3d6ac5dd313220d8fc53fd7ade21 Mon Sep 17 00:00:00 2001 From: lucas cardoso Date: Mon, 14 Feb 2022 08:47:10 -0400 Subject: [PATCH 102/144] =?UTF-8?q?removendo=20m=C3=A9todos=20n=C3=A3o=20u?= =?UTF-8?q?sados=20na=20IApiMedia?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Interfaces/IApiMedia.php | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/app/Interfaces/IApiMedia.php b/app/Interfaces/IApiMedia.php index d54b0ff..626b70c 100644 --- a/app/Interfaces/IApiMedia.php +++ b/app/Interfaces/IApiMedia.php @@ -10,13 +10,7 @@ interface IApiMedia 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(); - function setStorage($storage); function getProfile(); function getPhone(); function getType(); @@ -28,13 +22,6 @@ interface IApiMedia 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(); - public function getLinkDownload($host); - public function retornaTituloDocument(); - public function convertToWebsocket($content, $matricula = '', $idAtendimento, $type, $name, $number, $data, $idProvedor, $mimetype); + function retornaTituloDocument(); + function convertToWebsocket($content, $matricula = '', $idAtendimento, $type, $name, $number, $data, $idProvedor, $mimetype); } \ No newline at end of file From 146c3b6138d20b1ef94561e1d8400ccf315a310b Mon Sep 17 00:00:00 2001 From: lucas cardoso Date: Mon, 14 Feb 2022 08:47:39 -0400 Subject: [PATCH 103/144] doc var na classe supervisor --- app/Middleware/ApiSupervisor.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/Middleware/ApiSupervisor.php b/app/Middleware/ApiSupervisor.php index b166609..08f1dd1 100644 --- a/app/Middleware/ApiSupervisor.php +++ b/app/Middleware/ApiSupervisor.php @@ -8,7 +8,8 @@ use app\Models\SupervisorModel; class ApiSupervisor implements IApi { - public $supervisor; + /** @var SupervisorModel $supervisor model de supervisor */ + protected $supervisor; public function __construct() { @@ -27,7 +28,7 @@ class ApiSupervisor implements IApi } } function listaAgentesDisponivel() - {; + { try { $ret = $this->supervisor->listaAgentesDisponivel(); $agentes = []; @@ -60,4 +61,4 @@ class ApiSupervisor implements IApi } echo json_encode($data); } -} +} \ No newline at end of file From 4e92df9cd9586f388379923448168a70553a704c Mon Sep 17 00:00:00 2001 From: lucas cardoso Date: Mon, 14 Feb 2022 09:00:36 -0400 Subject: [PATCH 104/144] =?UTF-8?q?refatorando=20core=20m=C3=ADdia=20e=20p?= =?UTF-8?q?rovider=20positus?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Core/CoreMedia.php | 136 ++++++++++++++++++--------------- app/Core/Model.php | 1 - app/Providers/Positus.php | 156 +++++--------------------------------- 3 files changed, 93 insertions(+), 200 deletions(-) diff --git a/app/Core/CoreMedia.php b/app/Core/CoreMedia.php index 023bb75..c923464 100644 --- a/app/Core/CoreMedia.php +++ b/app/Core/CoreMedia.php @@ -13,7 +13,6 @@ 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; @@ -24,20 +23,47 @@ use websocket\WsInterface; */ class CoreMedia { - private $api; - private $request; - private $commands; - private $queue; - private $agente; - private $palavroes; - private $ws; - private $atendimento; - private $supervisor; - private $eventos; - private $host; - private $bilheteController; - private $message; - private $systemController; + /** @var IApiMedia $api api do provedor de mensagens */ + protected $api; + + /** @var array $request hook da requisição de webhook*/ + protected $request; + + /** @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() { @@ -54,7 +80,7 @@ class CoreMedia $this->bilheteController = new BilheteController; $this->systemController = new SystemMessageController(); } - public function setApi($api) + public function setApi(IApiMedia $api) { $this->api = $api; } @@ -83,32 +109,7 @@ class CoreMedia //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->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() - ); - $this->ws->enviaMsg( - $this->api->convertToWebsocket( - $this->retornaConteudo(), - $atendiment->matricula, - $atendiment->uniqueid, - $this->api->getType(), - $this->api->getProfile(), - $this->api->getPhone(), - time(), - $this->api->getId(), - $this->api->getMimetype() - ) - ); + $this->enviaMensagemAgente($atendiment); return null; } //verifica se tem atendimento em espera, se tiver ja mostra a sua posição na fila @@ -253,39 +254,52 @@ class CoreMedia } } - 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; - } - public function retornaConteudo() { switch ($this->api->getType()) { case 'text': return $this->api->getMessage(); case 'image': - return $this->api->getLinkDownload($this->host); case 'sticker': - return $this->api->getLinkDownload($this->host); case 'video': - return $this->api->getLinkDownload($this->host); case 'voice': case 'audio': - return $this->api->getLinkDownload($this->host); case 'document': - return $this->api->getLinkDownload($this->host); + return $this->api->getId(); case 'contacts': return null; case 'location': return null; } } -} + + function enviaMensagemAgente($atendiment) + { + $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() + ); + $this->ws->enviaMsg( + $this->api->convertToWebsocket( + $this->retornaConteudo(), + $atendiment->matricula, + $atendiment->uniqueid, + $this->api->getType(), + $this->api->getProfile(), + $this->api->getPhone(), + time(), + $this->api->getId(), + $this->api->getMimetype() + ) + ); + } +} \ No newline at end of file diff --git a/app/Core/Model.php b/app/Core/Model.php index 7a8d43b..cb3597c 100644 --- a/app/Core/Model.php +++ b/app/Core/Model.php @@ -107,7 +107,6 @@ 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(); diff --git a/app/Providers/Positus.php b/app/Providers/Positus.php index ac88aab..c3ca38c 100644 --- a/app/Providers/Positus.php +++ b/app/Providers/Positus.php @@ -3,33 +3,34 @@ namespace app\Providers; use app\Interfaces\IApiMedia; -use app\Providers\RequestURL; -class Positus implements IApiMedia +class Positus extends Requests implements IApiMedia { - private $token; - private $url; - private $metodo; - private $query; - private $requestType; - private $request; - private $params = array(); + /** @var string $hook resposta do webhook */ private $hook; private $storage = CONF_PATH_FILES; - public $channel = CONF_WHATSAPP_CHANNEL; - public $timeout_client_resposta = CONF_WHATSAPP_TIMEOUT_CLIENT_RESPOSTA; + private $channel = CONF_WHATSAPP_CHANNEL; - function __construct() + public function getchannel() + { + return CONF_WHATSAPP_CHANNEL; + } + + function setHook($hook) + { + $this->hook = $hook; + } + + function setToken() { $this->token = CONF_WHATSAPP_AUTH_TOKEN; - $this->url = CONF_WHATSAPP_AUTH_URL; - $this->request = new RequestURL(); } - public function getchannel() + function setUrl() { - return CONF_WHATSAPP_CHANNEL; + $this->url = CONF_WHATSAPP_AUTH_URL; } + public function convertToWebsocket($content, $matricula = '', $uniqueid, $type, $name, $number, $data, $idProvedor, $mimetype) { if ($number) { @@ -59,21 +60,7 @@ class Positus implements IApiMedia } return null; } - function enviaImagem($whatsapp, $link, $titulo = null) - { - } - function enviaSticker($whatsapp, $link) - { - } - function enviaVideo($whatsapp, $link, $titulo = null) - { - } - function enviaAudio($whatsapp, $link) - { - } - function enviaDocumento($whatsapp, $link, $titulo = null) - { - } + function enviarMedia($whatsapp, $link, $type, $titulo = null) { $tipos = []; @@ -263,10 +250,6 @@ class Positus implements IApiMedia //throw $th; } } - function setStorage($storage) - { - $this->storage = $storage; - } /** * Profile WhatsApp @@ -393,109 +376,6 @@ class Positus implements IApiMedia return false; } - function setHook($hook) - { - $this->hook = $hook; - } - - 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; - } - } - return true; - } - - /** - * 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"; - } - } - - 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); - } - // logger('core')->debug('header: ' . var_export($header, true), debug_backtrace()); - // logger('core')->debug('body: ' . var_export($this->params, true), debug_backtrace()); - $this->request->header($header); - $this->request->method_request($this->requestType); - return $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() { From d3e8bf7679c4fb415463d17f3db19ed75b75b2b4 Mon Sep 17 00:00:00 2001 From: lucas cardoso Date: Mon, 14 Feb 2022 09:49:55 -0400 Subject: [PATCH 105/144] add doc var e logger no desconecta --- websocket/Servidorsocket.php | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/websocket/Servidorsocket.php b/websocket/Servidorsocket.php index 41d8e53..a36491b 100644 --- a/websocket/Servidorsocket.php +++ b/websocket/Servidorsocket.php @@ -2,28 +2,32 @@ namespace websocket; -include __DIR__ . '/../Providers/Positus.php'; - use app\Middleware\ApiAgente; use app\Models\Atendimento; use Ratchet\ConnectionInterface; use Ratchet\MessageComponentInterface; +use SplObjectStorage; class Servidorsocket implements MessageComponentInterface { - public $clients; + /** @var ApiAgente $api api das função do agente */ + protected $api; + + /** @var SplObjectStorage $clients api das função do agente */ + protected $clients; + + /** @var Atendimento $atendimento api das função do agente */ + protected $atendimento; + public $connection = []; public $shell = []; public $conectado = []; public $idConexion = []; public $clientes = []; - public $api; - public $atendimento; public function __construct() { - - $this->clients = new \SplObjectStorage(); + $this->clients = new SplObjectStorage(); $this->api = new ApiAgente(); $this->atendimento = new Atendimento(); } @@ -52,7 +56,6 @@ class Servidorsocket implements MessageComponentInterface { try { $mensagem = json_decode($msg, true); - logger('ws')->debug(var_export($mensagem, true)); if ($mensagem['matricula']) { $this->clientes[$from->resourceId]['matricula'] = $mensagem['matricula']; } else { @@ -70,10 +73,13 @@ class Servidorsocket implements MessageComponentInterface public function onClose(ConnectionInterface $conn) { - // The connection is closed, remove it, as we can no longer send it messages - $this->conectado[$conn->resourceId] = false; - $this->clients->detach($conn); - $this->api->logoff($this->clientes[$conn->resourceId], false); + try { + $this->conectado[$conn->resourceId] = false; + $this->clients->detach($conn); + $this->api->logoff($this->clientes[$conn->resourceId], false); + } catch (\Exception $th) { + logger('ws:onClose')->debug($th->getMessage()); + } } public function onError(ConnectionInterface $conn, \Exception $e) From 6bc541a6aec910c70a7f5e1e5cf5d38834899ce8 Mon Sep 17 00:00:00 2001 From: lucas cardoso Date: Mon, 14 Feb 2022 17:25:23 -0400 Subject: [PATCH 106/144] add evento de atualiza status na api --- app/Middleware/ApiAgente.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/app/Middleware/ApiAgente.php b/app/Middleware/ApiAgente.php index aa3b4ba..9562e49 100644 --- a/app/Middleware/ApiAgente.php +++ b/app/Middleware/ApiAgente.php @@ -265,7 +265,7 @@ class ApiAgente implements IApi $atends = $this->atendimento->getAtendimentoAbertoByAgente($request['matricula']); if (empty($atends)) { $this->agentController->enterPause($request['matricula'], $agente->motivo_pausa); - $ws->enviaMsg($this->enviaActions('Agente em pausa', 'pausa', $agente->matricula, $request['uniqueid'])); + $ws->enviaMsg($this->enviaActions('Agente em pausa', 'att_status', $agente->matricula, $request['uniqueid'])); } } if ($agente->status == CONF_AGENT_STATUS_OCUPADO) { @@ -532,6 +532,7 @@ class ApiAgente implements IApi function entrarPausa($request) { + $ws = new WsInterface(); $agente = $this->supervisor->findAgentByMatricula($request['matricula']); //verifica se existe agente if (empty($agente)) { @@ -553,6 +554,7 @@ class ApiAgente implements IApi 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 @@ -561,6 +563,7 @@ class ApiAgente implements IApi 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 @@ -570,6 +573,7 @@ class ApiAgente implements IApi function saiPausa($request) { + $ws = new WsInterface(); $agente = $this->supervisor->findAgentByMatricula($request['matricula']); //verifica se existe agente if (empty($agente)) { @@ -580,6 +584,7 @@ class ApiAgente implements IApi if (is_string($ret)) { $this->retorno($ret); } else { + $ws->enviaMsg($this->enviaActions('Agente em pausa', 'att_status', $agente->matricula)); $this->retorno( "Agente 'livre'", $ret @@ -641,7 +646,7 @@ class ApiAgente implements IApi } } - function enviaActions($msg, $tipo, $destino, $uniqueid) + function enviaActions($msg, $tipo, $destino, $uniqueid = null) { try { $mensagem = []; From 05f916682e933e9ddac1265005ff16c86b2d59d0 Mon Sep 17 00:00:00 2001 From: lucas cardoso Date: Mon, 14 Feb 2022 17:25:43 -0400 Subject: [PATCH 107/144] nova variavel de moment --- app/Core/CoreMedia.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Core/CoreMedia.php b/app/Core/CoreMedia.php index c923464..daa12c5 100644 --- a/app/Core/CoreMedia.php +++ b/app/Core/CoreMedia.php @@ -223,7 +223,7 @@ class CoreMedia } $this->systemController->sendMessageSystem( CONF_MOMENT_INICIAR_ATENDIMENTO, - [], + [["nome" => "@agente_name", "valor" => $agent[0]->nome]], $this->api, $numero ); From 26ed4dc592a327899239d93807f686dab9704b5e Mon Sep 17 00:00:00 2001 From: lucas cardoso Date: Wed, 16 Feb 2022 15:12:25 -0400 Subject: [PATCH 108/144] add evento de re-start no ws --- app/Core/CoreMedia.php | 7 ++++++- app/Models/Evento.php | 6 ++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/app/Core/CoreMedia.php b/app/Core/CoreMedia.php index daa12c5..0ae4c50 100644 --- a/app/Core/CoreMedia.php +++ b/app/Core/CoreMedia.php @@ -212,6 +212,7 @@ class CoreMedia $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)) { $atendimentosAbertos = $this->atendimento->getAtendimentoAbertoByAgente($agent[0]->matricula); @@ -232,7 +233,11 @@ class CoreMedia "Número do protocolo do atendimento é $protocol\n" ); try { - $ws->enviaMsg($ws->enviaActions('Atendimento iniciado!', 'start', $agent[0]->matricula, $uniqueid)); + if ($event->evento == CONF_EVENT_ERRO_ATEND) { + $ws->enviaMsg($ws->enviaActions('Este atendimento foi realocado para sua responsabilidade.', 're_start', $agent[0]->matricula, $uniqueid)); + } 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); } diff --git a/app/Models/Evento.php b/app/Models/Evento.php index 1fe7c17..409b407 100644 --- a/app/Models/Evento.php +++ b/app/Models/Evento.php @@ -41,4 +41,10 @@ class Evento extends Model 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(); + } } \ No newline at end of file From e5e47b571602a830c91aba391c3ee747bc9ce83d Mon Sep 17 00:00:00 2001 From: lucas cardoso Date: Wed, 16 Feb 2022 15:13:02 -0400 Subject: [PATCH 109/144] =?UTF-8?q?ajustes=20de=20espa=C3=A7amento=20da=20?= =?UTF-8?q?query?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Models/Pause.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/Models/Pause.php b/app/Models/Pause.php index 85814ad..0125b52 100644 --- a/app/Models/Pause.php +++ b/app/Models/Pause.php @@ -18,7 +18,7 @@ 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(); } @@ -97,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 .= "AND motivo NOT IN('login','ausente','RECUSADA') LIMIT 10 "; + $this->query .= " AND motivo NOT IN('login','ausente','RECUSADA') LIMIT 10 "; return $this->read($this->query, $data)->fetchAll(); } From fbf4e25f6d0aa7aa309e775d745978835d921b4e Mon Sep 17 00:00:00 2001 From: lucas cardoso Date: Wed, 16 Feb 2022 15:14:20 -0400 Subject: [PATCH 110/144] validando se o agente foi desconectado --- app/Models/SupervisorModel.php | 16 ++++++++++++++-- service/ServiceSupervisorPBx.php | 7 +++++++ tests/test_services.php | 2 +- 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/app/Models/SupervisorModel.php b/app/Models/SupervisorModel.php index cf1fb7d..e18c5e2 100644 --- a/app/Models/SupervisorModel.php +++ b/app/Models/SupervisorModel.php @@ -103,6 +103,18 @@ class SupervisorModel extends Model return $this->read($this->query, $data)->fetchAll(); } + public function findAllAgentesPBX($queue = null) + { + + $data = []; + $this->query = "SELECT * FROM pbx_supervisor_agentes WHERE 1=1 "; + if ($queue) { + $this->query .= " AND fila = :queue "; + $data['queue'] = $queue; + } + return $this->read($this->query, $data)->fetchAll(); + } + public function findByAgent($matricula) { $this->query = "SELECT * FROM " . self::USUARIOS . " WHERE matricula = :matricula;"; @@ -322,10 +334,10 @@ class SupervisorModel extends Model return $this->update($this->query, $data); } - public function deleteAgent($matricula) + public function deleteAgentPbx($matricula) { $data = []; - $this->query = "DELETE FROM " . self::SUPERVISOR_AGENTE . " WHERE matricula = :matricula"; + $this->query = "DELETE FROM pbx_supervisor_agentes WHERE matricula = :matricula"; $data['matricula'] = $matricula; return $this->delete($this->query, $data); } diff --git a/service/ServiceSupervisorPBx.php b/service/ServiceSupervisorPBx.php index a61195f..5fcb496 100644 --- a/service/ServiceSupervisorPBx.php +++ b/service/ServiceSupervisorPBx.php @@ -23,6 +23,13 @@ class ServiceSupervisorPBx implements IService $this->atualizaTabelaSupervisor($agente, $agentPbx); } } + $agentesPbx = $this->supervisorModel->findAllAgentesPBX(); + foreach ($agentesPbx as $key => $pbx) { + $age = $this->supervisorModel->findAgentByMatricula($pbx->matricula); + if (empty($age)) { + $this->supervisorModel->deleteAgentPbx($pbx->matricula); + } + } } function atualizaTabelaSupervisor($agente, $agentePbx) diff --git a/tests/test_services.php b/tests/test_services.php index 3f2df8f..281f20e 100644 --- a/tests/test_services.php +++ b/tests/test_services.php @@ -17,5 +17,5 @@ while (true) { $sevice_timeout->run(); echo "executar servce_queue \n"; $servce_queue->run(); - sleep(10); + sleep(3); } \ No newline at end of file From 4b84a425d36f16984653d33d6e0c3b435ba978b6 Mon Sep 17 00:00:00 2001 From: lucas cardoso Date: Thu, 17 Feb 2022 09:52:46 -0400 Subject: [PATCH 111/144] add metodo de deletar agente --- app/Models/SupervisorModel.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/Models/SupervisorModel.php b/app/Models/SupervisorModel.php index e18c5e2..fb4a216 100644 --- a/app/Models/SupervisorModel.php +++ b/app/Models/SupervisorModel.php @@ -342,6 +342,14 @@ class SupervisorModel extends Model 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 = []; From 644aa72e329e38b0940aaaaa4c11a31e18588c32 Mon Sep 17 00:00:00 2001 From: lucas cardoso Date: Thu, 17 Feb 2022 09:53:10 -0400 Subject: [PATCH 112/144] =?UTF-8?q?configure=20timeout=20de=20sess=C3=A3o?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- service/ServiceTimeout.php | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/service/ServiceTimeout.php b/service/ServiceTimeout.php index b1890fd..ce74e51 100644 --- a/service/ServiceTimeout.php +++ b/service/ServiceTimeout.php @@ -8,6 +8,7 @@ use app\Models\Atendimento; use app\Models\Message; use app\Models\NotificaMedia; use app\Providers\Positus; +use DateInterval; class ServiceTimeout implements IService { @@ -57,6 +58,7 @@ class ServiceTimeout implements IService $atendiss = $this->atendimentos->findAtenEmAberto(); foreach ($atendiss as $value) { $this->timeoutCliente($value->uniqueid, $value->cliente_id); + $this->timeoutSessao($value->uniqueid, $value->cliente_id, $value->data_reg); } } private function timeoutCliente($uniqueid, $client) @@ -87,4 +89,26 @@ class ServiceTimeout implements IService } } } + + private function timeoutSessao($uniqueid, $client, $data_atendimento) + { + $date1 = date_create($data_atendimento); + $date2 = date_create(); + echo $date1->format('Y-m-d H:i:s') . "\n"; + echo $date2->format('Y-m-d H:i:s') . "\n"; + $diff = date_diff($date1, $date2); + echo $diff->h . "\n"; + if ($diff->h > 23) { + $this->notificaMedia->addNotifica( + $uniqueid, + $client, + utf8_encode($this->mensagem['TIMEOUT_CLIENT_INATIVIDADE']) . $uniqueid + ); + $this->positus->enviarMsg( + $client, + $this->mensagem['TIMEOUT_CLIENT_INATIVIDADE'] + ); + $this->command->finalizar($client); + } + } } \ No newline at end of file From 5b1cc7f4fa504ee10a5957e89c4fbd0e03c6f825 Mon Sep 17 00:00:00 2001 From: lucas cardoso Date: Thu, 17 Feb 2022 09:53:46 -0400 Subject: [PATCH 113/144] nova mensagem de aviso para o novo atendente --- app/Core/CoreMedia.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/Core/CoreMedia.php b/app/Core/CoreMedia.php index 0ae4c50..f0a70e4 100644 --- a/app/Core/CoreMedia.php +++ b/app/Core/CoreMedia.php @@ -235,6 +235,16 @@ class CoreMedia 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)); } From 435a9bfa24ba29cc39237d6c0a582cb88113e7cc Mon Sep 17 00:00:00 2001 From: Simples IP Desenvolvimento Date: Thu, 17 Feb 2022 10:21:34 -0400 Subject: [PATCH 114/144] correcoes e novas funcionalidades --- public/css/styles.css | 7 +-- public/js/config.js | 7 +-- public/js/main.js | 14 +++--- public/js/requests.js | 52 ++++++++++++---------- public/js/util.js | 101 +++++++++++++++++++++++++----------------- 5 files changed, 104 insertions(+), 77 deletions(-) diff --git a/public/css/styles.css b/public/css/styles.css index 240c4ab..41c5179 100644 --- a/public/css/styles.css +++ b/public/css/styles.css @@ -79,9 +79,10 @@ a { } .bottom { - background: var(--background-beige); + /** background-image: linear-gradient(to right top, #af8866, #b7766e, #ae6984, #8e679b, #506ba8); */ + background-image: linear-gradient(to right bottom, #676767, #626262, #5d5d5d, #585858, #535353); grid-column: 1 / -1; - grid-row: 2 / -1; + grid-row: 1 / 5; } /****** @@ -97,7 +98,7 @@ a { max-width: 1250px; margin: 2rem auto; border: 1px solid rgba(0, 0, 0, 0.1); - box-shadow: 0 0 1rem 0.05rem rgba(0, 0, 0, 0.2); + box-shadow: 0 0 2rem 1rem rgba(0, 0, 0, 0.3); border-top-left-radius: 20px; border-bottom-left-radius: 20px; } diff --git a/public/js/config.js b/public/js/config.js index b85f106..57823fd 100644 --- a/public/js/config.js +++ b/public/js/config.js @@ -1,8 +1,5 @@ -const server = localStorage.getItem('obj_server') -const ws = `ws://${server}/wss` - -const server_api = server -const URLApi = `http://${server}` +const ws = localStorage.getItem('obj_ws') +const server_api = localStorage.getItem('obj_server') let mediaRecorder const icontypes = ['csv', 'doc', 'pdf', 'txt', 'xls', 'zip', 'ppt'] \ No newline at end of file diff --git a/public/js/main.js b/public/js/main.js index d1660b1..f261e17 100644 --- a/public/js/main.js +++ b/public/js/main.js @@ -144,10 +144,10 @@ const selectNotification = (id) => { ) } - if(e.event?.mensagem.type == 'finish'){ + if(e.event?.mensagem.type == 'finish' || e.event?.mensagem.type == 're_start'){ $('.chat-window').append(`
- Atendimento finalizado + ${e.event.mensagem.content}
` ) } @@ -295,12 +295,15 @@ const viewMessage = (ev) => { ${datesend}
`) break - case 'finish': + case 're_start': $('.chat-window').append(`
- Atendimento finalizado + ${ev.event.mensagem.content}
`) break + case 're_start': + console.log('APENAS UM TESTE RÁPIDO DE TESTE') + break } } @@ -368,10 +371,11 @@ const receiveNotification = (data) => { switch(data.event.mensagem.type){ case 'start': case 'transfer': - case 'pause': + case 'att_status': obj = {} break case 'finish': + case 're_start': obj = { uniqueid: data.event?.mensagem.uniqueid, action: data.event.mensagem.type, diff --git a/public/js/requests.js b/public/js/requests.js index 6928172..f518e6e 100644 --- a/public/js/requests.js +++ b/public/js/requests.js @@ -1,6 +1,6 @@ const enviarMensagem = (dataSend) => { $.ajax({ - url: `http://${server_api}/integracao/media/api/agente/enviarMensagem`, + url: `${server_api}/integracao/media/api/agente/enviarMensagem`, type: "POST", data: JSON.stringify(dataSend), success: function (res) { @@ -14,7 +14,7 @@ const enviarMensagem = (dataSend) => { const listaMensagem = (uniqueid) => new Promise((resolve) => { $.ajax({ - url: `http://${server_api}/integracao/media/api/agente/listarMensagem`, + url: `${server_api}/integracao/media/api/agente/listarMensagem`, type: "POST", data: JSON.stringify({ uniqueid @@ -32,11 +32,9 @@ const listaMensagem = (uniqueid) => new Promise((resolve) => { const listarAgentesDisponivel = () => new Promise((resolve) => { $.ajax({ - url: `http://${server_api}/integracao/media/api/agente/listarAgentesDisponivel`, + url: `${server_api}/integracao/media/api/agente/listarAgentesDisponivel`, type: "GET", success: function (res) { - localStorage.removeItem('obj_agentes_disponivel') - localStorage.setItem('obj_agentes_disponivel', JSON.stringify(res)) resolve(res) }, error: function (res) { @@ -47,7 +45,7 @@ const listarAgentesDisponivel = () => new Promise((resolve) => { const listarAtendimentoAgente = (matricula) => new Promise((resolve) => { $.ajax({ - url: `http://${server_api}/integracao/media/api/agente/listarAtendimentoAgente`, + url: `${server_api}/integracao/media/api/agente/listarAtendimentoAgente`, type: "POST", data: JSON.stringify({ matricula @@ -58,21 +56,22 @@ const listarAtendimentoAgente = (matricula) => new Promise((resolve) => { resolve(res) }, error: function (res) { - alert('Nao foi possivel carregar as listas de atendimento.') + alertModal( + `

CARREGANDO MENSAGENS  

`, + 'Estamos restabelecendo a conexão' + ) } }); }) const listarPausasAgente = (matricula) => new Promise((resolve) => { $.ajax({ - url: `http://${server_api}/integracao/media/api/agente/listarPausasAgente`, + url: `${server_api}/integracao/media/api/agente/listarPausasAgente`, type: "POST", data: JSON.stringify({ matricula }), success: function (res) { - localStorage.removeItem('obj_pauses') - localStorage.setItem('obj_pauses', JSON.stringify(res)) resolve(res) }, error: function (res) { @@ -83,7 +82,7 @@ const listarPausasAgente = (matricula) => new Promise((resolve) => { const entrarPausa = (id_pausa, matricula) => new Promise((resolve) => { $.ajax({ - url: `http://${server_api}/integracao/media/api/agente/entrarPausa`, + url: `${server_api}/integracao/media/api/agente/entrarPausa`, type: "POST", data: JSON.stringify({ id_pausa, @@ -98,14 +97,14 @@ const entrarPausa = (id_pausa, matricula) => new Promise((resolve) => { } }, error: function (res) { - alert('Erro ao carregar as listas de pausa.') + alert('Não foi possível atribuir a pausa no momento!') } }); }) const sairPausa = (matricula) => new Promise((resolve) => { $.ajax({ - url: `http://${server_api}/integracao/media/api/agente/sairPausa`, + url: `${server_api}/integracao/media/api/agente/sairPausa`, type: "POST", data: JSON.stringify({ matricula @@ -122,7 +121,7 @@ const sairPausa = (matricula) => new Promise((resolve) => { const entrar = (matricula, queue) => new Promise((resolve) => { $.ajax({ - url: `http://${server_api}/integracao/media/api/agente/entrar`, + url: `${server_api}/integracao/media/api/agente/entrar`, type: "POST", data: JSON.stringify({ id_fila: queue, @@ -140,7 +139,7 @@ const entrar = (matricula, queue) => new Promise((resolve) => { const sair = (matricula) => { $.ajax({ - url: `http://${server_api}/integracao/media/api/agente/sair`, + url: `${server_api}/integracao/media/api/agente/sair`, type: "POST", data: JSON.stringify({ matricula @@ -161,7 +160,7 @@ const sair = (matricula) => { const finalizarAtendimento = (matricula, uniqueid) => new Promise((resolve) => { $.ajax({ - url: `http://${server_api}/integracao/media/api/agente/finalizarAtendimento`, + url: `${server_api}/integracao/media/api/agente/finalizarAtendimento`, type: "POST", data: JSON.stringify({ matricula, @@ -186,7 +185,7 @@ const finalizarAtendimento = (matricula, uniqueid) => new Promise((resolve) => { const statusAgente = (matricula) => new Promise((resolve) => { $.ajax({ - url: `http://${server_api}/integracao/media/api/agente/statusAgente`, + url: `${server_api}/integracao/media/api/agente/statusAgente`, type: "POST", data: JSON.stringify({ matricula @@ -197,23 +196,28 @@ const statusAgente = (matricula) => new Promise((resolve) => { resolve(res) }, error: function (res) { - reconnectWS() + alertModal( + `

RECONECTANDO, AGUARDE  

`, + 'Estamos restabelecendo a conexão com nosso sistema!' + ) console.log('Recarregando ...') - //alert('Não foi possível carregar as infoemações do agente.') } }); }) -const atualizaAgente = () => { +const atualizaAgente = () => new Promise((resolve) => { $.ajax({ - url: `http://${server_api}/index.php?idProg=14&idSubProg=3&ajax=1&acao=atualiza`, + url: `${server_api}/index.php?idProg=14&idSubProg=3&ajax=1&acao=atualiza`, type: "GET", + success: function(res){ + resolve(res) + } }); -} +}) const transferirAtendimento = (origem, destino, uniqueid) => new Promise((resolve) => { $.ajax({ - url: `http://${server_api}/integracao/media/api/agente/transferirAtendimento`, + url: `${server_api}/integracao/media/api/agente/transferirAtendimento`, type: "POST", data: JSON.stringify({ matricula_origem: origem, @@ -237,7 +241,7 @@ const transferirAtendimento = (origem, destino, uniqueid) => new Promise((resolv const marcarMensagemVista = (uniqueid) => { $.ajax({ - url: `http://${server_api}/integracao/media/api/agente/marcarMensagemVista`, + url: `${server_api}/integracao/media/api/agente/marcarMensagemVista`, type: "POST", data: JSON.stringify({ uniqueid diff --git a/public/js/util.js b/public/js/util.js index f8b1795..ed18135 100644 --- a/public/js/util.js +++ b/public/js/util.js @@ -53,15 +53,16 @@ const hideButtons = (type) => { } } -const reconnectWS = () => { +const alertModal = (title, message) => { modalStart() $(this).css({'align-items': 'center'}) $('.modal-content-body').append(() => - `

RECONECTANDO, AGUARDE  

` + title ); - $('.modal-header-title').append(`Por favor, aguarde alguns instantes!`) + $('.modal-header-title').append(`${message}`) $('#modalselect').show() } + /** * HABILITA O ENVIO DE ARQUIVO DE IMAGENS E APRESENTA UMA MODAL PARA APRESENTAÇÃO DA IMAGEM SELECIONADA */ @@ -117,29 +118,30 @@ const startSendFile = () => { const startPause = () => { $("#entrePause").on('click', function(){ - modalStart() - $("#modalselect").show() - listarPausasAgente(localStorage.getItem('my_uniqueid')) - const pausas = JSON.parse(localStorage.getItem('obj_pauses')) - - $('.modal-content-body').append(() => { - let selectPause = '' - pausas.data.forEach(e => { - if(e.matricula != localStorage.getItem('my_uniqueid')){ - selectPause += `` - } - }) - if(selectPause.length > 0){ - selectPause = `` - } else { - selectPause = '

Nenhuma pausa foi encontrado!

' - } - return selectPause - }); - $('.modal-header-title').append(`Selecione uma Pausa:`) - $('#footer-content-right').append(``) - $('#modalselect').show() + listarPausasAgente(localStorage.getItem('my_uniqueid')).then((pausas) => { + modalStart() + $("#modalselect").show() + + $('.modal-content-body').append(() => { + let selectPause = '' + pausas.data.forEach(e => { + if(e.matricula != localStorage.getItem('my_uniqueid')){ + selectPause += `` + } + }) + if(selectPause.length > 0){ + selectPause = `` + } else { + selectPause = '

Nenhuma pausa foi encontrado!

' + } + return selectPause + }); + + $('.modal-header-title').append(`Selecione uma Pausa:`) + $('#footer-content-right').append(``) + $('#modalselect').show() + }) }) $("#exitPause").on('click', () => { @@ -159,18 +161,24 @@ const startPause = () => { const startTransfer = () => { $("#tranferagent").on('click', function(){ modalStart() - listarAgentesDisponivel() - const agentes = JSON.parse(localStorage.getItem('obj_agentes_disponivel')) - $('.modal-content-body').append(() => { - let selectPause = `${optAgent}` } - }) - selectPause += `` - return selectPause - }); + $('#transfersend').hide() + return `

Nenhum agente disponível no momento!

` + }); + }) + + $('.modal-header-title').append(`Selecione um agente para transferir:`) $('#footer-content-right').append(``) @@ -261,7 +269,7 @@ function recorderVoice () { * @returns */ const messageTypeMedia = (obj) => { - const fileDownload = URLApi + "/integracao/media/link/" + obj.id_provedor + "/" + window.btoa(obj.mimetype) + const fileDownload = server_api + "/integracao/media/link/" + obj.id_provedor + "/" + window.btoa(obj.mimetype) if(obj.type == 'voice' || obj.type == 'audio'){ $('.chat-window').append(`
@@ -479,12 +487,23 @@ const monitorPausaAgente = () => { } const supervisorAgente = () => { - atualizaAgente() - /** MONITORA AS CONFIGURACOES */ setTimeout(() => { + atualizaAgente().then((res) => { + if(res.indexOf('close') >= 0){ + window.close() + } + }) + + statusAgente(localStorage.getItem('my_uniqueid')).then((agente) => { + if(agente.status == 'error' && agente.message == 'Agente não encontrado'){ + window.close() + } + }) + supervisorAgente() - }, 90000); + + }, 40000); } /** CONNECT TO WS */ @@ -524,6 +543,8 @@ const connect = (wsserver) => { /** att: atualizacao do websocket */ if(e.data != 'att'){ const data = JSON.parse(e?.data) + + console.log(data) if(localStorage.getItem('session_uniqueid') == null){ localStorage.setItem('session_uniqueid', data.event.mensagem.uniqueid) From 83aa438ff52d25493fcee38e76084d83ce5cc610 Mon Sep 17 00:00:00 2001 From: lucas cardoso Date: Mon, 21 Feb 2022 16:49:11 -0400 Subject: [PATCH 115/144] =?UTF-8?q?add=20script=20de=20servi=C3=A7os?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- inicia.sh | 3 --- wpp | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 3 deletions(-) delete mode 100644 inicia.sh create mode 100644 wpp diff --git a/inicia.sh b/inicia.sh deleted file mode 100644 index 4b54052..0000000 --- a/inicia.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash -exec apache2-foreground & -php websocket/websocket.php \ No newline at end of file diff --git a/wpp b/wpp new file mode 100644 index 0000000..4ae91bc --- /dev/null +++ b/wpp @@ -0,0 +1,58 @@ +#!/bin/bash +# +# chkconfig: 35 90 12 +# description: Foo server +# + +# Get function from functions library +. /etc/init.d/functions + +# Start the service FOO +script="./websocket.php" +script2="./test_services.php" +name="wpp" +start() { + if [ "$(ps aux | grep $script | grep -v grep)" != "" ] && [ "$(ps aux | grep $script2 | grep -v grep)" != "" ]; then + echo "$name em execucao." + exit 1 + else + php /var/www/html/aplicativo/integracao/media/websocket/$script & + php /var/www/html/aplicativo/integracao/media/tests/$script2 & + echo "iniciado $name" + fi +} +stop() { + kill $(ps aux | grep $script | grep -v "grep" | awk '{print $2}') + kill $(ps aux | grep $script2 | grep -v "grep" | awk '{print $2}') + echo "Desligando $name" +} +status() { + if [ "$(ps aux | grep $script | grep -v "grep")" != "" ] && [ "$(ps aux | grep $script2 | grep -v "grep")" != "" ]; then + echo "$name Iniciado." + else + echo "$name Parado." + fi +} + +case "$1" in +start) + start + ;; +stop) + stop + ;; +restart) + stop + sleep 2 + start + ;; +status) + status + ;; +*) + echo $"usa assim vagabundo: $name {start|stop|restart|reload|status}" + exit 1 + ;; +esac + +exit 0 From 843a312bbf74c6eedb401208014b6afb11d765f6 Mon Sep 17 00:00:00 2001 From: lucas cardoso Date: Mon, 21 Feb 2022 17:21:03 -0400 Subject: [PATCH 116/144] add classe da twilio --- .htaccess | 6 - apache2.conf | 232 ------------------------------------ app/Providers/ApiTwilio.php | 83 +++++++++++++ composer.json | 5 +- 4 files changed, 86 insertions(+), 240 deletions(-) delete mode 100644 .htaccess delete mode 100644 apache2.conf create mode 100644 app/Providers/ApiTwilio.php diff --git a/.htaccess b/.htaccess deleted file mode 100644 index 3b8eaed..0000000 --- a/.htaccess +++ /dev/null @@ -1,6 +0,0 @@ -RewriteEngine on -RewriteCond %{REQUEST_FILENAME} !-f -RewriteCond %{REQUEST_FILENAME} !-d -RewriteRule ^(.*)$ index.php/$1 [L] -AddType application/json json php -php_value default_mimetype application/json \ No newline at end of file diff --git a/apache2.conf b/apache2.conf deleted file mode 100644 index 2ba5c3a..0000000 --- a/apache2.conf +++ /dev/null @@ -1,232 +0,0 @@ -# This is the main Apache server configuration file. It contains the -# configuration directives that give the server its instructions. -# See http://httpd.apache.org/docs/2.4/ for detailed information about -# the directives and /usr/share/doc/apache2/README.Debian about Debian specific -# hints. -# -# -# Summary of how the Apache 2 configuration works in Debian: -# The Apache 2 web server configuration in Debian is quite different to -# upstream's suggested way to configure the web server. This is because Debian's -# default Apache2 installation attempts to make adding and removing modules, -# virtual hosts, and extra configuration directives as flexible as possible, in -# order to make automating the changes and administering the server as easy as -# possible. - -# It is split into several files forming the configuration hierarchy outlined -# below, all located in the /etc/apache2/ directory: -# -# /etc/apache2/ -# |-- apache2.conf -# | `-- ports.conf -# |-- mods-enabled -# | |-- *.load -# | `-- *.conf -# |-- conf-enabled -# | `-- *.conf -# `-- sites-enabled -# `-- *.conf -# -# -# * apache2.conf is the main configuration file (this file). It puts the pieces -# together by including all remaining configuration files when starting up the -# web server. -# -# * ports.conf is always included from the main configuration file. It is -# supposed to determine listening ports for incoming connections which can be -# customized anytime. -# -# * Configuration files in the mods-enabled/, conf-enabled/ and sites-enabled/ -# directories contain particular configuration snippets which manage modules, -# global configuration fragments, or virtual host configurations, -# respectively. -# -# They are activated by symlinking available configuration files from their -# respective *-available/ counterparts. These should be managed by using our -# helpers a2enmod/a2dismod, a2ensite/a2dissite and a2enconf/a2disconf. See -# their respective man pages for detailed information. -# -# * The binary is called apache2. Due to the use of environment variables, in -# the default configuration, apache2 needs to be started/stopped with -# /etc/init.d/apache2 or apache2ctl. Calling /usr/bin/apache2 directly will not -# work with the default configuration. - - -# Global configuration -# - -# -# ServerRoot: The top of the directory tree under which the server's -# configuration, error, and log files are kept. -# -# NOTE! If you intend to place this on an NFS (or otherwise network) -# mounted filesystem then please read the Mutex documentation (available -# at ); -# you will save yourself a lot of trouble. -# -# Do NOT add a slash at the end of the directory path. -# -#ServerRoot "/etc/apache2" - -# -# The accept serialization lock file MUST BE STORED ON A LOCAL DISK. -# -#Mutex file:${APACHE_LOCK_DIR} default - -# -# The directory where shm and other runtime files will be stored. -# - -DefaultRuntimeDir ${APACHE_RUN_DIR} - -# -# PidFile: The file in which the server should record its process -# identification number when it starts. -# This needs to be set in /etc/apache2/envvars -# -PidFile ${APACHE_PID_FILE} - -# -# Timeout: The number of seconds before receives and sends time out. -# -Timeout 300 - -# -# KeepAlive: Whether or not to allow persistent connections (more than -# one request per connection). Set to "Off" to deactivate. -# -KeepAlive On - -# -# MaxKeepAliveRequests: The maximum number of requests to allow -# during a persistent connection. Set to 0 to allow an unlimited amount. -# We recommend you leave this number high, for maximum performance. -# -MaxKeepAliveRequests 100 - -# -# KeepAliveTimeout: Number of seconds to wait for the next request from the -# same client on the same connection. -# -KeepAliveTimeout 5 - - -# These need to be set in /etc/apache2/envvars -User ${APACHE_RUN_USER} -Group ${APACHE_RUN_GROUP} - -# -# HostnameLookups: Log the names of clients or just their IP addresses -# e.g., www.apache.org (on) or 204.62.129.132 (off). -# The default is off because it'd be overall better for the net if people -# had to knowingly turn this feature on, since enabling it means that -# each client request will result in AT LEAST one lookup request to the -# nameserver. -# -HostnameLookups Off - -# ErrorLog: The location of the error log file. -# If you do not specify an ErrorLog directive within a -# container, error messages relating to that virtual host will be -# logged here. If you *do* define an error logfile for a -# container, that host's errors will be logged there and not here. -# -ErrorLog ${APACHE_LOG_DIR}/error.log - -# -# LogLevel: Control the severity of messages logged to the error_log. -# Available values: trace8, ..., trace1, debug, info, notice, warn, -# error, crit, alert, emerg. -# It is also possible to configure the log level for particular modules, e.g. -# "LogLevel info ssl:warn" -# -LogLevel warn - -# Include module configuration: -IncludeOptional mods-enabled/*.load -IncludeOptional mods-enabled/*.conf - -# Include list of ports to listen on -Include ports.conf - - -# Sets the default security model of the Apache2 HTTPD server. It does -# not allow access to the root filesystem outside of /usr/share and /var/www. -# The former is used by web applications packaged in Debian, -# the latter may be used for local directories served by the web server. If -# your system is serving content from a sub-directory in /srv you must allow -# access here, or in any related virtual host. - - Options FollowSymLinks - Header set Access-Control-Allow-Origin "*" - AllowOverride None - Require all denied - - - - Header set Access-Control-Allow-Origin "*" - AllowOverride None - Require all granted - - - - Header set Access-Control-Allow-Origin "*" - Options Indexes FollowSymLinks - AllowOverride None - Require all granted - - -# -# Options Indexes FollowSymLinks -# AllowOverride None -# Require all granted -# - - - - -# AccessFileName: The name of the file to look for in each directory -# for additional configuration directives. See also the AllowOverride -# directive. -# -AccessFileName .htaccess - -# -# The following lines prevent .htaccess and .htpasswd files from being -# viewed by Web clients. -# - - Header set Access-Control-Allow-Origin "*" - Require all denied - - - -# -# The following directives define some format nicknames for use with -# a CustomLog directive. -# -# These deviate from the Common Log Format definitions in that they use %O -# (the actual bytes sent including headers) instead of %b (the size of the -# requested file), because the latter makes it impossible to detect partial -# requests. -# -# Note that the use of %{X-Forwarded-For}i instead of %h is not recommended. -# Use mod_remoteip instead. -# -LogFormat "%v:%p %h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" vhost_combined -LogFormat "%h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" combined -LogFormat "%h %l %u %t \"%r\" %>s %O" common -LogFormat "%{Referer}i -> %U" referer -LogFormat "%{User-agent}i" agent - -# Include of directories ignores editors' and dpkg's backup files, -# see README.Debian for details. - -# Include generic snippets of statements -IncludeOptional conf-enabled/*.conf - -# Include the virtual host configurations: -IncludeOptional sites-enabled/*.conf - -# vim: syntax=apache ts=4 sw=4 sts=4 sr noet -ServerName 192.168.115.65 diff --git a/app/Providers/ApiTwilio.php b/app/Providers/ApiTwilio.php new file mode 100644 index 0000000..7790fcb --- /dev/null +++ b/app/Providers/ApiTwilio.php @@ -0,0 +1,83 @@ +sid, $this->token); + $message = $twilio->messages->create( + "whatsapp:+5565996107663", // to + [ + "from" => "whatsapp:+14155238886", + "body" => "Hello there!" + ] + ); + + logger('twilio')->info(var_export($message)); + } + 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() + { + } + function getProfile() + { + } + function getPhone() + { + } + function getType() + { + } + function getMimetype() + { + } + function getId() + { + } + function getIsValidMessage() + { + } + function getMessage() + { + } + function getContactFormatted() + { + } + function getContactPhone() + { + } + function getGeolocation($type) + { + } + function setHook($hook) + { + } + function retornaTituloDocument() + { + } + function convertToWebsocket($content, $matricula = '', $idAtendimento, $type, $name, $number, $data, $idProvedor, $mimetype) + { + } +} \ No newline at end of file diff --git a/composer.json b/composer.json index 2be7919..dc97c61 100644 --- a/composer.json +++ b/composer.json @@ -16,6 +16,7 @@ }, "require": { "cboden/ratchet": "^0.4.4", - "textalk/websocket": "^1.3.1" + "textalk/websocket": "^1.3.1", + "twilio/sdk": "^6.34" } -} \ No newline at end of file +} From eb5ab82ee45e90e392896cec3d6073371f4d2f2d Mon Sep 17 00:00:00 2001 From: lucas cardoso Date: Wed, 23 Feb 2022 15:12:13 -0400 Subject: [PATCH 117/144] =?UTF-8?q?atualiza=C3=A7=C3=A3o=20do=20status=20q?= =?UTF-8?q?uando=20ocorrer=20transferencia?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Controllers/AgentController.php | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/app/Controllers/AgentController.php b/app/Controllers/AgentController.php index 31930d4..5da7e9a 100644 --- a/app/Controllers/AgentController.php +++ b/app/Controllers/AgentController.php @@ -291,6 +291,8 @@ class AgentController extends Controller '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; @@ -303,6 +305,25 @@ class AgentController extends Controller return false; } + public function atualizaStatusAgente($agente) + { + + $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); + } + } + if ($agente->status == CONF_AGENT_STATUS_INDISPONIVEL) { + if (empty($atendimentosAbertos)) { + $this->enterPause($agente->matricula, $agente->motivo_pausa); + } + } + } + public function infoAgentes($media, $queue = null) { try { From 132df6f13ffc806957e56c148863c346dc9e07f1 Mon Sep 17 00:00:00 2001 From: Simples IP Desenvolvimento Date: Thu, 24 Feb 2022 15:52:26 -0400 Subject: [PATCH 118/144] =?UTF-8?q?corre=C3=A7=C3=A3o=20dos=20headers=20ap?= =?UTF-8?q?i?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Middleware/Middleware.php | 1 + index.php | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Middleware/Middleware.php b/app/Middleware/Middleware.php index 4c8297b..95ecdf8 100644 --- a/app/Middleware/Middleware.php +++ b/app/Middleware/Middleware.php @@ -29,6 +29,7 @@ class Middleware extends Http { $this->link = $config['LINK_UPLOAD']; $this->header = new WebHeader($config); + $this->api(); $this->hook(); } diff --git a/index.php b/index.php index c9d18ef..7bd4a37 100644 --- a/index.php +++ b/index.php @@ -5,5 +5,4 @@ include __DIR__ . '/includes/config.php'; use app\Middleware\Middleware; -$middle = new Middleware($config); -$middle->api(); \ No newline at end of file +$middle = new Middleware($config); \ No newline at end of file From ef1fda97dcf4cad026a7b6ffa74f1f8f356c3c71 Mon Sep 17 00:00:00 2001 From: lucas cardoso Date: Fri, 25 Feb 2022 10:23:23 -0400 Subject: [PATCH 119/144] configurando quebra de linha nas mesagens de momento --- app/Controllers/SystemMessageController.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/Controllers/SystemMessageController.php b/app/Controllers/SystemMessageController.php index 2d7ccb3..065c269 100644 --- a/app/Controllers/SystemMessageController.php +++ b/app/Controllers/SystemMessageController.php @@ -25,7 +25,9 @@ class SystemMessageController extends Controller { //$variavels = [["nome" => '@cliente', "valor" => 'afonso']] try { $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']; From ce34963d68fc7682982f057084f4b5cf5431d994 Mon Sep 17 00:00:00 2001 From: lucas cardoso Date: Fri, 25 Feb 2022 10:24:36 -0400 Subject: [PATCH 120/144] ajustes genericos --- app/Middleware/ApiAgente.php | 2 +- app/Providers/Positus.php | 4 ++-- config/moments.php | 6 ++---- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/app/Middleware/ApiAgente.php b/app/Middleware/ApiAgente.php index 9562e49..3974619 100644 --- a/app/Middleware/ApiAgente.php +++ b/app/Middleware/ApiAgente.php @@ -384,7 +384,7 @@ class ApiAgente implements IApi $anmeArquivo = CONF_PATH_FILES . $mensagem['id_provedor']; $texto = base64_decode($mensagem['content']); file_put_contents($anmeArquivo, $texto); - $provider->ConvertWavToMp3($anmeArquivo); + $provider->convertWavToMp3($anmeArquivo); $retuno = $provider->enviarMedia( $mensagem['dst'], CONF_MIDDLEWARE_LINKUPLOAD . diff --git a/app/Providers/Positus.php b/app/Providers/Positus.php index c3ca38c..1aeba07 100644 --- a/app/Providers/Positus.php +++ b/app/Providers/Positus.php @@ -239,7 +239,7 @@ class Positus extends Requests implements IApiMedia } return false; } - function ConvertWavToMp3($fileOrig) + function convertWavToMp3($fileOrig) { try { // ffmpeg -i 61f1a021ca8908.72216404_1643308267776 61f1a021ca8908.72216404_1643308267776 @@ -247,7 +247,7 @@ class Positus extends Requests implements IApiMedia exec($cmd); copy($fileOrig . ".mp3", $fileOrig); } catch (\Exception $th) { - //throw $th; + logger('ConvertWavToMp3')->error($th->getMessage()); } } diff --git a/config/moments.php b/config/moments.php index 06c9702..6be8677 100644 --- a/config/moments.php +++ b/config/moments.php @@ -12,9 +12,7 @@ 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_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 \ No newline at end of file From ecf3361586a9546c8e1ecddd58d19ba6de2fb874 Mon Sep 17 00:00:00 2001 From: lucas cardoso Date: Fri, 25 Feb 2022 10:24:42 -0400 Subject: [PATCH 121/144] Update att-v3.sql --- database/att-v3.sql | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/database/att-v3.sql b/database/att-v3.sql index 7194ece..710c616 100644 --- a/database/att-v3.sql +++ b/database/att-v3.sql @@ -103,7 +103,7 @@ VALUES ), ( now(), - 'Atendimento iniciado!', + 'Atendimento iniciado com @agente_name!', 0, 'INICIAR_ATENDIMENTO' ), @@ -124,6 +124,24 @@ VALUES '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 ( From be58b42947e5e55f0f8063a1d8264a2a39adbd7d Mon Sep 17 00:00:00 2001 From: lucas cardoso Date: Fri, 25 Feb 2022 10:47:07 -0400 Subject: [PATCH 122/144] add listar fila quando cancelar a fila --- app/Core/Commands.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/app/Core/Commands.php b/app/Core/Commands.php index f810cc2..615d939 100644 --- a/app/Core/Commands.php +++ b/app/Core/Commands.php @@ -4,6 +4,7 @@ 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; @@ -33,8 +34,12 @@ class Commands /** @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(); @@ -151,6 +156,10 @@ class Commands $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->api->enviarMsg($numero, CONF_NAME_REPONSE . " : Não foi possível cancelar o atendimento! " . $this->bilheteController->message()); From 219ec130f94307943809eeb24238367c94169181 Mon Sep 17 00:00:00 2001 From: lucas cardoso Date: Fri, 25 Feb 2022 13:50:53 -0400 Subject: [PATCH 123/144] =?UTF-8?q?valida=C3=A7=C3=A3o=20da=20notifica?= =?UTF-8?q?=C3=A7=C3=A3o=20de=20novo=20atendimento?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Core/CoreMedia.php | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/app/Core/CoreMedia.php b/app/Core/CoreMedia.php index f0a70e4..8a527d3 100644 --- a/app/Core/CoreMedia.php +++ b/app/Core/CoreMedia.php @@ -213,8 +213,15 @@ class CoreMedia //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)) { + $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(); From 86157bc876c678e9129ec404832a779f3ade1696 Mon Sep 17 00:00:00 2001 From: lucas cardoso Date: Mon, 28 Feb 2022 08:49:14 -0400 Subject: [PATCH 124/144] Update CoreMedia.php --- app/Core/CoreMedia.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Core/CoreMedia.php b/app/Core/CoreMedia.php index 8a527d3..ded19dc 100644 --- a/app/Core/CoreMedia.php +++ b/app/Core/CoreMedia.php @@ -231,7 +231,7 @@ class CoreMedia } $this->systemController->sendMessageSystem( CONF_MOMENT_INICIAR_ATENDIMENTO, - [["nome" => "@agente_name", "valor" => $agent[0]->nome]], + [["nome" => "@agente_name", "valor" => utf8_encode($agent[0]->nome)]], $this->api, $numero ); From 2715d9d75b4c49e92a1d6d61dcdab127fe5d4a57 Mon Sep 17 00:00:00 2001 From: Simples IP Desenvolvimento Date: Mon, 28 Feb 2022 10:00:49 -0400 Subject: [PATCH 125/144] =?UTF-8?q?corre=C3=A7=C3=B5es=20e=20ajustes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/css/styles.css | 7 ------- public/images/alerta.png | Bin 0 -> 22579 bytes public/js/main.js | 37 ++++++++++++++++++++----------------- public/js/requests.js | 33 ++++++++++++++++++--------------- public/js/util.js | 34 ++++++++++++++++++++-------------- 5 files changed, 58 insertions(+), 53 deletions(-) create mode 100644 public/images/alerta.png diff --git a/public/css/styles.css b/public/css/styles.css index 41c5179..e97a4db 100644 --- a/public/css/styles.css +++ b/public/css/styles.css @@ -111,8 +111,6 @@ a { grid-column: 1 / 2; background: var(--white); border-right: 1px solid rgba(226, 226, 226, 0.7); - border-bottom-left-radius: 20px; - border-top-left-radius: 20px; } .sidebar-span { @@ -140,7 +138,6 @@ a { justify-content: space-between; height: 6rem; padding: 1rem 2rem; - border-top-left-radius: 20px; } /* Profile image */ @@ -1104,10 +1101,6 @@ input[type="range"] { } @media (max-height: 820px){ - .sidebar{ - height: 78vh; - } - #welcometomessage h1,h2{ font-size: 30px; diff --git a/public/images/alerta.png b/public/images/alerta.png new file mode 100644 index 0000000000000000000000000000000000000000..476337e457db55a24d77c7de52a2cb456b907037 GIT binary patch literal 22579 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 literal 0 HcmV?d00001 diff --git a/public/js/main.js b/public/js/main.js index f261e17..23744dc 100644 --- a/public/js/main.js +++ b/public/js/main.js @@ -61,7 +61,7 @@ $(function(){ }) $('#footer-content-right').on('click', '#footersend', () => { - sendMedia() + sendMedia(mediaRecorder) $('#modalselect').css({display: 'none'}) }) supervisorAgente() @@ -80,6 +80,7 @@ const selectNotification = (id) => { let number let name let dataContact + let protocolo const dataRequest = JSON.parse(localStorage.getItem('obj_contact')) listarAtendimentoAgente(localStorage.getItem('my_uniqueid')) @@ -113,6 +114,7 @@ const selectNotification = (id) => { uniqueid = e.uniqueid name = e.nome number = e.cliente_id + protocolo = e.protocolo } }) @@ -122,7 +124,7 @@ const selectNotification = (id) => { localStorage.setItem('session_window', number) $('.chat-window-contact-name').text(name) - $('.chat-window-contact-status').text('WhatsApp') + $('.chat-window-contact-status').text('Protocolo: ' + protocolo) /** REMOVE AS MSG NA E CONSTRIO A TELA NOVAMENTE (EVITAR DUPLICAR) */ $('.chat-window .sender').remove() @@ -295,30 +297,28 @@ const viewMessage = (ev) => { ${datesend}
`) break + case 'finish': case 're_start': $('.chat-window').append(`
${ev.event.mensagem.content}
`) break - case 're_start': - console.log('APENAS UM TESTE RÁPIDO DE TESTE') - break } - } - const mediaDownload = ["image", "voice", "document", "audio", "video", "sticker"] - if(mediaDownload.indexOf(ev.event?.mensagem.type) >= 0){ - const sendobj = { - filename: ev.event?.mensagem.file_name, - id_provedor: ev.event?.mensagem.id_provedor, - type: ev.event?.mensagem.type, - mimetype: ev.event?.mensagem.mimetype, - from: 'receiver' + const mediaDownload = ["image", "voice", "document", "audio", "video", "sticker"] + if(mediaDownload.indexOf(ev.event?.mensagem.type) >= 0){ + const sendobj = { + filename: ev.event?.mensagem.file_name, + id_provedor: ev.event?.mensagem.id_provedor, + type: ev.event?.mensagem.type, + mimetype: ev.event?.mensagem.mimetype, + from: 'receiver' + } + messageTypeMedia(sendobj) } - messageTypeMedia(sendobj) + scrollDown() } - scrollDown() } /** @@ -408,10 +408,13 @@ const keepMensage = (ev) => { /** * FUNÇÃO PARA CAPTURAR O ARQUIVO A SER ENVIADO */ - const sendMedia = () => { + const sendMedia = (media = null) => { let rec let filename if($("#footer-content-left audio").length){ + if(media.state == 'recording'){ + media.stop(); + } let el = $("#footer-content-left audio")[0].src fileContent = $("#footer-content-left audio")[0].outerHTML sendMessage({ content: el.replace("data:", "").replace(/^.+,/, ""), type: 'audio', mimetype: 'audio/mpeg', fileContent }) diff --git a/public/js/requests.js b/public/js/requests.js index f518e6e..7aa9ca6 100644 --- a/public/js/requests.js +++ b/public/js/requests.js @@ -24,7 +24,8 @@ const listaMensagem = (uniqueid) => new Promise((resolve) => { localStorage.setItem('obj_contact', JSON.stringify(res)) resolve() }, - error: function (res) { + error: function (res, status, error) { + console.error(status + ': ', res) alert('Nao foi possivel carregar as listas de mensagens.') } }) @@ -56,10 +57,7 @@ const listarAtendimentoAgente = (matricula) => new Promise((resolve) => { resolve(res) }, error: function (res) { - alertModal( - `

CARREGANDO MENSAGENS  

`, - 'Estamos restabelecendo a conexão' - ) + } }); }) @@ -136,7 +134,6 @@ const entrar = (matricula, queue) => new Promise((resolve) => { }); }) - const sair = (matricula) => { $.ajax({ url: `${server_api}/integracao/media/api/agente/sair`, @@ -178,7 +175,12 @@ const finalizarAtendimento = (matricula, uniqueid) => new Promise((resolve) => { resolve(res) }, error: function (res) { - alert('Nao foi possivel carregar as listas de pausa.') + alertModal( + `

OPS... HOUVE UM PROBLEMA  

+

Não foi possível finalizar atendimento!

+

Error: ${res}

`, + 'OPS!!!' + ) } }); }) @@ -196,18 +198,14 @@ const statusAgente = (matricula) => new Promise((resolve) => { resolve(res) }, error: function (res) { - alertModal( - `

RECONECTANDO, AGUARDE  

`, - 'Estamos restabelecendo a conexão com nosso sistema!' - ) - console.log('Recarregando ...') + console.error('Não foi possível reconectar ao agente supervisor!') } }); }) const atualizaAgente = () => new Promise((resolve) => { $.ajax({ - url: `${server_api}/index.php?idProg=14&idSubProg=3&ajax=1&acao=atualiza`, + url: window.location.href + `index.php?idProg=14&idSubProg=3&ajax=1&acao=atualiza`, type: "GET", success: function(res){ resolve(res) @@ -222,7 +220,7 @@ const transferirAtendimento = (origem, destino, uniqueid) => new Promise((resolv data: JSON.stringify({ matricula_origem: origem, matricula_destino: destino, - uniqueid + uniqueid }), success: function (res) { if (res.status == 'success') { @@ -234,7 +232,12 @@ const transferirAtendimento = (origem, destino, uniqueid) => new Promise((resolv resolve(res) }, error: function (res) { - alert('Nao foi possivel carregar as infoemacoes do agente.') + alertModal( + `

OPS... HOUVE UM PROBLEMA  

+

Não foi possível carregar as infoemacoes do agente!

+

Error: ${res}

`, + 'OPS!!!' + ) } }); }) diff --git a/public/js/util.js b/public/js/util.js index ed18135..80124bd 100644 --- a/public/js/util.js +++ b/public/js/util.js @@ -133,7 +133,7 @@ const startPause = () => { if(selectPause.length > 0){ selectPause = `` } else { - selectPause = '

Nenhuma pausa foi encontrado!

' + selectPause = '

Nenhuma pausa foi encontrado!

' } return selectPause }); @@ -174,22 +174,22 @@ const startTransfer = () => { return `` } $('#transfersend').hide() - return `

Nenhum agente disponível no momento!

` + return `

Nenhum agente disponível no momento!

` }); }) - - $('.modal-header-title').append(`Selecione um agente para transferir:`) $('#footer-content-right').append(``) $('#modalselect').show() }) $('#footer-content-right').on('click', '#transfersend', () => { - transferirAtendimento(localStorage.getItem('my_uniqueid'), $("#selectranfer").val(), localStorage.getItem('session_uniqueid')).then(() => { - hideButtons(true) - notifications({ matricula: localStorage.getItem('my_uniqueid'), uniqueid: localStorage.getItem('session_uniqueid'), action: 'finish' }) - $('#modalselect').css({display: 'none'}) + transferirAtendimento(localStorage.getItem('my_uniqueid'), $("#selectranfer").val(), localStorage.getItem('session_uniqueid')).then((res) => { + if(res.status == 'success'){ + hideButtons(true) + notifications({ matricula: localStorage.getItem('my_uniqueid'), uniqueid: localStorage.getItem('session_uniqueid'), action: 'finish' }) + $('#modalselect').css({display: 'none'}) + } }) }) } @@ -359,8 +359,7 @@ const buildNotification = (data = {}) => {
${data.media}
-
-
+
` @@ -447,7 +446,8 @@ const notifications = (obj = {}) => { media: e.context, name: e.profile_name, datetime: e.data_reg, - status: e.status + status: e.status, + protocolo: e.protocolo }) }) $('#chats').append(chatList) @@ -472,7 +472,10 @@ const monitorPausaAgente = () => { $('#exitPause').hide() $('#status_agent').attr('class','status-reconnect') } else { - $('#status_agent').attr('class','status-connect') + $('#entrePause').hide() + $('#exitPause').show() + $('#status_agent').attr('class','status-reconnect') + statusagent = agente.data.status } $('#status_agent').text(statusagent) @@ -500,9 +503,7 @@ const supervisorAgente = () => { window.close() } }) - supervisorAgente() - }, 40000); } @@ -518,6 +519,10 @@ const connect = (wsserver) => { }; ws.onerror = function(err) { + alertModal( + `

DESCONECTADO, TENTANTO RECONECTAR  

`, + 'Estamos tentando restabelecer a conexão com nosso sistema, por favor aguarde!' + ) $("#status_agent").addClass("status-desconnect").text('DESCONECTADO'); ws.close(); }; @@ -527,6 +532,7 @@ const connect = (wsserver) => { entrar(localStorage.getItem('my_uniqueid'), localStorage.getItem('obj_queue')).then((login) => { if(login.status == 'success' || login.status.indexOf('autenticado') === -1){ + $('#modalselect').css({display: 'none'}) monitorPausaAgente() ws.send(JSON.stringify({matricula: localStorage.getItem('my_uniqueid')})); notifications() From 9663a48c0798e895866e8be98a301e70dddaed81 Mon Sep 17 00:00:00 2001 From: lucas cardoso Date: Wed, 9 Mar 2022 10:04:21 -0400 Subject: [PATCH 126/144] =?UTF-8?q?adequa=C3=A7=C3=A3o=20do=20projeto=20pa?= =?UTF-8?q?ra=20Twilio?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Controllers/AgentController.php | 4 +- app/Core/CoreMedia.php | 37 +++---- app/Interfaces/IApiMedia.php | 4 +- app/Middleware/ApiAgente.php | 10 +- app/Middleware/Middleware.php | 24 ++--- app/Providers/ApiTwilio.php | 156 ++++++++++++++++++++++++++-- app/Providers/Positus.php | 59 ++--------- app/Providers/RequestURL.php | 1 + app/Providers/Requests.php | 12 ++- app/Providers/WebHeader.php | 4 +- app/Providers/whatsapp.php | 9 ++ composer.json | 4 +- config/helpers.php | 33 ++++++ index.php | 3 +- service/ServiceQueue.php | 17 +-- service/ServiceTimeout.php | 18 ++-- 16 files changed, 260 insertions(+), 135 deletions(-) create mode 100644 app/Providers/whatsapp.php diff --git a/app/Controllers/AgentController.php b/app/Controllers/AgentController.php index 31930d4..78390ea 100644 --- a/app/Controllers/AgentController.php +++ b/app/Controllers/AgentController.php @@ -8,12 +8,10 @@ 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 app\Providers\Positus; use Exception; use websocket\WsInterface; @@ -277,7 +275,7 @@ class AgentController extends Controller $agentTransf->matricula ); //$ws->enviaMsg($ws->enviaActions('Atendimento transferido', 'transfer', $agent->matricula, $uniqueid)); - $provedor = new Positus(); + $provedor = returnChannel($atendAtual->context); $provedor->enviarMsg($atendAtual->cliente_id, CONF_NAME_REPONSE . ": Atendimento transferido"); $messegeModel = new Message(); $messegeModel->addMessage( diff --git a/app/Core/CoreMedia.php b/app/Core/CoreMedia.php index f0a70e4..2058ba0 100644 --- a/app/Core/CoreMedia.php +++ b/app/Core/CoreMedia.php @@ -26,9 +26,6 @@ class CoreMedia /** @var IApiMedia $api api do provedor de mensagens */ protected $api; - /** @var array $request hook da requisição de webhook*/ - protected $request; - /** @var Commands $commands classe de comandos do sistema*/ protected $commands; @@ -92,13 +89,13 @@ class CoreMedia public function inicia($data, IApiMedia $providere) { /** Validate $data */ - $this->build($data); $this->api = $providere; - $this->api->setHook($this->request); + $this->build($data); + if (!$this->api->getIsValidMessage()) { return null; } - logger('positus')->info(print_r($data, true)); + if (!$this->api->baixarMidia()) { $this->api->enviarMsg($this->api->getPhone(), "Não foi possivel entregar o aquivo para o agente. Envie novamente!"); } @@ -144,29 +141,20 @@ class CoreMedia */ public function build($data) { - $this->request = $data; + $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; - } + $this->api->setMessage($msg); } catch (\Exception $th) { } return; @@ -234,7 +222,12 @@ class CoreMedia ); 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)); + $ws->enviaMsg($ws->enviaActions( + 'Este atendimento foi realocado para sua responsabilidade.', + 're_start', + $agent[0]->matricula, + $uniqueid + )); $this->message->addMessage( $uniqueid, $agent[0]->matricula, @@ -304,7 +297,7 @@ class CoreMedia $this->api->getId() ); $this->ws->enviaMsg( - $this->api->convertToWebsocket( + $this->ws->convertToWebsocket( $this->retornaConteudo(), $atendiment->matricula, $atendiment->uniqueid, @@ -313,7 +306,9 @@ class CoreMedia $this->api->getPhone(), time(), $this->api->getId(), - $this->api->getMimetype() + $this->api->getMimetype(), + $this->api->retornaTituloDocument(), + $this->api->getchannel() ) ); } diff --git a/app/Interfaces/IApiMedia.php b/app/Interfaces/IApiMedia.php index 626b70c..7bc6b35 100644 --- a/app/Interfaces/IApiMedia.php +++ b/app/Interfaces/IApiMedia.php @@ -5,6 +5,8 @@ namespace app\Interfaces; interface IApiMedia { 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); @@ -18,10 +20,10 @@ interface IApiMedia function getId(); function getIsValidMessage(); function getMessage(); + function setMessage($msg); function getContactFormatted(); function getContactPhone(); function getGeolocation($type); function setHook($hook); function retornaTituloDocument(); - function convertToWebsocket($content, $matricula = '', $idAtendimento, $type, $name, $number, $data, $idProvedor, $mimetype); } \ No newline at end of file diff --git a/app/Middleware/ApiAgente.php b/app/Middleware/ApiAgente.php index 9562e49..e27ced0 100644 --- a/app/Middleware/ApiAgente.php +++ b/app/Middleware/ApiAgente.php @@ -15,7 +15,6 @@ use app\Models\Parametros; use app\Models\Pause; use app\Models\Queue; use app\Models\SupervisorModel; -use app\Providers\Positus; use websocket\WsInterface; class ApiAgente implements IApi @@ -243,7 +242,7 @@ class ApiAgente implements IApi $ws = new WsInterface(); $ws->enviaMsg($this->enviaActions('Atendimento finalizado', 'finish', $agente->matricula, $request['uniqueid'])); $systemController = new SystemMessageController(); - $provedor = new Positus(); + $provedor = returnChannel($atendimento->context); $systemController->sendMessageSystem( CONF_MOMENT_FINALIZAR_ATENDIMENTO, [], @@ -298,8 +297,9 @@ class ApiAgente implements IApi $fila, $matricula ); + $atendimento = $this->atendimento->findAtendId($uniqueid); $systemController = new SystemMessageController(); - $provedor = new Positus(); + $provedor = returnChannel($atendimento->context); $systemController->sendMessageSystem( CONF_MOMENT_ERRO_ATEND, [], @@ -371,7 +371,7 @@ class ApiAgente implements IApi $contact = $request['event']['contact']; $status = $this->atendimento->getStatusAtendimento($mensagem['uniqueid']); if ($status->evento == CONF_EVENT_START) { - $provider = new Positus(); + $provider = returnChannel($mensagem['media']); $modelMensagem = new Message(); $msgTexto = $contact['name'] . ': ' . $this->validaPalavroes($mensagem['content']); if ($mensagem['type'] == 'text') { @@ -384,7 +384,7 @@ class ApiAgente implements IApi $anmeArquivo = CONF_PATH_FILES . $mensagem['id_provedor']; $texto = base64_decode($mensagem['content']); file_put_contents($anmeArquivo, $texto); - $provider->ConvertWavToMp3($anmeArquivo); + ConvertWavToMp3($anmeArquivo); $retuno = $provider->enviarMedia( $mensagem['dst'], CONF_MIDDLEWARE_LINKUPLOAD . diff --git a/app/Middleware/Middleware.php b/app/Middleware/Middleware.php index 4c8297b..5c82b05 100644 --- a/app/Middleware/Middleware.php +++ b/app/Middleware/Middleware.php @@ -6,7 +6,7 @@ use app\Core\CoreMedia; use app\Middleware\Http; use app\Middleware\ApiAgente; use app\Providers\WebHeader; -use app\Providers\Positus; +use app\Providers\Whatsapp; /** * Description of WppController @@ -16,7 +16,7 @@ use app\Providers\Positus; class Middleware extends Http { - /** @var return Positus request */ + /** @var array request */ private $request; /** @class WebHeader */ @@ -29,6 +29,7 @@ class Middleware extends Http { $this->link = $config['LINK_UPLOAD']; $this->header = new WebHeader($config); + $this->api(); $this->hook(); } @@ -36,9 +37,9 @@ 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); } /** @@ -54,13 +55,11 @@ class Middleware extends Http $this->baseUri(); switch (strtolower($this->param()[1])) { case 'whatsapp': - $coremedia->inicia($this->request, new Positus()); - echo json_encode(['success' => true]); + $provedor = new Whatsapp(); + $coremedia->inicia($this->request, $provedor); + $this->api($provedor->getContentType()); + echo ''; return null; - // case 'telegram': - // $coremedia->inicia($this->request, new ApiTelegram()); - // echo json_encode(['success' => $this->request]); - // return null; case 'api': switch ($this->param()[2]) { case 'agente': @@ -70,13 +69,13 @@ class Middleware extends Http $apiSupervisor->router($this->param()[3], $this->request); return null; default: - $this->header->redirect(); + echo 'erro'; } case 'link': $this->header->fileTransfer($this->param()[2], CONF_PATH_FILES . $this->param()[2], base64_decode($this->param()[3])); exit(0); default: - $this->header->redirect(); + echo 'erro'; } } @@ -86,7 +85,6 @@ class Middleware extends Http private function hook() { $this->request = file_get_contents('php://input'); - $this->request = json_decode($this->request, true); $this->router(); } diff --git a/app/Providers/ApiTwilio.php b/app/Providers/ApiTwilio.php index 7790fcb..eb635de 100644 --- a/app/Providers/ApiTwilio.php +++ b/app/Providers/ApiTwilio.php @@ -7,24 +7,50 @@ use Twilio\Rest\Client; class ApiTwilio implements IApiMedia { - private $sid = 'AC1a22ada91ee3f0e63178419f4d31b37a'; - private $token = '70ee98619e000b10810860cbbe1329bc'; + 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 = 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)); + 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:+5565996107663", // to + "whatsapp:+$whatsapp", // to [ - "from" => "whatsapp:+14155238886", - "body" => "Hello there!" + "from" => "whatsapp:+{$this->numeroTwilio}", + "body" => $mensagem, ] ); - - logger('twilio')->info(var_export($message)); + logger('twilio')->info(var_export($message->body)); + return $message->body; } function enviarMsgIterativaLista($whatsapp, $mensagem, $nomeButton, $sections) { @@ -40,44 +66,154 @@ class ApiTwilio implements IApiMedia } 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() { + return empty($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]; + } + print_r($map); + + // 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() { - } - function convertToWebsocket($content, $matricula = '', $idAtendimento, $type, $name, $number, $data, $idProvedor, $mimetype) - { + return $this->hook['Body']; } } \ No newline at end of file diff --git a/app/Providers/Positus.php b/app/Providers/Positus.php index c3ca38c..a227479 100644 --- a/app/Providers/Positus.php +++ b/app/Providers/Positus.php @@ -8,8 +8,11 @@ class Positus extends Requests implements IApiMedia { /** @var string $hook resposta do webhook */ private $hook; - private $storage = CONF_PATH_FILES; - private $channel = CONF_WHATSAPP_CHANNEL; + + function getContentType() + { + return "application/json; charset=UTF-8"; + } public function getchannel() { @@ -18,7 +21,7 @@ class Positus extends Requests implements IApiMedia function setHook($hook) { - $this->hook = $hook; + $this->hook = json_decode($hook, true); } function setToken() @@ -26,41 +29,6 @@ class Positus extends Requests implements IApiMedia $this->token = CONF_WHATSAPP_AUTH_TOKEN; } - function setUrl() - { - $this->url = CONF_WHATSAPP_AUTH_URL; - } - - public function convertToWebsocket($content, $matricula = '', $uniqueid, $type, $name, $number, $data, $idProvedor, $mimetype) - { - if ($number) { - $mensagem = []; - $mensagem["event"] = [ - "type" => "mensagem", - "contact" => [ - "name" => $name, - "number" => $number, - "matricula" => '' - ], - "mensagem" => [ - "type" => $type, - "content" => $content, - "id_provedor" => $idProvedor, - "dst" => $matricula, - "uniqueid" => $uniqueid, - "media" => $this->channel, - "file_name" => $this->retornaTituloDocument(), - "datetime" => $data, - "status" => "sent", - 'mimetype' => $mimetype - ] - ]; - - return json_encode($mensagem); - } - return null; - } - function enviarMedia($whatsapp, $link, $type, $titulo = null) { $tipos = []; @@ -239,17 +207,6 @@ class Positus extends Requests implements IApiMedia } return false; } - 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; - } - } /** * Profile WhatsApp @@ -337,6 +294,10 @@ class Positus extends Requests 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 diff --git a/app/Providers/RequestURL.php b/app/Providers/RequestURL.php index 9a4925d..0ada0c2 100644 --- a/app/Providers/RequestURL.php +++ b/app/Providers/RequestURL.php @@ -86,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); diff --git a/app/Providers/Requests.php b/app/Providers/Requests.php index 239db85..52f5135 100644 --- a/app/Providers/Requests.php +++ b/app/Providers/Requests.php @@ -25,11 +25,17 @@ class Requests /** @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(); + $this->setUrl(CONF_WHATSAPP_AUTH_URL); } function response($result) @@ -126,8 +132,8 @@ class Requests $this->token = CONF_WHATSAPP_AUTH_TOKEN; } - function setUrl() + public function setUrl($url) { - $this->url = CONF_WHATSAPP_AUTH_URL; + $this->url = $url; } } \ No newline at end of file diff --git a/app/Providers/WebHeader.php b/app/Providers/WebHeader.php index 9cad500..3c3d075 100644 --- a/app/Providers/WebHeader.php +++ b/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'] diff --git a/app/Providers/whatsapp.php b/app/Providers/whatsapp.php new file mode 100644 index 0000000..15c9383 --- /dev/null +++ b/app/Providers/whatsapp.php @@ -0,0 +1,9 @@ +api(); \ No newline at end of file +$middle = new Middleware($config); \ No newline at end of file diff --git a/service/ServiceQueue.php b/service/ServiceQueue.php index d2df5d0..f4eb39e 100644 --- a/service/ServiceQueue.php +++ b/service/ServiceQueue.php @@ -3,10 +3,8 @@ namespace service; use app\Core\CoreMedia; -use app\Providers\Positus; use app\Models\Atendimento; use app\Models\SupervisorModel; -use app\Providers\ApiTelegram; class ServiceQueue implements IService { @@ -45,11 +43,12 @@ class ServiceQueue implements IService function run() { try { - $this->coremedia->setApi(new Positus()); + $agentesLista = $this->supervisor->listaAgentesDisponivel('LIVRE'); foreach ($agentesLista as $item) { $atendimentos = $this->atendimento->getAtendFila($item->fila); if (count($atendimentos) > 0) { + $this->coremedia->setApi(returnChannel($atendimentos[0]->context)); $this->coremedia->criaAtendimento( $atendimentos[0]->fila, $atendimentos[0]->cliente_id, @@ -61,16 +60,4 @@ class ServiceQueue implements IService logger('monitora')->info($ex->getMessage(), debug_backtrace()); } } - - function retornaApiMedia($media = '') - { - switch ($media) { - case CONF_TELEGRAM_CHANNEL: - return new ApiTelegram(); - case CONF_WHATSAPP_CHANNEL: - return new Positus(); - default: - break; - } - } } \ No newline at end of file diff --git a/service/ServiceTimeout.php b/service/ServiceTimeout.php index ce74e51..d2ba68b 100644 --- a/service/ServiceTimeout.php +++ b/service/ServiceTimeout.php @@ -4,19 +4,18 @@ namespace service; use app\Controllers\MessageController; use app\Core\Commands; +use app\Interfaces\IApiMedia; use app\Models\Atendimento; use app\Models\Message; use app\Models\NotificaMedia; -use app\Providers\Positus; -use DateInterval; class ServiceTimeout implements IService { /** @var String[] $mensagem lista de mensagens para o sistema*/ private $mensagem; - /** @var Positus $positus provider positus*/ - private $positus; + /** @var IApiMedia $api provider api*/ + private $api; /** @var MessageController $message controller de message*/ private $message; @@ -37,7 +36,6 @@ class ServiceTimeout implements IService function __construct() { - $this->positus = new Positus(); $this->message = new MessageController(); $this->notificaMedia = new NotificaMedia(); $this->mensages = new Message(); @@ -54,9 +52,11 @@ class ServiceTimeout implements IService } public function run() { - $this->command->setApi($this->positus); + $atendiss = $this->atendimentos->findAtenEmAberto(); foreach ($atendiss as $value) { + $this->api = returnChannel($value->context); + $this->command->setApi($this->api); $this->timeoutCliente($value->uniqueid, $value->cliente_id); $this->timeoutSessao($value->uniqueid, $value->cliente_id, $value->data_reg); } @@ -65,7 +65,7 @@ class ServiceTimeout implements IService { // echo "unique: $uniqueid - client: $client \n"; if ($this->message->timeoutTalk($uniqueid, $client) == 'FINISH') { - $this->positus->enviarMsg($client, $this->mensagem['TIMEOUT_CLIENT_INATIVIDADE']); + $this->api->enviarMsg($client, $this->mensagem['TIMEOUT_CLIENT_INATIVIDADE']); $this->command->finalizar($client); return null; } @@ -82,7 +82,7 @@ class ServiceTimeout implements IService $client, utf8_encode($this->mensagem['TALK_ALERT_FINISH']) . $msg->id ); - $this->positus->enviarMsg( + $this->api->enviarMsg( $client, $this->mensagem['TALK_ALERT_FINISH'] ); @@ -104,7 +104,7 @@ class ServiceTimeout implements IService $client, utf8_encode($this->mensagem['TIMEOUT_CLIENT_INATIVIDADE']) . $uniqueid ); - $this->positus->enviarMsg( + $this->api->enviarMsg( $client, $this->mensagem['TIMEOUT_CLIENT_INATIVIDADE'] ); From f14a5cbf95ee79711e8d57dff4649e8633ae8a30 Mon Sep 17 00:00:00 2001 From: lucas cardoso Date: Wed, 23 Mar 2022 09:21:15 -0400 Subject: [PATCH 127/144] add union na consulta de agente --- app/Models/SupervisorModel.php | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/app/Models/SupervisorModel.php b/app/Models/SupervisorModel.php index fb4a216..83d13af 100644 --- a/app/Models/SupervisorModel.php +++ b/app/Models/SupervisorModel.php @@ -12,6 +12,7 @@ class SupervisorModel extends Model const EVENTO_AGENTE = 'pbx_eventos_agentes'; const BILHETE = 'pbx_bilhetes'; const EVENTOS_DACS = 'pbx_eventos_dacs'; + private $supervisor = 'md_supervisor'; private $atendimento = 'md_atendimento'; @@ -56,7 +57,7 @@ class SupervisorModel extends Model ) < (SELECT prm_media_simultaneo FROM pbx_parametros pp LIMIT 1 )"; } if ($status) { - $this->query .= " AND ms.status = :status "; + $this->query .= " AND ms.status = :status "; $data['status'] = $status; } $this->query .= " ORDER BY countAtendimentos "; @@ -86,8 +87,10 @@ class SupervisorModel extends Model public function findByMatricula($matricula) { - $this->query = "SELECT * " - . "FROM " . self::SUPERVISOR_AGENTE . " WHERE (matricula = :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(); } @@ -97,7 +100,7 @@ class SupervisorModel extends Model $data = []; $this->query = "SELECT * FROM " . self::SUPERVISOR_AGENTE . " WHERE 1=1 "; if ($queue) { - $this->query .= " AND fila = :queue "; + $this->query .= " AND fila = :queue "; $data['queue'] = $queue; } return $this->read($this->query, $data)->fetchAll(); @@ -109,7 +112,7 @@ class SupervisorModel extends Model $data = []; $this->query = "SELECT * FROM pbx_supervisor_agentes WHERE 1=1 "; if ($queue) { - $this->query .= " AND fila = :queue "; + $this->query .= " AND fila = :queue "; $data['queue'] = $queue; } return $this->read($this->query, $data)->fetchAll(); @@ -257,7 +260,6 @@ class SupervisorModel extends Model return $this->create($this->query, $data); } - public function updateAgent( $matricula, $status = 'LIVRE', From 54e77bbacd6f856f16c296a4d6fd8379d5b4f950 Mon Sep 17 00:00:00 2001 From: lucas cardoso Date: Wed, 23 Mar 2022 09:25:39 -0400 Subject: [PATCH 128/144] =?UTF-8?q?valida=C3=A7=C3=A3o=20da=20remo=C3=A7?= =?UTF-8?q?=C3=A3o=20de=20agente?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- service/ServiceSupervisorPBx.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/service/ServiceSupervisorPBx.php b/service/ServiceSupervisorPBx.php index 5fcb496..44d7fe5 100644 --- a/service/ServiceSupervisorPBx.php +++ b/service/ServiceSupervisorPBx.php @@ -26,7 +26,7 @@ class ServiceSupervisorPBx implements IService $agentesPbx = $this->supervisorModel->findAllAgentesPBX(); foreach ($agentesPbx as $key => $pbx) { $age = $this->supervisorModel->findAgentByMatricula($pbx->matricula); - if (empty($age)) { + if (empty($age) && empty($pbx->ramal)) { $this->supervisorModel->deleteAgentPbx($pbx->matricula); } } From f3292698ce0719ec28febe6ff7623f3c1c21bd7a Mon Sep 17 00:00:00 2001 From: lucas cardoso Date: Wed, 23 Mar 2022 09:26:54 -0400 Subject: [PATCH 129/144] =?UTF-8?q?valida=C3=A7=C3=A3o=20dos=20servi=C3=A7?= =?UTF-8?q?os=20quando=20cair=20o=20banco?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/test_services.php | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/tests/test_services.php b/tests/test_services.php index 281f20e..39f5587 100644 --- a/tests/test_services.php +++ b/tests/test_services.php @@ -11,11 +11,16 @@ $sevice_timeout = new ServiceTimeout(); $servce_queue = new ServiceQueue(); $servce_supervisor = new ServiceSupervisorPBx(); while (true) { - echo "executar servce_supervisor \n"; - $servce_supervisor->run(); - echo "executar sevice_timeout \n"; - $sevice_timeout->run(); - echo "executar servce_queue \n"; - $servce_queue->run(); - sleep(3); + try { + echo "executar servce_supervisor \n"; + $servce_supervisor->run(); + echo "executar sevice_timeout \n"; + $sevice_timeout->run(); + echo "executar servce_queue \n"; + $servce_queue->run(); + sleep(3); + } catch (\Throwable $th) { + sleep(3); + logger('run_service')->info($th->getMessage()); + } } \ No newline at end of file From 95ba8e8c0e55118fa46c068bd0af087a01d4b598 Mon Sep 17 00:00:00 2001 From: lucas cardoso Date: Wed, 23 Mar 2022 09:27:34 -0400 Subject: [PATCH 130/144] =?UTF-8?q?add=20fun=C3=A7=C3=A3o=20de=20remo?= =?UTF-8?q?=C3=A7=C3=A3o=20de=20chars=20special?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/helpers.php | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/config/helpers.php b/config/helpers.php index 5fad220..137df5c 100644 --- a/config/helpers.php +++ b/config/helpers.php @@ -13,4 +13,29 @@ function whereIn($array) return "'" . implode("','", $array) . "'"; } return "'$array'"; +} + +//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); } \ No newline at end of file From e879bdfa16b7a1ad7f3384a4aa2b3514231fde7a Mon Sep 17 00:00:00 2001 From: lucas cardoso Date: Thu, 24 Mar 2022 17:02:26 -0400 Subject: [PATCH 131/144] validar inatividade do agente --- service/ServiceSupervisorPBx.php | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/service/ServiceSupervisorPBx.php b/service/ServiceSupervisorPBx.php index 44d7fe5..f3102a7 100644 --- a/service/ServiceSupervisorPBx.php +++ b/service/ServiceSupervisorPBx.php @@ -16,18 +16,24 @@ class ServiceSupervisorPBx implements IService $this->supervisorModel = new SupervisorModel; $agentes = $this->supervisorModel->listaAgentesDisponivel(null, false); foreach ($agentes as $key => $agente) { - $agentPbx = $this->validaAgentCriado($agente->matricula); - if (!$agentPbx) { - $this->criaRegistroSupervisor($agente); + if ($this->validaInatividade($agente->duracao)) { + $agentPbx = $this->validaAgentCriado($agente->matricula); + if (!$agentPbx) { + $this->criaRegistroSupervisor($agente); + } else { + $this->atualizaTabelaSupervisor($agente, $agentPbx); + } } else { - $this->atualizaTabelaSupervisor($agente, $agentPbx); + $this->supervisorModel->deleteAgent($agente->matricula); } } $agentesPbx = $this->supervisorModel->findAllAgentesPBX(); foreach ($agentesPbx as $key => $pbx) { - $age = $this->supervisorModel->findAgentByMatricula($pbx->matricula); - if (empty($age) && empty($pbx->ramal)) { - $this->supervisorModel->deleteAgentPbx($pbx->matricula); + if (empty($pbx->ramal)) { + $age = $this->supervisorModel->findAgentByMatricula($pbx->matricula); + if (empty($age) && empty($pbx->ramal)) { + $this->supervisorModel->deleteAgentPbx($pbx->matricula); + } } } } @@ -84,4 +90,13 @@ class ServiceSupervisorPBx implements IService } return 0; } + + function validaInatividade($dateTime) + { + $timealert = strtotime($dateTime . '+40 seconds'); + if ($timealert < strtotime(date('Y-m-d H:i:s'))) { + return false; + } + return true; + } } \ No newline at end of file From 685d97f15ed9ff734b46f191cf850bf8a1bde934 Mon Sep 17 00:00:00 2001 From: lucas cardoso Date: Thu, 24 Mar 2022 17:02:56 -0400 Subject: [PATCH 132/144] atualizar o campo duaracao no supervisor --- app/Models/SupervisorModel.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/Models/SupervisorModel.php b/app/Models/SupervisorModel.php index 83d13af..87c919a 100644 --- a/app/Models/SupervisorModel.php +++ b/app/Models/SupervisorModel.php @@ -265,12 +265,13 @@ class SupervisorModel extends Model $status = 'LIVRE', $motivo_pausa = null ) { - $this->query = "UPDATE {$this->supervisor} SET status = :status, motivo_pausa = :motivo_pausa"; + $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); } From b4c7de26b32bb38ae42eba8182b9c344d4f56de5 Mon Sep 17 00:00:00 2001 From: lucas cardoso Date: Thu, 24 Mar 2022 17:04:25 -0400 Subject: [PATCH 133/144] melhoria no retorno do endpoint listarAtendimentoAgent --- app/Middleware/ApiAgente.php | 54 +++++++++++++++++++++--------------- 1 file changed, 32 insertions(+), 22 deletions(-) diff --git a/app/Middleware/ApiAgente.php b/app/Middleware/ApiAgente.php index 3974619..4beda2a 100644 --- a/app/Middleware/ApiAgente.php +++ b/app/Middleware/ApiAgente.php @@ -443,21 +443,26 @@ class ApiAgente implements IApi function listaAtendimentoAgent($request) { - if ($request['matricula']) { - $agente = $this->supervisor->findAgentByMatricula($request['matricula']); - //verifica se existe agente - if (empty($agente)) { - $this->retorno("Agente não encontrado"); - return; + try { + if ($request['matricula']) { + $agente = $this->supervisor->findAgentByMatricula($request['matricula']); + //verifica se existe agente + if (empty($agente)) { + $this->retorno("Agente nã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; } - $ret = $this->atendimento->findAtendAgent($request['matricula'], $request['quantidade']); - $this->retorno( - $ret ? "Sucesso" : "Erro", - $ret ? $ret : null, - $ret ? $ret : null - ); - return null; } function listaMensagem($request) @@ -513,21 +518,24 @@ class ApiAgente implements IApi //verifica se existe agente if (empty($agente)) { $this->retorno("Agente não encontrado"); - return; + return null; } $param = $this->parametros->findProtocolByParams(); - if ($param->prm_pausa_grupo) { - $retunr = $this->pausasModel->findPauseByGroupUser($request['matricula']); + if (false) { + $ret = $this->pausasModel->findPauseByGroupUser($request['matricula']); + } else { + $ret = $this->pausasModel->findAllPause(); } - if (empty($retunr)) { - $retunr = $this->pausasModel->findAllPause(); + foreach ($ret as $key => $value) { + $ret[$key] = removeAcentosArray((array) $value); } + $this->retorno( - $retunr ? "Sucesso" : "Erro", - $retunr ? $retunr : null, - $retunr ? $retunr : null + $ret ? "Sucesso" : "Erro", + $ret ? $ret : null, + $ret ? $ret : null ); - return; + return null; } function entrarPausa($request) @@ -635,7 +643,9 @@ class ApiAgente implements IApi $this->retorno("Agente nã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, From 3420a2da552594d3d1a266dcfe94aaf6ccc45e25 Mon Sep 17 00:00:00 2001 From: lucas cardoso Date: Fri, 25 Mar 2022 08:30:45 -0400 Subject: [PATCH 134/144] add try catch nos services --- service/ServiceQueue.php | 2 +- service/ServiceSupervisorPBx.php | 40 ++++++++++++++++++-------------- service/ServiceTimeout.php | 14 +++++++---- 3 files changed, 32 insertions(+), 24 deletions(-) diff --git a/service/ServiceQueue.php b/service/ServiceQueue.php index d2df5d0..f7f9bff 100644 --- a/service/ServiceQueue.php +++ b/service/ServiceQueue.php @@ -58,7 +58,7 @@ class ServiceQueue implements IService } } } catch (\Exception $ex) { - logger('monitora')->info($ex->getMessage(), debug_backtrace()); + logger('ServiceQueue')->info($ex->getMessage(), debug_backtrace()); } } diff --git a/service/ServiceSupervisorPBx.php b/service/ServiceSupervisorPBx.php index f3102a7..f683d17 100644 --- a/service/ServiceSupervisorPBx.php +++ b/service/ServiceSupervisorPBx.php @@ -13,28 +13,32 @@ class ServiceSupervisorPBx implements IService function run() { - $this->supervisorModel = new SupervisorModel; - $agentes = $this->supervisorModel->listaAgentesDisponivel(null, false); - foreach ($agentes as $key => $agente) { - if ($this->validaInatividade($agente->duracao)) { - $agentPbx = $this->validaAgentCriado($agente->matricula); - if (!$agentPbx) { - $this->criaRegistroSupervisor($agente); + try { + $this->supervisorModel = new SupervisorModel; + $agentes = $this->supervisorModel->listaAgentesDisponivel(null, false); + foreach ($agentes as $key => $agente) { + if ($this->validaInatividade($agente->duracao)) { + $agentPbx = $this->validaAgentCriado($agente->matricula); + if (!$agentPbx) { + $this->criaRegistroSupervisor($agente); + } else { + $this->atualizaTabelaSupervisor($agente, $agentPbx); + } } else { - $this->atualizaTabelaSupervisor($agente, $agentPbx); + $this->supervisorModel->deleteAgent($agente->matricula); } - } else { - $this->supervisorModel->deleteAgent($agente->matricula); } - } - $agentesPbx = $this->supervisorModel->findAllAgentesPBX(); - foreach ($agentesPbx as $key => $pbx) { - if (empty($pbx->ramal)) { - $age = $this->supervisorModel->findAgentByMatricula($pbx->matricula); - if (empty($age) && empty($pbx->ramal)) { - $this->supervisorModel->deleteAgentPbx($pbx->matricula); + $agentesPbx = $this->supervisorModel->findAllAgentesPBX(); + foreach ($agentesPbx as $key => $pbx) { + if (empty($pbx->ramal)) { + $age = $this->supervisorModel->findAgentByMatricula($pbx->matricula); + if (empty($age) && empty($pbx->ramal)) { + $this->supervisorModel->deleteAgentPbx($pbx->matricula); + } } } + } catch (\Exception $ex) { + logger('ServiceSupervisorPBx')->info($ex->getMessage(), debug_backtrace()); } } @@ -93,7 +97,7 @@ class ServiceSupervisorPBx implements IService function validaInatividade($dateTime) { - $timealert = strtotime($dateTime . '+40 seconds'); + $timealert = strtotime($dateTime . '+70 seconds'); if ($timealert < strtotime(date('Y-m-d H:i:s'))) { return false; } diff --git a/service/ServiceTimeout.php b/service/ServiceTimeout.php index ce74e51..11c14a1 100644 --- a/service/ServiceTimeout.php +++ b/service/ServiceTimeout.php @@ -54,11 +54,15 @@ class ServiceTimeout implements IService } public function run() { - $this->command->setApi($this->positus); - $atendiss = $this->atendimentos->findAtenEmAberto(); - foreach ($atendiss as $value) { - $this->timeoutCliente($value->uniqueid, $value->cliente_id); - $this->timeoutSessao($value->uniqueid, $value->cliente_id, $value->data_reg); + try { + $this->command->setApi($this->positus); + $atendiss = $this->atendimentos->findAtenEmAberto(); + foreach ($atendiss as $value) { + $this->timeoutCliente($value->uniqueid, $value->cliente_id); + $this->timeoutSessao($value->uniqueid, $value->cliente_id, $value->data_reg); + } + } catch (\Exception $ex) { + logger('ServiceTimeout')->info($ex->getMessage(), debug_backtrace()); } } private function timeoutCliente($uniqueid, $client) From b2a984f07608bd71c7029ecdb6cce936b9937c40 Mon Sep 17 00:00:00 2001 From: lucas cardoso Date: Fri, 25 Mar 2022 08:31:03 -0400 Subject: [PATCH 135/144] =?UTF-8?q?alterando=20ip=20de=20produ=C3=A7=C3=A3?= =?UTF-8?q?o?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Middleware/Middleware.php | 1 - config/whatsapp.php | 2 +- websocket/Servidorsocket.php | 2 +- websocket/WsInterface.php | 2 +- websocket/websocket.php | 2 +- 5 files changed, 4 insertions(+), 5 deletions(-) diff --git a/app/Middleware/Middleware.php b/app/Middleware/Middleware.php index 95ecdf8..4ff0fd3 100644 --- a/app/Middleware/Middleware.php +++ b/app/Middleware/Middleware.php @@ -48,7 +48,6 @@ class Middleware extends Http */ private function router() { - $coremedia = new CoreMedia(); $apiAgente = new ApiAgente(); $apiSupervisor = new ApiSupervisor(); diff --git a/config/whatsapp.php b/config/whatsapp.php index bb01d6f..f4123fe 100644 --- a/config/whatsapp.php +++ b/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/'); /* |-------------------------------------------------------------------------- diff --git a/websocket/Servidorsocket.php b/websocket/Servidorsocket.php index a36491b..85fb688 100644 --- a/websocket/Servidorsocket.php +++ b/websocket/Servidorsocket.php @@ -36,7 +36,7 @@ class Servidorsocket implements MessageComponentInterface { try { $headers = $conn->httpRequest->getHeaders(); - $conn->send($conn->resourceId); + $conn->send('att'); $this->clients->attach($conn); $this->connection[$conn->resourceId] = null; $this->shell[$conn->resourceId] = null; diff --git a/websocket/WsInterface.php b/websocket/WsInterface.php index 4ad8e74..781dcd0 100644 --- a/websocket/WsInterface.php +++ b/websocket/WsInterface.php @@ -11,7 +11,7 @@ class WsInterface function enviaMsg($msg) { if (!empty($msg)) { - $this->client = new Testess("ws://192.168.115.65:8080/ws"); + $this->client = new Testess("ws://191.252.0.171:8080/ws"); $this->client->send($msg); $this->client->close(); return null; diff --git a/websocket/websocket.php b/websocket/websocket.php index f3b0b9c..17453f8 100644 --- a/websocket/websocket.php +++ b/websocket/websocket.php @@ -6,7 +6,7 @@ include __DIR__ . '/../includes/config.php'; use websocket\Servidorsocket; try { - $app = new Ratchet\App('192.168.115.65', 8080, '0.0.0.0'); + $app = new Ratchet\App('191.252.0.171', 8080, '0.0.0.0'); $app->route('/ws', new Servidorsocket(), array('*')); $app->run(); } catch (\Exception $th) { From 71f6fcfe79d7923739c9870221ab647c704f4512 Mon Sep 17 00:00:00 2001 From: lucas cardoso Date: Fri, 25 Mar 2022 09:55:49 -0400 Subject: [PATCH 136/144] Update .gitignore --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index c7e1704..a98b225 100644 --- a/.gitignore +++ b/.gitignore @@ -12,7 +12,6 @@ Homestead.yaml npm-debug.log yarn-error.log /.idea -/.vscode /stubs /public/node_modules /public/vendor From 9d936241af163a0487283d39b4b7dacbca886dc9 Mon Sep 17 00:00:00 2001 From: lucas cardoso Date: Fri, 25 Mar 2022 10:06:47 -0400 Subject: [PATCH 137/144] alterando ws para local e adicionando func convetws --- websocket/WsInterface.php | 31 ++++++++++++++++++++++++++++++- websocket/websocket.php | 2 +- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/websocket/WsInterface.php b/websocket/WsInterface.php index 4ad8e74..8f2ce36 100644 --- a/websocket/WsInterface.php +++ b/websocket/WsInterface.php @@ -11,7 +11,7 @@ class WsInterface function enviaMsg($msg) { if (!empty($msg)) { - $this->client = new Testess("ws://192.168.115.65:8080/ws"); + $this->client = new Testess("ws://127.0.0.1:8080/ws"); $this->client->send($msg); $this->client->close(); return null; @@ -40,4 +40,33 @@ class WsInterface logger('monitora')->info($th->getMessage(), debug_backtrace()); } } + public function convertToWebsocket($content, $matricula = '', $uniqueid, $type, $name, $number, $data, $idProvedor, $mimetype, $file_name = null, $channel) + { + if ($number) { + $mensagem = []; + $mensagem["event"] = [ + "type" => "mensagem", + "contact" => [ + "name" => $name, + "number" => $number, + "matricula" => '' + ], + "mensagem" => [ + "type" => $type, + "content" => $content, + "id_provedor" => $idProvedor, + "dst" => $matricula, + "uniqueid" => $uniqueid, + "media" => $channel, + "file_name" => $file_name, + "datetime" => $data, + "status" => "sent", + 'mimetype' => $mimetype + ] + ]; + + return json_encode($mensagem); + } + return null; + } } \ No newline at end of file diff --git a/websocket/websocket.php b/websocket/websocket.php index f3b0b9c..b2fa57b 100644 --- a/websocket/websocket.php +++ b/websocket/websocket.php @@ -6,7 +6,7 @@ include __DIR__ . '/../includes/config.php'; use websocket\Servidorsocket; try { - $app = new Ratchet\App('192.168.115.65', 8080, '0.0.0.0'); + $app = new Ratchet\App('127.0.0.1', 8080, '0.0.0.0'); $app->route('/ws', new Servidorsocket(), array('*')); $app->run(); } catch (\Exception $th) { From 85f897a237a4532c2d0bd38f28db4b6c4b91227a Mon Sep 17 00:00:00 2001 From: lucas cardoso Date: Fri, 25 Mar 2022 10:07:26 -0400 Subject: [PATCH 138/144] =?UTF-8?q?corre=C3=A7=C3=B5es=20twilio?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Core/CoreMedia.php | 13 ++++++++----- app/Middleware/ApiAgente.php | 1 + app/Middleware/Middleware.php | 3 ++- app/Providers/ApiTwilio.php | 12 ++++++------ 4 files changed, 17 insertions(+), 12 deletions(-) diff --git a/app/Core/CoreMedia.php b/app/Core/CoreMedia.php index 2058ba0..c9f01b5 100644 --- a/app/Core/CoreMedia.php +++ b/app/Core/CoreMedia.php @@ -95,9 +95,8 @@ class CoreMedia if (!$this->api->getIsValidMessage()) { return null; } - if (!$this->api->baixarMidia()) { - $this->api->enviarMsg($this->api->getPhone(), "Não foi possivel entregar o aquivo para o agente. Envie novamente!"); + $this->api->enviarMsg($this->api->getPhone(), "N�o foi possivel entregar o aquivo para o agente. Envie novamente!"); } // VERIFICA SE ? UM COMANDO if ($this->commands->isCommand($this->api)) { @@ -105,11 +104,12 @@ class CoreMedia } //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; } - //verifica se tem atendimento em espera, se tiver ja mostra a sua posição na fila + //verifica se tem atendimento em espera, se tiver ja mostra a sua posi��o na fila $atende = $this->atendimento->getAtendimentoByCliente($this->api->getPhone()); if (empty($atende)) { $atende = $this->atendimento->getAtendimentoByCliente($this->api->getPhone(), CONF_EVENT_ERRO_ATEND); @@ -118,7 +118,7 @@ class CoreMedia $this->mostraPosiscaoNaFila($this->api->getPhone(), $atende->fila); return null; } - //verifica se o cliente escolheu uma fila caso não escolheu manda as filas + //verifica se o cliente escolheu uma fila caso n�o escolheu manda as filas $filas = $this->queue->listAllQueueWhatsApp($this->api->getMessage()); if ($filas['LIST']) { //manda as mensagens personalizada do moment CONF_MOMENT_SAUDACAO @@ -218,7 +218,7 @@ class CoreMedia ); $this->api->enviarMsg( $numero, - "Número do protocolo do atendimento é $protocol\n" + "N�mero do protocolo do atendimento � $protocol\n" ); try { if ($event->evento == CONF_EVENT_ERRO_ATEND) { @@ -283,6 +283,7 @@ class CoreMedia function enviaMensagemAgente($atendiment) { + $this->message->addMessage( $atendiment->uniqueid, $this->api->getPhone(), @@ -296,6 +297,7 @@ class CoreMedia $this->api->retornaTituloDocument(), $this->api->getId() ); + logger('vvvv')->info('passou1'); $this->ws->enviaMsg( $this->ws->convertToWebsocket( $this->retornaConteudo(), @@ -311,5 +313,6 @@ class CoreMedia $this->api->getchannel() ) ); + logger('vvvv')->info('passou2'); } } \ No newline at end of file diff --git a/app/Middleware/ApiAgente.php b/app/Middleware/ApiAgente.php index e27ced0..577992b 100644 --- a/app/Middleware/ApiAgente.php +++ b/app/Middleware/ApiAgente.php @@ -53,6 +53,7 @@ class ApiAgente implements IApi function router($rota, $request) { + $request = json_decode($request, true); switch ($rota) { case 'entrar': $this->login($request); diff --git a/app/Middleware/Middleware.php b/app/Middleware/Middleware.php index 5c82b05..5e2e58d 100644 --- a/app/Middleware/Middleware.php +++ b/app/Middleware/Middleware.php @@ -29,7 +29,6 @@ class Middleware extends Http { $this->link = $config['LINK_UPLOAD']; $this->header = new WebHeader($config); - $this->api(); $this->hook(); } @@ -61,6 +60,7 @@ class Middleware extends Http echo ''; return null; case 'api': + $this->api(); switch ($this->param()[2]) { case 'agente': $apiAgente->router($this->param()[3], $this->request); @@ -72,6 +72,7 @@ class Middleware extends Http 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); default: diff --git a/app/Providers/ApiTwilio.php b/app/Providers/ApiTwilio.php index eb635de..bd5fa98 100644 --- a/app/Providers/ApiTwilio.php +++ b/app/Providers/ApiTwilio.php @@ -33,7 +33,7 @@ class ApiTwilio implements IApiMedia "mediaUrl" => [$link] ] ); - logger('twilio')->info(var_export($message->body)); + logger('twilio')->info(var_export($message->body, true)); return $message->body; } function enviarMsg($whatsapp, $mensagem, $encode = true) @@ -49,7 +49,7 @@ class ApiTwilio implements IApiMedia "body" => $mensagem, ] ); - logger('twilio')->info(var_export($message->body)); + logger('twilio')->info(var_export($message->body, true)); return $message->body; } function enviarMsgIterativaLista($whatsapp, $mensagem, $nomeButton, $sections) @@ -114,7 +114,8 @@ class ApiTwilio implements IApiMedia } function getIsValidMessage() { - return empty($this->hook); + logger('getIsValidMessage')->info('getIsValidMessage: ' + is_array($this->hook)); + return is_array($this->hook); } function getMessage() { @@ -145,7 +146,6 @@ class ApiTwilio implements IApiMedia $auxi = $array = explode("=", $value); $map[$auxi[0]] = $auxi[1]; } - print_r($map); // mensagem normal // [SmsMessageSid] => SM3f3e0cac9d0da519ec0ffa7f15eaa15b @@ -179,14 +179,14 @@ class ApiTwilio implements IApiMedia // [MediaUrl0] => https://api.twilio.com/2010-04-01/Accounts/ACab626d6f133aa20b21879d37cd21b139/Messages/MM0e531d626f74ec950e637d7491b0080c/Media/ME4ff81dee03f2cd1b7875d87954e8abd7 // [ApiVersion] => 2010-04-01 - // documento não valido + // documento n�o valido // [SmsMessageSid] => MM2f04ee178bf6843dd26ac19e6e193c19 // [NumMedia] => 0 // [ProfileName] => Lucas // [SmsSid] => MM2f04ee178bf6843dd26ac19e6e193c19 // [WaId] => 556596107663 // [SmsStatus] => received - // [Body] => INSTALAÇÃO WHATSAPP.docx + // [Body] => INSTALA��O WHATSAPP.docx // [To] => whatsapp:+553140428280 // [NumSegments] => 1 // [MessageSid] => MM2f04ee178bf6843dd26ac19e6e193c19 From 0f60dbed2d0b4bdae2a03f6a322c107dc5e640ac Mon Sep 17 00:00:00 2001 From: Simples IP Desenvolvimento Date: Fri, 1 Apr 2022 08:53:22 -0400 Subject: [PATCH 139/144] =?UTF-8?q?ajustes=20servi=C3=A7o=20do=20superviso?= =?UTF-8?q?r?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Models/SupervisorModel.php | 15 ++++++-- config/app.php | 10 +++++ public/css/styles.css | 5 +-- public/images/enter.svg | 2 + public/images/wallpaper_simpleschat.png | Bin 0 -> 214516 bytes public/index.html | 9 +++-- public/js/requests.js | 18 +++++---- public/js/util.js | 48 +++++++++++++----------- service/ServiceSupervisorPBx.php | 2 +- 9 files changed, 68 insertions(+), 41 deletions(-) create mode 100644 public/images/enter.svg create mode 100644 public/images/wallpaper_simpleschat.png diff --git a/app/Models/SupervisorModel.php b/app/Models/SupervisorModel.php index fb4a216..d894a5a 100644 --- a/app/Models/SupervisorModel.php +++ b/app/Models/SupervisorModel.php @@ -103,7 +103,7 @@ class SupervisorModel extends Model return $this->read($this->query, $data)->fetchAll(); } - public function findAllAgentesPBX($queue = null) + public function findAllAgentesPBX($queue = null, $media = null) { $data = []; @@ -112,6 +112,11 @@ class SupervisorModel extends Model $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(); } @@ -334,10 +339,14 @@ class SupervisorModel extends Model return $this->update($this->query, $data); } - public function deleteAgentPbx($matricula) + public function deleteAgentPbx($matricula, $media = null, $oper = '=') { $data = []; - $this->query = "DELETE FROM pbx_supervisor_agentes WHERE matricula = :matricula"; + $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); } diff --git a/config/app.php b/config/app.php index 447a377..a853937 100644 --- a/config/app.php +++ b/config/app.php @@ -48,6 +48,16 @@ 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 diff --git a/public/css/styles.css b/public/css/styles.css index e97a4db..77bb3b6 100644 --- a/public/css/styles.css +++ b/public/css/styles.css @@ -79,8 +79,7 @@ a { } .bottom { - /** background-image: linear-gradient(to right top, #af8866, #b7766e, #ae6984, #8e679b, #506ba8); */ - background-image: linear-gradient(to right bottom, #676767, #626262, #5d5d5d, #585858, #535353); + background-image: linear-gradient(to right top, #af8866, #b7766e, #ae6984, #8e679b, #506ba8); grid-column: 1 / -1; grid-row: 1 / 5; } @@ -416,8 +415,6 @@ a { grid-column: 2 / -1; background: var(--chat-window-beige); position: relative; - border-bottom-right-radius: 20px; - border-top-right-radius: 20px; } /* Main chat window header */ diff --git a/public/images/enter.svg b/public/images/enter.svg new file mode 100644 index 0000000..402ccae --- /dev/null +++ b/public/images/enter.svg @@ -0,0 +1,2 @@ + + diff --git a/public/images/wallpaper_simpleschat.png b/public/images/wallpaper_simpleschat.png new file mode 100644 index 0000000000000000000000000000000000000000..c25b149d58657b36d9eaf3777e900cf872ddcd88 GIT binary patch literal 214516 zcmXtf1z1$y7cC9aNOviMgtT;bcMshyU4t}8mvlD>L${PP0z=nGNW+j)!ic{6`@i@2 zK0b!aaOa$J&)IwJwbuTmrJ;z2O@)nwgoLN8B&UOfgu0A`ghqsg0bF^L`GpVof#xlv ztcL}Bg<#pn0l&ZSR5JDk-rw}!H!|4H_zAd3=_7CCqw8Vs<8S3n z^Q!wf^dI!dpx!8ru5ne>y)z&9du^SHU+?CR&P>|8sx?~GI`MI zuK+RI)^#O)eQCt7y>Fc%@Ikx5bNoqP%^O!fZJqnIz|Kt^f^fGk{e#aR5St=vU#teK z#VkC+OgRg&w^Er_nt$)?_H-J-LO?J*5WZifcuCJ6#;jm!H<=`pF7oS z{BH`es16U0A%8#k=sGw8;{o373sTi4alr^4jYne;?Sq|TP)BaZ1J@rAApF?Kvj;@C zhr?(*Dc{ar(B$4V#mt3q;VCJ*LH%znU<`p0#lM33?ERhFbuF?EoPReu&I3c=_HfFp zvJ-X$UI1%zUNAo$?=j1L>guAeuR}8WqmzVGeX-3uQg}!-WvCt*?X|&p&}jVS@VOr` z(Tl1eSd+*yaDSug@BlG7;O-S1-|KPZL5+G@2Zo~Z+t{YG1Am!+d2kL)c!*2R8h}<8 z_|;9naib%c$p2dv^TuDH0|xV?i;(_Ue5T5pAA)dJq&l2#SsmdQm@j`R$e=-?HrUZ8_2h86A^8kjfxO%Q$7f;HMpz);J$yH7! zek#*p`~U9|U?L6QK8Grn@~ER9pSW-yJD}L3;A@^H)rp*@}hM#N|V;t01*7 z4gSz-jKN2d!m=NWz;gVOQi(OO^{p!`u~&SsK7XnC^T3fP>A$D0gr~Kn|MI^BO9NQPA(e0uVaS*xm0IVjN~|y;a}M_@7_5FD1|4S5-LMsQp|@_|r+$ z;2Q-@3Tx=*(KTsFQ$AT5HKGDdaXf<0>C~$SRzP)){TD z2wI(KCv21^diZR{m0e=K6mkg6);Y8qVYmbX66?#aSMGG)_ibOcTEFA2V$}Hc9Gcw&A!jqGT2-j`pKEv5xqSvT zTXSHk^{gN{^j;RtksAFIs-{xxamrg!o)LFez{r=pb(?wa2iBN}Q-|H~Fb-gv)`pusJF^&7egjv1(K77efUJ*pn#ZV8v z3SHEpymLPoRdXIs69%8bK1Kods#UzZ9F{8r+l84#BDtjDt_0dIh(_J&@C~P%RGqkG)X7%eYw_L?n z7;Ni<6#yw?-47jb(3FhFGxhLnrT?Ad!VD=VAUKVRFEfHi7}%mm@%P{>$@PV-#w5rn zWJly+5W$TaU7jetb9@`xH{DmUv(<^t>fk-kTUrbfhqB~v%FBygI$`Sf1^t7iMQ_*Q zw7GbHXA z17xmTT~mVdkCD<0V7^MMZaj#~~_a`Ob(5?&&O z`#}0T_j6&s=w3jLMX8p42oVU5gwePiZ!q2jE6l1&~o&?qTO0l|F|NY%r}ffrLkJ z@2197i;q3@9?|#1y|V+?5uea@>N#ZmdbeQOsVJs|LshsKx`oU^bIo?oKyFD`=|$4k z_etkTt72fxx~xadN)o-DZ6)FDNMq^FemY@iUvt~dcLTK8W1A)kV~0q{ zY|Kjd)^TqL-%v82=@#a8OB+CN*rU}p&X%iXaZtKf-c?f8;y$gy55R5(ClsgiEFc{| z@53M4Y|SbsQceQbOGn1WS)8bof9USZglis}E)T1UVI{e*j1)kMF~tL5F#G|C&oC{`1H$lEhBzZQ4Qv(Ey*HT4LOB(s|>E~bCy`^^K%82*6Z z%>G{Kw%kJE=(|W*nksSj5G-7%abBv(ZO=;h&a~N@%ac3gdw;Br`ryp{W$z}JQR)mP zqlPPJ)vcoriag9V#B+_le%d|ASH+ur*QKW~u>NSCRe2+x+EDb;BOn4n4v?{drYYw;9rB&N&l$>J1z>0QNOuXtiYsL~B=(E_;g%ICZ{a zqHhH0lE`~bD(dT>(`AZGNae!xZps@Q8_E7I($WCiEV8VaB*3^?Y}&XZcUhy>NugM) zl@GnHE*;njB6Zy<>zNVnwT1->Q)af`Fx!<&HW=}|uCe$dsCDVc=V#o>Rx=NQ3#i{H zCX_aIP#>-TdEio^VsfBrva1r*ndgan?yH4Yf#;VM4RuF!hdU3x>xD5YiCc3$e10h_ zcY2R_E;g`cBQi;rGSy1ke#SlOY>Z}XFhi`ce8RbxC(3;_NJwnoJrfTdD)7@4)DSQp z-E{Bly2tgl>4dfC(r%pY$IY30+FS?qZREM(=|%P8<1hKI26kC`M{Sg)2?zZ381i@D74qT^R*R$r9!sEuK;t5?fff6as@BoFbI zmU`pMDI5;#hEf*JO;pO0?Y7nD>S<7)MQKer>gzZ3%T6_8TTTBYf%}!Vy{qWOhPrIM zmq`Ory+6Zy;;Wa1%xt^u?V6gDwtHb|E!548?K5&Ow`=lZprK6f)N8sZLdt2qOyP#! zxIXVZZ~T7;PYGY(h#O8LHLW}{Nb&hyz_uDBL@AkPjYXbs!AAuquZ)G3YuXiYp6?B&}c!E9oGP!Es45~ z)4+D8??)a>Z!*o`(%HLST*7RkrcHMMx~iS(jY~+`x$X)}u5Ai(v)+R~i zLT{cqf*%rXl2r*5Zi2*P_A|&GB1`OtV+GdK-AG64{8fVa>b&6!_#XM;PFWc_AKzbU z??2!k%S_iJC{o?xe;PSGCmLyB=5_%yrs@Ybto2+p;$V$d2E3!Vl9ZI3Z>04iXk(ih z`nzy%X`E$n6n7)iVIw|LOZLc#s$7Wst3AJo{)5=>UUMjG&hS{58COHn=?#s|J?3^Y z{Zqe2weIE3O)%Dy^?*2C%F4ho!W_kHEgFRTviNAtRq*3ex4-zXU$5IXkmK?*Kd5X6 zMumb8sU8xVnB79b_k{h8tk@m`!H2vSCzI3@SkLc0XKkjHFW6<~04$r6$|`NP7p{7M=jydqW)U?~%_La)+*Qmd0tf{oZ5E zMXaTxBJz{jkzSqbJ!SsOs?5{B$Y&Zo|cYPb=k7B5gY`k^WO_A*#Kxd|SkZtKe2z znwixXd8!R>V~-dBXaN7~vLEHj1?2R)I_Ed0`1#`e1oTq*^VHZ1!uhoMpOtH)vK9>A zxrvyB#0rzo#0g!(FgF~T_@r5sM2C9DsuLEzG1eLr9=6`}fvJUU27P-6DF*B2Ncbe6 z^!>rJ0-O9vV#QEDGs-t0;t`|uLoL>TPLaXV0nL9!5Qm#jKJU=ckP%XY+_ot`Gn)9z z&3m@yHoco2ZEn;PmMvq`1Bi%)k|lv$#s^|ishBj;F`;GW5m0XVoSMycfA(pir^560 zzh{edIHIiXZMPX$t4P$6`X2YgIgVCryfJ-eESn~3Dz#zRD~yZ$FGJ09JrNTZ(d*q)XTbsHpbb0 z&Mf><>z-G2E?yWZHLm68u<;;ug`Vcm!stuJnDB))Uhr+E>?^9HF-+hoy;YG83mY81 zdHmyWBj;G|V*VIEeC+sU#}h6Z)%9_UQNhX(#E6V)dM-@OuyhOAP&>3+75crB=zZA+ zJ+1=}ofLoJ!V)Q;_;@qWII9R{G{u;HK;&dd#Ii0~9~u~E8QWp$l-JemxH@JtlcEYL zNM)DTs|KiPlNJYC7yiazsuE!ypEK$mi)WCpNt-l3)NOpf|0JQ7TKces-iCD80ru>< zc<6nP$47STkjO)4Eh_Di{HY&Fm7K6o6`g|ArnJy8ZNNc$R-jInka-6ndFoB5bsJ#A zKT|o5(b)y72*yk#ilHm%Z|;BP;ByD*3(vrmFVSMD+D|J{>U2&eHq>;O5=wm+^z|nn zLuTVKE>Ov^k}*$1wETu_Nv`<%VSZ41 z)rNjDQzT}FS3CUrwK7UO#DnHmxNcDSgt|GtVwZby$#3bU!Aa1r9$a`SLrJ=9iK*O1 zpGVzGG^2pb2wcilJ;czQ?^s}V-Fq(SsI^PQVtGeW!Yd!~@i#@vTx+w@waANa?B@~$ zaw1;WpTG~rs~x>Rm$Tm3YTUgMTFMjTt8Bu&lLg2ZuIvPVA7x$MkR@#I2#7yRnF?9@ zaF_!V`>AQC_~_7}IKKARepRsvbuM_eENNDFelz$qBZM z6-jc^ua`)So+#9N8PYL=7}iwc9q?P>DXrwdB8j|1_#M^Kffe>JS7v`Qkw%lIoXB!Z z=~)bhKj`$5>6hjp4_d!lJU}byeGcR|^b$!Rh?VuK^d@>NzZV_z!*=31NZ{eOm>E;L zHXm;i4`~LHp{LLvqO_?`FC{R&JnI6tdA7xa@cZlEq6}R=z`hh@@@W(o_8;L$kXc^r zp`HYfWqEEw&D`2q*4JxG&Ff^v-$^Y78Mr=SnN z5mH%^=3N`gF#@)z(*1M(3YNqF#YgBKV=ne~AO)HZx6x%bDwz9=sDk`rYaI?>}TEo2wXF-^i zd{D+VOvfGXw_?^>W88$kZPR*P1?5@q(I>2v`(oZj^O6z{E2kAQ!ijId!!kfvGboIL zos$&`MyGYzI@3O9y2Wy0o)Xd~LAvurbUAld6E;XVy@kIy%14F$U8((Pb1T*A*AB6+ zH@n2G7WJ_hmKyGoA@QjZCsOfE5f~46sGYS0(NovGg-R5vq3(y&?EBJqXLj66Z-gO{?3%sNDd;Ne zi{*3ZTF)sR10!!n$*4WeV!J7|*QW08&vbWovAJA)dTJ}ip+#*F*CrV)Nw4dNr`Tbq z@heddR?%Dp^(i>0_f03iw;rdQ`vY$upEaUDXLywQxo_o{BpA_XIXtm7M0smFI(%Nx$#V^pRZ~ig> zbtd08G7dJc=*3A8F6SmJ443j%xJAFi+L~+i;kbL$P4$`vb7wxSN2kV#YaqLwh8&{D zMoZ|0v{C}O8;CPzjfMct?j=!t;MM)d$p*u6Xzf0yd)pNMxa{@#kL=!KYoRGFOR=uM z?&hi^P@%$jtKGMSj7pz&O5TBbTsV4JcR^v{{3~po!f3e-%yR=8cA562(bGEHhu$BhO}J&fR_0jd-*QUbPJdBYDq9 z8FwuD)AvihN1oXj-FVSFZnLh@c6Wvv(a=OIpd|8JXd>|@b>WSQB-^EEp~lCNV8nfT zxASg>@G?(ui8}IWm9~gPp@y*?v07>+{--fD{p>xI^?{`@`ze|N>YgH><5E=*U8eT_ zxb|kk+zJ||+dNxM>o6tXVk5U3=^T32Y$klbyZd8qaOcwXs-v zEyH+>Q};XUf%bbW?OWEG!G`o@dVGG|lLc0^K(|P;S88dl z*=)f__bO~IQ~3_$y;S9Q{=Of1lIP!+KSBR-4+h92gz%-BC*v!n1ljNOQISNrH&$mS ze9>IkHqGmQR-4i9J+o=wul`Wq)sqdza(mB*>C;AD=dU3S?U70fzZ_va<)h}@{kN*1 z_S47({j@s*OMP(yputRMN0O^n81LNe#?ww+uv1X z1eo5I?aP01pvPX#q+&PS`@k(AB2z_+s3yq>yM!SX;na?Lc>Tvo`i5C!xc1^(HE8ZU z)eEL#ZLJ}JAvn6H3_f=6LJCV#p}9kKOO^z|1H&dd5px%qf+LN&ee*mOy>A6(-Fw$Pg#}ecM$q<6_nJvIFMkGOY8LOm~E*deD9Pm9+Ei z`3doL+qPFnfJnAB-gG<1X-urVLk}BD!HEG(s zFOU>j->IGwwh?i)!4Pgl4)M~}`i|$cS{+_Yy2qik_2yX8%ue-1$;s0*CDbIvfi&pbsXxzJoh5t zJzCW^zOlXv1>>xS-=O;7p_P{9>S{6qAI2w$Jap{`m6wa-XSKx>W2ypi>2qF@T~bFWw4Z&s5?kj0sPns8Jnr?;0THyW1RVtczKE3Ye1i8wkMhx7Hc zamaI1!Xw9-Mf@kpBd%9bF%Z6MT7u_)7mo5#JRVu>< zmUJ{J<5_{n3sXb$kh(h3E%{=*(XRo0=Eg$dT!d=;{~X10`XyNig=JS+vc@vL_#E!o-uMjkA6Y{CkGNCYI*mnZ~3QNRC0KVORsb)r9J)@BNY#so2 zqOCd3e{JG=jwjI!T{U>mP*C1;M}{CpTB(dPRRp7X^P_<|JFFpZrq}^Ixit89ZAZQ4 zI zQ)SyW$nB?O(Dv*c#u-$`yb}<9u`VkXCaiGQ26}p)6A=`E?2nu;LS#$ z!n-0kPIWJn{egFcLVbC>3y1MR2}5$9?rz<-70>g>*b24mYfVOHftBWCo!{>VVs|8& z#G1LI7HCaO$l|-(nE0t#4$X$Rlz+zU#&w|t0!Kj-qdjXhAS>yeRWeJc3 zf+H3nyefFv;$rg3F`rmP4*=1!2h!)_R~6H=kM* zJXmsI%zqGtZb&k^(^Jw-5L4S|C0n1wqJSoV=H2r4_ut{4bEn87ytoNvsX(==$zsjs zyWAs@b-nO!Ik_fgwmyo%4{AZ(LnG6&{Pa9v-q_(DR3j@s{^$N#V`N;WIEAD0JLB=x zpNCQie8o}St|a+Gx{UMl#Ks=Or~<$j)^%Jy0}B8B*S}~CyQD7%apYZ@i%QZoQ%BWY zbAvw|Q(gOJ8T`R9qBzs`2?wMM>4q3^uqq;K&~aOI^YX#XsY?AJZe$f0TCDI-lmi?z z;xE&ZI!5U)`(*y-Q%0{5_+#`$81d`X&sh|?P#{Glgyg;sNO zoe@oBZx^-UE3K%KRFg=QpGs3GcIExjT>Cv+QS`5pHN@SyANBO$+^2Z0rp5yfksr}C ze~5@E-Ka-w!kQJBmMZEV&Z8kmx#b~N zxA`(rb{BYty47AzX`?KV@+ezNn{8?V{?a>Cnq6Hqd7lcE$O+mzQrmZClxbB?0Nz%G z;N<0hi`r;*(^brw^B+#Ja*c``0whJaP_85#)}*c&hKhzvfsYy?CTp70m01a1-aSIY zI3UR>hiu)Odv<*=kQtH?d|0PM0nbK#MID=Juio>CO&%qTF~Hioe5mObv@HmzBALbI z?2y6rHTgMB`I|Cfd~d$Pz;q$VW;>Tz^`YUMMjGF{Fwt|mkKq;fDQwcI^~Bxci$2_q z`>2bn@mC-Ls2!f*Rp+G4?O&18-?qk z!`fC#Rfvu2=?3le9|=rYGwaB-Zo zSL@ig6L!2WRhXNDK3cm5w=zDUtBY3g3e1f)Ugl1SsV`fzzvlP826Rvz-j{s3;<2+4 z6CPj}_sifH^$j^nLujuY$xXAP25ZlI(Aq+Xs4Dz@adVj+#ZTR$+pv|sPr)R#JdaJ>OW&1QmUv0^=S-5F=}r=jm3FX(+=o+oqsZK$Y%WWEA&54o!95 zW{`4(c&O_azl`*!bRIf{bcSgNcFH~ni0y|+Jlvc9aQzE^rz9xI5>sck7gC`^8IvbD zZkAhC1jbkE*Z%X7YVM~e#j9VxLL|!F>d?YNq$GpV{;jYga~^NVYfnI-DgPU<{N-1yF~}ye=-(KZ}Q#TGOAqDRE|1ouS@A#auCR@rk;r$^@594W6P2y)DEVBL>L*$AhwCpRM(7?S$EO1id&FBk@m5@5Y zuiaaTujU!|sIe;LBVgNlgMYV_pZg^y*gZ;XB+C=&7Wve_UN%n$+2Wc;@F^9(fg&a3 z138*|jVrq|)iF~@JMkiOR_zl7IR3@Xepiuf(2NUiZRCnW@%dK;CH7m|3~*BJOv3!S zh&X@}F>SX+DsD*3thelckynEa2LzBSco=QA>*#K(gQ zryXfPjRaYB`3|A&HD`q_1!Erpwk{X9ie_l#XgmlmM!Hx{5NUv)Z-^g30OASRQ0b)}&IK zp9uiAp_)jAI$kfENnsM1Xg=Pqsz>YLvzhP$4u)x^)6rOzH(1)x%7uNm9Cii_A*j{Z z9f*L12LFyEoPF!jy))zc!2<7ZnJ_mw&5OP3-_StiRF{Emz>DU%>U>Ob0|M02<++wV z2b7QAXULITRKB0R9cqU%-ROOyY}MMhL(-~vu<#T1ML% zl6gcpfhFmD3oC+XM4ouE)8iK)h7cQppmne+XTWd;2=0a3(kxr(Fag(xt%FQ=LF)go z?Zdo39=#T{5y1mrNoZ0P$e)bO1>9(t6j5>DQeoL~ri;P_WytWe4p%%XWYnZL><||U z2&?k@hKeslH9pdNf8NpF4#7Kt7okMjPw!nTL9izhZj$oU=kJkE7mb*UUXUWRsC55T z%G+8iPQ=ADH;?#5r~!V&tg$wq#KP}3Elk*hs#)Kg*awE z)qDgrSbEsRlco~J#!^M7&1@;so^hJrFftgn^_9edN(z|(oc+h$QE))BJ-EhkJ#-+2 zsfsjVS8QeS+lg}JS3Gux{XkujC&oFwOB$1!h}w%`j`s`rShKC_^)Cb-7>_8x(XqS# z{$BpW_-L>{a6}P|N0d7vxMTyY$6`kA$MPlSeQ-rn?QIBe7ngx~8#=o)qKBL7E0Reaf?+eaO) zNrp-tFmoAigfMn7KrNW;$(JJo9GIpPC<0KT}w2KW3D32sj+L$tyCS;Tn| zMYzNeX0~kGx~qxZME|Al3?CrqmUtvy4?N(I9ET9=ot$NRZ zZhux4dr&IAD`362bQRNR^BeQ%gcV;d2MCFmly-4l95N04n?E3YpYw#`06imAm zr3@}qTRSHC05(c^3CP&}@MGNGm@%(CG{Ja_%w??)vOS8Pu(3Arcxld*4|r=dy5H)<=QWttlL;$&KAINh zRvn(?mGI&(rpD6xwOzT` zBhWUNh|hZRB#>q$Fe+>6>T8$vVFhuI?fRmnM>ZUlK3|>;6|j+N?R>)oh{ms%nINJP zpR(`ik@IKYGW`IT=Qb|NHV;3f&O8|FFH?T z>jd)k8>$x`7sFP{ls%fU?4|>m8zpQORO-J}vFMnGqo%^XMTI^?k z7JLZZkIeDQpe#V=$uMCz;J{l`%se>=Fu)9);2E(d%(X1Pt^GLDa4xG&QW#5sjLMr{ z`(E1F$VP}j_USX^w;6}Bp4-VBf0YM@!^lhAlLBe7G}m5E26vlRo`*W$>2^6D(F#Dt z-ogn04YP5PHVD|BwP*WDuy1&r>jY}-=%}TdtboxtD;5uGygjs1n|tZR${$?ug_Eq( z!DcN87!k5#{v60=U|SW?2Qjn0UM5D1-Civu&76!L26M7jL#Xtq0n4=E@EzmMRiGH% z$IvZ)b=cB>_Pt##*~mEGl2JhvU^C4p0j4v$tZgo}m<)2$iR=8qke82*cT=e?%uCbr zVpr(7ZCXRBpdee+*`A5U9S6|r{8Djke#&{*wOO-N*odO44OUaTOq)J_tn8PiA{0SW zcEv;@^vVL22aFEs))AjC<)n*Nv^TdqxHiRK>Qn&n{~o_xsYF-YcDQ8alfnVuCm5O+ z+q@t0mw{82lI&2HFcESC-ZYKsMcYhW-reTJa5j>`|Lm~Ao?4QH!7MBSJ~2VQHN(i@ z+>4hIN3Mp1q@>SU<5WFrTZG7o!D9J!^c?ztQ3fo=+cu+S&FX@J9k)@u(rv&zVLU zh((I+a{20$P5c}z(wbCalf3uq6GPYP`z^rTV0_byrWNt(mkZws@9LU$I?vDZ*ReGA zJ?&F0m*SuJ}Ny|GFeS04n zPhm8%E}DrUaqC^mkdx74XY%$M3P3|kkSlKOCd+!-)XKNo*?EatxX8rHxmV2xnx_JE zP){@Vv*Z}Ra{!npHjyl{98hOejXNIw{qR>;!(biT(!!p#t3WP}B_ZjxogJ3&p( zI8cR;?YwXjkh|w>j~oQ64<(0{QNseqPpHZ|E+QE--BosV)=acyblH*!qQKcgidJPlUl`bJy2#7AM9K!i z69R_SPp$unwO-w~Iyo!C-1va!u|X9K_~G1MU?TDp846CSoYFHJLZ|^4;t@kn#1$bD zmtqxV6ID`i;1kq@aHlzPu2u%S&D(SzKpF4ertndnG$fH))Ag84TFW zT)&Ydhia2>BHm;_GwXRw*n+(-TDGQrt6l1PM@}Zp8XfNAJovP zaoh(f@9Jfg4FFXLKG(4oqYNt>LR14e$fBC;y~+Fi%R!R?5zdm_7K>%yO_7`)u4Cd= z`h)j?BeZfr5oOm9G#0YR@1__Juu>R)o+Z9u$s2ra3KNTs*;U}Yf;%$Uj;H)u$nFtf zFGYXnSd)E2A`$t`FBfb@GVksG7psfmM@!>>?}7qTk{VV;=A6f?7V-9K;UQrt{TGew z27lZntC5N8AaKbBqz_G#&MrY8vA!0bYMtsh3ruLKbV7)x?D+1Hb1?)jmfB;5sqdEV zVKw1Fj6lzf=&gO@GUwxanxfZG2i_DW-pQ>y^{dHbfTrrWygKkAB68~0>pCPY;}&vm z!sZ9qc@YzNZKKigysAfLo$Me8JUx;v{KG>~U;3KNE6Nxj$7|&EQ^N4I%w!3hoJb*Z z!B3Z6d$%(BqiHK2-?z?lIjsB4^=|9|ojALaT{Szu$wY={ppW2{>_>~4m)9FFVzgBH zo6lukM_iRVU~1iTA^N0d~M#&Z3v1A2Q(&ZRk8CA``B?x_LGi^{j3$~%3q ztAmuz@AZ;Hv8%<=0EITS!ODKfXT5OptjX@XnePv{d9U>-ch2VP7-!2@-gWX-E{L6A ztxCUJn{Yp4poItUfh;R~qxxvKDwN?=St8?iG|qQg%X3+BwO+Kh*6F9ID+ZqiLYRBB z;%nDIEqoFR8CfsL*rY>P?BnkR>^@Oa^DQqhYJ=udb$bn1$elrEJL0x@H3 zzBriA4*>IRcXmL+X{DI*Ms_fHsH8?YHN3k!FWtM>9Q_PWS+4~j&*;qi*c6D>YeSaON5qi-0q3Q_u8{$w#-i1 zmf?+g&0M|QgwF~LJZ+K!B^o9li+tS{8IR#^eL4e%^*QugMPqa7aM*?fZnV2&s1S|4 zKT0)SHc74>E;6r^cgjgTY4gs@pM;O5)N)y-4&u{649~(4w*}%4Q{EeVlIU~KzD+1( zGKS|jdS!Ao!uh$ab6Z`y9Rkv5+||EOT|etN5KX~qdkSy?(Cku))ibkc&5nWU4N&vO zh8y#O{~A(~GBcQ-0L&G?RXCrCppWoyK@g&BOanQrzGh38l1Zz5oC038XK8;(h%x!O zu*q6pEvH?Y{TC{te?2?v-$JPRzNEwS4|<}_0%u8PZ+j8l?)Gb-%ZfJpN7VI%L3JIN z?6JMwah;r4*16NFxx%&CSE4v$sm>ocOY8Wlby+q41!IIhDA#qh^{@pW!0`rZ={j!lTy>pzy2+Ty7g~Fmu{3YKx#E=bZQHK@uQ4m2srDLHGx;UsAH8SQdcGN@ z;63Z{vt#U)ci*advqgpgUs{50LvYDaW}#rXrD>h3z&Z@@y^pNy;^H;8>)?#^OcCou zw8S!^*s-V`1-7pLak?pA75ZoYrQIB8!}@czwLDH&P+P0n-3~rRnd<+)MtTE`Bfb0^ zqu9J4mi)+iKFO5vy%Za_guf6YBAgylRBor$!vW4_$#%%k3sY5YWx1>)GXnjG_VAa* zspgxk2HAFOK>h*LbU43E1Kop+%x->tE0a?6Xt`4wqZ!|u_IVxTikY_4~2tlmRCFlT@5eh6*;b`}0wU*FDn zPgH)zDp6MbxwNs=rNc|w%t1WNU<5@WYoam0`q;s#@bg|#N$Ii!n6E-;tB2g>Vo*D> zNLUaD<8yEg>eqGGeIF5^SM1N85;4X7%q;IVP{+e8vZb_E5kBo#d5fhA>!!B9yJnHk zlJ!8^a{HRfW@jHLb$Vy`s;%QcNb1JzSPkRb#ihDk==?o=Amofpb099bJwa;%vc1!e z-O#8Gm3qV1LQ@zfp7TD?o3?n8dCs!tU^Y5P7)v$mq12G0jNpp5Cgn2ap~|F;5?P?i z9EIxIv>&eogFSk6Czcs6G7HC`j(#oKLmwK2-@Z_)Saw{ShK0((`P_zEk#N;oX}7sN!D>-`=1kMbkVq9@40w3_g`-$ zw-&%YzHevBlh(r?4>$x@mTf{f0K)HPnrEp;0DkEyq$VgiZeJ_#pUeEL%{b3s)%PfP zczx1|9@HG-cwfMkfBWL=x3C3Tj()rh`#Sml#`DOD%foIoOs>kSB=ZW1kWb6CYAaNnE?am+MRu*R7wp-ur|rMk551j-UJd1vG6oD10E+A}oZ4 zC4-8*#9rvm65`rj?8d2N|5D z`&rHstJ>9p$^_UxDcW%RJ~H{Ao7b-~fUd_LNO@?rGGeUxaNEps07QFA+KhxFD;EJ~ zoCS+JG+SVuhJGtLM{Ojf+(>C5sD(*Ht+-daBf@@WnEH*OjTybI-dO!)_W{A-S}1_l z!Lt(kdE(Em2zwUhMu+NnHIt~~pfNm53T}`-m!W+b2Us36> z08Yd{XR!}wFL;03e=Awc!@mUVionr$^Sy-jGW8kg4-};>>49W4M)yEWR{+_IzTHFj zB@zzPXseY=rQQ~P?Izvcak=--FeEMsXwU!}!~)@5^w^yBr5@8LkT`B-(mBbo}a+))?s> z1RZdksnyQEJ%@UTN)yfifX#e+ujfs{4bVyg7+k!BXg2)J81a1#!T(5**8mQ6Mg3!* zES~3r&bmyk8z-pu9H3?+Tsdhr0K038a#az|n!9&PjOEks%%1=ufttf&XUx*-wibz1 zH4}KKiQGt><|wPCC5pAlASk31a%t)Sz)h@uv$19Z9s6W%Ubneis-4pW>=L?D7w3)g zb5XUDGvEY~3!xoa=SGS6G|#P8jlNDTFFc2!^7}RUVU;vJA%)8p2j9;hmsC9lh;_8j zqY;B2tZ+$hipHomDI_5;8%XS4Xw3l50-*6$m@b7LuI56Mq8Qj`J{fUgQq71C6sWM^ zP0apeiJPAx5}mH;!Y!>!@fz!xro*nvzfa&+@u&OPMTC_Xs}#~Owv#Zq*_7x9+YGN%eP8j|sBdk%b4OdML0~J+tUG*e7BD5co$spQ zbZ~PV-L9l>YUXpPwbcEhn6!lf*pxyw<^Fycy!!`xGH8`JB>cUQ`|{@B-P1nZq)fic zYzO%?r;$R_qJySFUl+U55%C~%n6jXT+}GBY9&wxLztd#qDsl5>n3P#`Ly203M{Hff#pJ;qoC^N8f`Si=?6qobSruik@Z>K0?ZFJ=Df_ zgi@(3jMY3A)A{sXL+xxv0;q;X;?sAp*uH!DY%h{nGHZT_%XRB1iOBbU5%ksI+gVu> z@!3g!1^^w&PPv>*O*pVp@AkIcd}|L66}1kD^}Z7o%mYnVg7JlTJ^#vH7(&f6=K1W! ze+D>~XB5F}37F3RlO&Mztbdu|5i-eU z)?T-NUd&sU^1i;iI*CWJSr(3(RG9!oq~Q>>0g+mL{OwhDI~v!~TcJx}TZU66@7K zlSgm(wb`_`+tpeT=jYTPOHJtdUSC>szXhI0Kq`S)tm&+%XEE?Su(dXzH?s^R;~_jZ zl2CRvJGeX1orp}2+}Og=7d)-=56C_F@^pxc569h@#rGzebWsJw6kboQvIQiYqRa2G ztT5O=Ga*rXIm$lG;(z0}krkIQ!mYzNGMo?OiUN4p4mZh}wkO-Bdy{pE5 zf+q@L5lQi#Q-j7jnKbv0zveXvN+NHu2LeSaHP|&o2Zl+1=GVKTG-bzB+9HDfL}LeX zE`eP|(RB2;U6J5pr#+;)w5-o#L%y&GV7}R>PL2HVhRJOb3j-H=cNMd}++iU&QcTKE>Eh#3I~lnX#8rUUhQEcrKCGO4o*b6bT5ZK|({{{DGApz# zZsBnLG`OXjimUkHICy21lFtwe;^onOukrfI6OdM3L2N)-b~>gBf*$=gUyeR-b)?3% zo!{sPGkV+l&a8m#Q?q=>LeH&J{tr)Y85h<2c5l<&-65FtmVl zNh2UKbeDjXl2StsAr8`wNHZeO{{HUI|9Q`gVK!X*I@h_@u~xJYqcS_g{Ykhz#Vb*h zvM1b3?e}^TVQNP_Z+O4WI~8<(zwrb;ZjX{RZ8QO^FMvm@^k`oozOVNF-+ZHYyvNOw z!R5e0BpfUD-&ZjAnY1)MZAMfYrx^VXb7#qXQ-8~>R+kwCeX-m}DvSF?EzIhL-*26@ z?5}j8vx?9Kt?Oe!Sw?$6j$Gy3n;nU7Dpnf%=?U@@%}qMp54bx&brA+JS{C}~!q^-> z|F*GleLodSEy|!>FtnGbE9@*3y82-~8o**Y|v6S0oKDBdy4IAWh>DSEnDNHYzmqNO=unm8dy$lz5AucDp%gNpXl1#Ud_J40K7MR-j zPEb~;JFF>k#>-00W%A>`ac=)>B2t6FPlm7Rh)Hb4HMsg{48&pZnF^Ea+oa{G;ROdm zXL~ZW%&D3YN_EXlWxJ_2bCmOr8fj_|g}d*Xo>K`dUXO&su87x&w0zdt;$TrajuGEHt5XY5?sBU(%bv{sg3fluX9}C&oBvkOF`F9ipdqB1bqOTH9s+?Us{ z>AeM%sQT@4=BtQDONbK6DXHUsnn6of8q8Mi>TIL#K8|{Ri+(;hvN_cUz96QIPo@wVP5V$h8hCqwjf`9rEI~P={FnO_y(pB+ujuA--@C)nMR|EFX1`gyo>_1hFVFt4iK!)!ks7U~WbBHKU?(I5tDY+WQQO>g zoCM7$@8yQ9l}d{r?g*Zwy!Bu|i>%Wv!^@*uls_fLeqwgO*>)-uDt5)6iz|TZV3a*Q z^bD|R026tmDi~9QnG2^d`A8c_Ct#USxx*T1K(e)!d&h;(TT48_eO1hp(m<+Amt4=S zjXP*=I8So0;$`cmp@{2Sz3Wd*mRhUu<8Gs$-LXv}eTzgHkJVCQQ{yy`@qT5Sr`7}(of^Z#MK9K1ntV57>{wXX0T zL#i#ES6k9BM{%8$CX4etNPa1&qnb(Cx3n&~e7CxD5R=Mf9=0u*El7w?Y7k!Ql21V$o{hCWhgNAwZ)I|}BVcB#gm3owiS z$xeb@A{ ztOY#@J&XtPTweXdOj78dRMd!@0X9q@jRG+b7&-B7f-L!U}~Td?>8% zK?0>h?I_EYOKwFq&+d&8M5kcUU9>SY`tnmCd#I&n zpzL#S^}2x4WLNxOF#;P=tGqzW8SyJ4tmmcWI;CjEfu$cbS*msiYBwb{Ti@vCfA8NN z6z*qm^=-Q~Lcz2+UhSLNA)X(@f)x4#*``+W8Z-k3c5po7RT#FJ* z@8iXaI4gH|EQT0Kv}Z{OHrA;Pkix$&PB+praR9HUhF_gZ@C|m)53c46G)u&y4 zyC|`6y-ea~CpX2bQ~tZx@(AqrC_O+pQ-#Ej)Fz+8(MTHoVugga1-_bn{Ac;M&Q4qn zXnD71&m;eoKA-8lAO0h;^vCLLGr#fvL->F!+&_q}~=jsHT<$U1%qKBVCZUh?N43`2EB(ceZ3;&F@K9>C!eaF`_^`f2hj!^MKu#Z2^MFob?# zr9(vOy&=!kGNLVV<1a(W7U|Fr+~xtb#JDL1EH=Oad%4o?acdZ2-FA}17a)8u(I0zM z-G{ucl6b%K>;pCFm?>Pe?UzptY<81awZuoa95Ut>b+?7S|3}`IFSq@Fqv(IUB>ztH z>3j+%84pd@Z!f%4V0!+OH}Z2i{*3>uAC1DBv)WT2aE3$Qd?+3`WEeoDAst<~j-yIfrYsJAg0a7Wb|6*uieaf|Cq@j=2d922F%USM6N;PTfKV;y>eN9d?n4Xai^s zg;PMV)r9XUF78}KB143vU7vG+P3YV4hT7cH{}cy z|Dzju2+rQ_Bg#co|04zfAxUl#I^y2iud?gRNfOjVS*OeWz3W;sdr93jp6W(DlReRA z7Yi@n$zB~n+o`I{aeg@1&yP*mD?bdkV;*?M6fU<(0mTwI{^H)T}iZ4m0sYyLo*N2&%-NMQ1i#Chl%QcaR_c% zqx4Ea7>AEd8}>!ka^dbPB?!XCOsL zGoOM*)T1aaLMe-G#$RkxMQ+e8aiw0}fDV0q_WEy&i#=ZB>GF=ib-fpJx-HQcwvnaU z0&tAMmU6ZDgsfPw>)^g_c|M`SX;3$hA5Q6`I2jp7R#QeA5(xFn=5<2{GAm$yHZnO98WYa_eb;4;(qS3Pc*Zs+IiFWXlHXm3N5JUBru zxFdyL@zyGj!J{^%=3B^4O<1vI5>eTkBl)4qnT>d-M%afqId!8^U`+z#Rv`kF6PUs! zu}vDOfxRL19D7-I$r)0#x6TE*;jfOl^Cz8*a2I{+xTh-8I>+VD?X_l7G`FPf%CHSM zIU=Z|JEQ8xW>!iQ-W&2S%s(5*{s{5>CM=7L`AV@$-YpoL z(6aovMKBF`Nj}IFJC3umrH-(pb8fwewcJA{cbBa_3UcwqZq{iQh z|KL=KhEAgdEbsJ^XXwaVJH-ytVe!jX19;5~z4*fh8tj+^2WjvR2t*2$Vz>XMN!HSa z`G!HPZa%K0MoOpB4|M&k;urLZ3?4(G2&72x4uaNOE&UoG(u zKgdr64t;4#u3MJm31_UQk2(9cbOz; zZF+ioV`l?>?~P3c3Q26DzjfwVDN!7GO^x%bXZM5G=52r(1uNqGHdKf%rxSen6B)s>cgx0u!W@_M&fQ$t|U zzKvdo`+S@X14g@z5FOpJ&mxlL>brSAp7Qcazy<-Z!fHM2(d$tEPHFiKH&VWCjq}fV z?jCjEk?O?xY&)4@;m8t`>Q1M{+z;I15ad*=%flrM@7Y`TO&?H!P@b0v=Ayom>;`3FHs( zdNfhIwE4Ag7EPVfPVSrSPs3Bl3?KMW(eCqsT2&2qvgH$TuSef_C(_sl)n*^m`5QCu zule>!MYyC*`Q56lWS0U!L&y6>3ki zk*zC6!BN2~#Zht?oaEI&Swco>xs9ew#p5g@hhvndiMVL$UCYtT)B;XUZomQ!XiWu8 zDWnH0hVz%ep44c>wJg0Ch`WTn@jjr@itKgpH2Rnb8Hp8rL=k~09CzXt_6&;iCO+Nl3c*MUW*4l%?14DanW4S-&bZ09m9P4M z&EM{BMPThD|1WdVrY`ktDPE+f68saLIh*oSL4P zB+a+Y!Qvt7>SR2`Ud<+KVNG#DcI{xGDt`O%zMp;C{#zc+iD%r9pJ znrdH%EI0y*&|sR|z<8N}3--DFVig`R4h|{2mrr2|dgM$HdmPlQ9GfcA6AAJxi|LAp ztSxeDUf`Vn!Xoe*`cY7H!=GRGOxo-=E!K4LO5q`t)pGSTfU%`ZFp?2xUUau$8Wu3K z+lN^h(!KW+e8*_BE`;1APst$rAjyu_gTmkAk>q1VqW&8 zD9XE1V6B&#APaIwRWLu;sciG2{t_%!S~de^cauRX$@_BkrT8INe!p=Ar^KFG9}yb| zt_cRn2|rq-wIKCNqqqZh`~}C~@I*bT+Ne(;LpxLm3>(St9v=f@NOvFNR(6q}C84uL zL~f5Vp{suTf4qg`&~FUA)*}8-*?N9rd%0ia2QK_wE%7kU@fID7@?i0--rw16l~`GD z(00m|Sf^r{6;($sndR2BCoz!ao~+hCcuoAcF|DxdxNBaviI*W=llLo}gg%p>bPBum zu3U$%a7_Fv9pI#{YUvbNq9O(NAp;e>v;$xwS&YL!ou{Ncm6v>vNRO;Un&cKc$v$x?K90f1QZ4p(m^rTgvdE7Ild$|}v^q#Ax zm^z@&x_%(K%1s8!-9`;^VL_ivO7l}w`6i0diK)kD7|7iP zyU&V7y|Jv28EvnZyG*m%x4c-AR|G#nw7rbuhr|U5AbOVa_&`Be)9Rv#zf)o%xI&#i zSj+Ag5KpotgEIt$z3~-B%fjxDBbvdE;T2YBkz(lvymXH_2YGnOm$vQz-wS_5Nb23O zsu*blQ(%Z%(UZm%V+`Ms^i*5@V1Ht3+=SkR zuU>k!TG21fJGyRvGcLm~MOznMhxDoikeY zi$FOJj9o#TXej*G`7J=X%^BZLWk4jLl3GX+#Wtctn_YuFh7HcmI{95=J-J-UPSTS{ zmxq^kyI|GpdF!iM0Rv;*Kts0&g8br4o^ zL!U~6R?W6e9yN?9DIQffq2qpT4kcF2>-*4R^zq!kIX;)@F2wXiJh%tIIuvA#Mj= ztr;Pg%lzSVk<3f*jU;WkvzU?23b+$#D?Te=K5a5x{HEY^%xj z(5~U9EU5m3)8Kbif0JlghPDKW?1gW=de~vz?vJIm{G-|7dHQ)dqwBYvr7hehCKfH+ zoJH(6YrWapj@(B_whRykF%Oq6_WAMdqeu!3Mo8Ou51DN!a=z z-?lz%`bfUL*n|0-%OM1Y3)MMOtSsf-!$1!San4As+7T|J#Jcc19hx)4fxG(I>nwnb ze){J^g{~P4h_msQh6eY~pVb5a{>sd@TebDWI%UtRdA<8~1d@y^rn|-6`y78`${S{cn;dOwx%Aw(=zeTwEvNCs*`k z5#m;CRjlxHI0ZT>VNSHu{ZdT$ZusduKI8Yt`-kH{@C&_Lj4&Q{u;fT>u7N-ERnIQn z&96gLh+_6c9+`iox${ObhAQYPh7`!-e8A+94X|O3g9S>5)*T+#Rm3p%@Ho6Z)2h2j zpf&1$>O@H_X63s)7^_mUAn*6H&;CQpZn0j)6pDKzMZz;h!jcJWK~eCy;hCmr`*_t) zAdnZ-#KnRrgR#1>rDt~uF%q{Q>kvou=$Zwr5GFo%TLgBf)q>s4lzKvcy2l; zGO5{+e3FMf84BTez3Fk$LcUb_^9#8x_j|UPU|V1}0wh(UaNZqsy;OTXNu_)rLSMdk z*{eIEc^x|v`NzVeRBJ{gQd}ZJRk-Gm(P4fys0#N9A@72`q%wwJKOWZAPRO1hv8X_| zLB9=j=$QiF=jMy+7MjpRx8mX1i`Y=h91+@<*P=4UO>V0+m#O7cZbRSZ_FLoW!+Ktp z*B`3OJa9d*dNVvl?O4~Eg946sh!jbV*=@7Sfm5$WlXw|Y%<;I*3lPRhgG*#7Rqjv3 zm&}qhavYO4kC?)Q@t{` zviszo5BmJ|^Yo$`QJkefQFIixlhc#57O9z>JM9cp)WUSopOJ3y`za zs4)YxkSQNDGRfTVYm0DN5p8$7=MTxub6v^(2h9#;qi|>Z&Jl&@9mWPJXuv>tcX;e? zs9rs9UJ;!A$WOE@e_YeENYuE@;`USKe6N>LJBh?{>445( zwv3;T=TUEy;~#2f)2A6O9a|6EU~Z{^I(jc$8b{1vW_k;F6k{N%3LnIBkG(i5X%q3L zaL!HQXm5)O`P)T?d}pQRwf!sqv-T@6te#NtCMHM5xaFNm)fVyzbM)WZlJqnma zw&{1eN!%+O!@S)TEn&9#Z+IMhn%)|1`!fXsnN`5HI5as8?3CJ_wcd|5zGM9ZO%Lx* z{ah$o{7U62q-|&?jxN(GxPpq=&h|eI#;B-eRA71E9BZM|aghR_CW1oaGcn?CC}bOO zh%;MQ6D?S6Axr7fhvp3cO8o}3l$749WOy(A`KnrG76+=cTXh@)uyehDE&=aSOhs z@)W}|;(Az245tFCw@%|8mo(S!`L1&tf^Y+vyY#2^JUmaX3&0wB|IZ76uKkh)cLyFX zd3~8!B;=DeYc_$vS6Nvdu&t!-+*K*?$ZaK_1K(si`^yGou_wl|>{^K2v&M_F*D?)( zoz`2vGHT=r4u63GIU(#b3rpRY*!=6kn6i^~tSG>H63o!lJeGx1+HmHDr!2DP?;E>FQ9(CG*!d(0B{uAR6r<(|&`bdf zroI& zBGIsOmhWGyhV7G_V@e<0o}Zsrc;^%B&yw&|9ynWsJ*{&FKG&P5Bx;LIPBcFw55U1b z8Ikz|!lN2^W?N=*&4yHruBQ7)(#u6^oi|ciawWQbd%C(KoPD0*k)7X`z%5J5jd)Lb z@*S86(7jhqs&9|s6UM0mUh$Mi7U_zBIUGus?@R^z8r=0ID)=P#Ts=o1U@b8*QjD)z z6M0~YFIM;r(%>fDmK8P!`r$1N9CR<e*9?KEWnx9QA29laeSRcj&>d!zvQzI4(P3k)V!m{s#yvdmcj(^*OOux8V=h;hFV( z$XA_^3-vH`{<#nTSohC#@5G%%0h7uZhCMX6Xl&;+OYP$F2vF9q!_!eP5PG3DyUYrN^ zfeWS)Z^Y@D5zg@iDJ_IniNeSa^W`e1UjgD%>F>Zwn*r8?2D6c1b*)C%eshV^OA+4; zFX&1@|802lZy>!5xeVMlEB>Q1>M{fvLrgDthrgW!bq`gyt6T%}`|^u!An7Caq|#(7 zjpA0gG}4=q+EHSjt53mNmoFlP6fCU#`X>W`H7NW|JLj0ht+MD?4U*`BWsmk*^e>Su z{Ex%oI&N5Cv40;{to!Gq_HlO&ecL~w@L22dSb+9Z_^ZCBAyvTs@#(ANfdpDkrRfcz z7)k9%jNSA-FQ3#h&yQV`+EJ}4Wr^VGtCImb0|3PI zU1MqXN?S*!^VKq1mcmB3J@oBxrM8Xx`WwzqJ`%ARbQ5`0P7mi;TFT~cVR#i(w#=6z;NgbKv7i4ucaQMpauKw}`rj!jS_EPWFr01=Yq0K)FrFmi3 za{y*_4zYNJZ3 zGKp^{-2PJ4#xXap#&e4}2G=flnVQY%8Nz(=b<^MGdtll&B@pkk?XP3woyhH|C zrI==KbHu(h%{%yCfy<7^LHxU|lBa0h*tLZH()YfO>=~-Tuj&ba3X^jb>MXdaOW z!#>&MNfo{S$E*4PBqs(w(oBq#a3j3N zB@bb1i(C^E-cOdR7DHl1yf)uVHj|cIceeH9Dh31rJqjB3ow|f8Vux&}rFn-(;tQw< z;LR4+*RZ@#o5|TWmzbVNbX1L>hSz&q|0Gc*Ae#ezd>wj#t9nxPKi2$|oX-6z%oTGu zrDlk3W~VoVd#=-|+=?W;Me5z?smw2&!wIM@o@Gr{!r$5ED(yKbf2W>d8A>O@!2239 zf`Iqrq2d4#{IJqdP8v@1WV67i=z_w4{8;XAbZTWdL#WP_9c1zrHf6A=^WATL;7Lng zYke80cp(7pm3LI$8@|FrWsSg8LcOVrPQ@t*7;oai%QwUx77iu`@~!D1QfPLSBsu7D z!Og^DBVFPu{c$F@THrS`!gdU-c9XzgWfDpNGM^{9WnI=HdPJ?-M-%i6w_LM}TDNka z)S7IT-I|@j7FYF5P{=pfZi$bJBE8ZK4>k9n!yg9^@R)OX zCJ!I^pCSJpM#KvoT)o?t4`6AW91y8kozUa{u_ymb^sAvftz4NWG1c!D3QRkzAcbok zsgx(1VxZ5vSf?f}&e?1c37B)GQ{~i3BtYP?{pgw6m&zmHSepM0`{=7PTp+ z%VnF7(e)B_$K9v(QwnPIIo&Ihgc6YXaZz3)$Yd}4Y@^RZ!Q=Y@{{O<_m(1zQ0Y=&D zMDT{w)|V%lCi89ePI~?&{P_pfE5DDRjf8)>U>yXuCH#YQae~Nh z`}A444rKHuK=w$ibXm&Mnq;r|Qp~Sw5EJTrmU}-_5wMl3D3j5YRhEN?wTdGN#6&et zQ1jN*@kbD`{P#x529tb@8y8vSDXs^m41&G%Myw|yIyf^bEDDRG+sy~s+$k#xf z2i}j`8DhU#bn^^BQ2?QMx-cJs2QzsR8~YCRbkI|_7V(tU_=Nbse)0Z_8amr!Kg4IEu^CE};5mJJw_uk zQFx8^3as6=6?kB`G`HWkJ9BgjGu&Cj&%dMRI%(n}lhM3%K=#~tiXM)59wKPqvr)W% z3vNG9KCx=Pd9GLDvLD=&FnSu|_%LVzjB44=hZDJ{t^7nlO8MtDD9}b;Viia^D!7II zlmMKn!y&%R_2eQpcW4# zBbKLpVBwaX5860!sMA-AQ_QV)xu!>NiV1Xkrf-WrMO0<^^lo~JFVMtPoDAer1d$@y24on z6++V`NYbrPUt;8-`C+?gJ(CA}waog8h@4buMc1j{ zI$;_j?C|P?SDzw}-H}lGa#Zkzfil7xGjr!YEL;sCP2;pDU63rrP^=4=>*z|RXlydv zo#FYKDM3J_)Uc2BjM9Rj?#G?})=o5tMnd%&FF5CV5>p15t11s-4T%yfmZXnWNyLBm zS8hE%Vi>-{!oi`1WMGwf6u5!`=?nk^Ca04HRM> zUmC!tIY%95KjtvSlj81f0jS=?`Cg0u%x1DFBP_Bm+iEy8{2R+V!YOv<*eO}8QK$sv zBy=HY@t}Br5D-dMT5MWx1u!JstMU-dLaVibA_J=o95?9a(_6$;#9ZD5kC-dE1hF$D zCg?0==Rc5Owk7m3Vw1gL;l~NEHs64MR@>?)=og(oKwXd(p?~h+c;HruJc?SYDAcKX zcX*xf(T!D`wAqF;#W#oF0stKMyevBYB1u3LP&J6Q9@z~6(o5&JLe3U0r3MfeNDf3M z1^4?pu%(*B2;DTNFbB%Hgh?48e>;-Z9}hB(An2?F><>G=$^s6H^Bz)ii{8mf%(r?b zH9brdn)3nKkjhJ^`hI;5!r!_azXiC8ENLyi*dHk5P+cOv3dW}iziIN-Byo3+wLe;E zynI!A!G&~y#Qt?U>~!<{xAB-yaSDv0RG$l4<$<<4v+meYa0oYCDB$lJSYjR!6TCZ#+Tb-)i4G zyflM9i@q;msi^ushh+vPUwHkRhO3L7-NW8) z>x*ydVbj#e{DT8j5mh}Xp*RM}L__$5k_X~q`#YSg>~r45n~nWE`oKvnFhU4F{0Rtw z8kdMgUuVGg)cl+pLwRkLH=*1rH2$w#ZT^+YpoZ$TAx*ks3LhC!AjuE>zBny$N~O%p z607UqwO@>Mt#(nRm#ND?suX6fM@uzGJl!1R6AY_R+Q)wb zq$aOZp8|erNyGZGGWYfp(&9Gt;ul(1b;MGbLnBv0U9@>8JhhR(iw=JV)I1XJgEAFr z%P|;Yw@sVtMWh-{qOWPA1jlC|gW%YTV6=@bDF5X=JX5a%=T0=7Yg7+7XNQ8KbD0f} z0cohF1Wo$vI^pwg=dXM1vD#inE{kTFm3!k)H(yWxGBD!0sd9}ZlzbG5s{qBQMO5v? zC6^L*JAA*bvWVzqyt62R9Ps(aD|sYHldTv^FB$|Wlr8&MB4Y-V%)LMbKc8EfnD`7b z61^^#n3diIBJZo~;K8(Lk)0Ndsd`?MaZ>=ufl(Z{hfR&&Oi!o|U~_P)|L8 zR>3SuNAQmt=d9<-Q%`DYxiFmxWGo(A*#)ESQ6B7zYqXFbU($WIm9(3`j1pfQU zU+tw0(^9B&pmDMOFcp4waC8Ca7-lk}a%KH&*<7Q~ZfSY&wLSatm~iSmw`l;RKzi1k zSYy+o+CP_1gm&TMxo&O+4)wdI4txoJS8{t@AD4sLTN2Mjcw5V+9yYfwi;4r}M|JHM z@#(;sqVH}Xt4hI(egT#<np$&_w4Zk-MmV&ikTn@xqHRebN9Z0Yku= zd@z4!!hC8C8tH4!La!v3q5;EXDi&vQ5PwHv-Y1$`J%ZhK+Pf z$qHh!jw~tA^(V68s&L5nyogU`4(H0Vc#hJ1?GLGvpZG4-x%8)@>}0Oz5yKrxANdo6 zb{(_;n0Gt=Enh-<(}+?ucQoR<@UQ%NLv{6H=gu^McW4&EXS`;R2k18oietEz3v8Ud zoV#&sAuCG($tLkvQ9fy}$8tT`n~SbNjXr}h$)%rGQ>HjocCd4*jG^Fef{aRD~r+x;&6aG|jFOC5G55Ya( zfr_2cS_xu%m?U(dpCO%`@Lc?Q?W5 z(&p?<{~6~@Nl^()krWELO|3+MD0jCb3Jwz_m@qn#i3zSNy+!~Y(49090Fffl7pkN|&e2?X4ffY(a>mVL%fCMBO?l77wz6hb2nD z#VK2MKId{66FvCK;~Ss+yqvsNBk$F@{hp{#>{Th8c@U5pd!&-5o(;9jwy)L!yJS$j z(%Ad3C!DU+72>hIjE&`?E3T8IMYXqd6vhDy`g4KY8Wom@ik}97;4;8_Pk3+BfK+Us zVu0g1s}|A&Ia0v#+AL(%t)6I?viyzjO|;Cs*jp@$e+b5X>%&ieY5~k#q@WyGX+|z- zuD%PKlXt<7*22EV1hqR11ofDsfF$>77h$@0Br)o=Rrqx?Nqe~TLyE-rX{r168fT2@ zD2ei7LEiWna`h?vd-EH-m+`SwgHgi@^Rm(2H-O3UqNlc63xIa!7^$fthhO_%w$j}O zJ_34)k(D;kr8$8OqYrsXY>ZJfQ>}!MSU{pWG>;Sa24GeIzxEWvkSMJ*Wm#()FG^Yx5PoBaIJH)7mNy9u_2%hB^&$c&jDrd< z1AWDX5cB#_a@{-m8qzPHVYoK`e6F59ETq*u7D~#Y9o_ z?fnezKG24jwmGYX$zQvn4AfR;8IJ9)d3f!$hiYB*4~(fX$zMEcyBzq0D2`Vn>*tJu z$Pd2*MQX|ZcVn>p{%a{)p4S>it18NOIR&C?Iw(-}cpgC7B}?xa#fj)D-CAC8dUw(B zXjVCyWgjN{bB+&7!_u{))CVoY%yQ6vYxTc9u0VoA5WXh_Sg@V+6{l^_3D^5S&f&5H z>?fArM(O`ocJ#se2wyl_AR;WvLzq2vr2Qp@pK3aa>XS%MBCQ}CLkfi=VQ>P47(7xW zl9^bsW=Ml8cpe(n-7}f@)$6n;mdnwM=@kS0%s^@#Sqj2ebuEK_48-R8tCnaap9zm* z^-{YFHoS|fWQ_A3jU6%9c@a*}oS*N5ji*zIr}ToxYn+PR(d0UN5z7v|Id*d>o4Xn- z^rsWcnWN%o18{pgZi#nP8B0oS!F<0MK;u8^GKIO~@+(4w5kQ8S!7;JQaH2w|xv^#{ zBlX4fRYN*H)?iIEH-fDjkW)~07;XlYCFWh`!GoC)z4ifyibDf=>DB3j`0uo#ll~?-s&yK)I$0-F(&76$6%$%<5juo{mak z;K_z*EGaKHxLaPBFbce2ZOg7Yc&bBG14qryNCLD8;Sit0zUjZNe%}VuxcuO6r{S38 zEAKgrnVodOj577*EHUvZloT1Qzd4nP#c>I>e&j&06eteDauPZZ;i}3QjK366Ad_Jrgq%{!RL`(6l6Dt)2Do zN!{IfUVzT0*N=j#eB@`UIF9kCurac&s?poh*6mEEqh@~APfP?E;Tck$**7DLk39%@ z<=X-(y40AB;&jv=U*_CTNUZPv?#w!r?=;d4p(j(lx6is$Py*2VFPO7QS?0@I_cGM4 zY>+&ODu?N8bUk4P;_kYAgvl0)gIk}+YHN=rU>IKNC{>+KG>OwfrzJ7AfI_JQb%*KvD{ zx#nr9pS_O1L&0c{JQ{%v*T<;cfBsoNZ=;%?9T`neoH4T4VTmpwOZ#~C@i$Flu{+O?i(<$3)Ynyvuzc2jrk^Nhh7Jo@ck3=BCCk&VVr9dI7->%*tQbImhGKIg znNdyvGpK3mq^ft-3z@psYwoj8G8jcfXD{YjlmfoqS7 zgi2@|kcX91vi@WPE-d+~SzUv?X-*h1sD*1phS$EpuVzJU7Vay7F*_SMf*&B=xzn-P z&J<@1zQF37&pNl&oPAk)Nas=ksPEPBlIWoy{|Yin-^<*XZ?E zR(1TC2byp=SnETd;eq}$Eh0k_U@z&~F1J{WUno>=-2Cl{)Tf(o(ov(Xp0apoj8ltO zDZUXS43_hS-#;7_BVNhUjxGv0KRDTz96eSO3n8>q1VldNUGb@#rCK;K$SV!uk-PG( zWKR@r-1W2*ifn6u&Js%T+rA5lYgCIAC0%gPtNxOUZ*KKpM2onl|5$fgyK3Q zH@u!-)_{}-R8=?UP<&Z?fP1es2Wq{ffoYFOR-G8n?}0@>37q+i-Fc=P`6m@%!W~`_+(a z1i%sl->bp*zHR0yqe^q#l!XK|91i?Mdl>IQ)wgWu@Lev)KesPpqcq_`Md)&>h zC|TEGh!)i+G_$In#U^v(E}Z`VX!^>qD7&t0TDp-g1!NGAmJkF+kd9$M8YHEpLFq0j zrMqJ&$w5F$q`PB;L8ViqLEzi>^By1n_yu##zSdg%TxV>W%GR%4GZR4)mF@)<*xuTg zweGfdORmGaWLXN|Ujq^B$>~R~{>&;)6)&1Rl6Emo*ay!AoIk|{h3DyWsNcF_!Q~yh z6EoYZ$wgtn5PWw-sFi?p!1Sb*%BgT9<%b5voYvrJV7V4XW z8l}>=F>a5?{EVFT;E5VJu)yt4?DIiR?AK=3#!$RJeHcwDz|R8Fml#28;UEb>a~NZtQbf`oTfK|8l~((}cX zg5_0ejAeez8}{pHcUb$@%*<35EU}2)?BMvba_AaI7PDoEU1WxjMGmk3P0;3syu;zM zpyS#TmB@zL`Sn?O5b*qsEfEioHJBFZ{gH$d%TWSPrra(IlC~>v><%lnyJ}ryI!q5( zQdvI?3i(}@cd29j7NXFN*(caDaUvnSCTdQeA9V0rA7x+R55QLH8=`{@-#>#Ftp^YB zI{3I3D^L7`h#$ap=jnbe48iKzwS1Jw!)~&7Csl6&Ym7Pm)Q7oO{F3_5t)mi+zz|8g zFu1g6f6KL$jmSMSLY`lwJQAT|-^5fh8N9wBg~b!$w*OXRglIH7V8Q)ZJJijKH~?bd zNI%IWG7o}5rce9CJ?bMN*SZA3S-Yp1H@s1p@r2T@s;EwcE|+YGb`F&|wjlZgV(=`y z7=J34fAcGsl7MI52aIG>FWGNh^MZgIduO!-)>if?F`hS+ghP)`M8lIn*$Pb;g{)v- z;X~PKaULqyhx;{*8G~5aGwC*ebD|j?p9qtEkcc8S{loWeCO>(&Nt1t2?4hq};@UK` z!`bEW<&S%4wZp%=+4hBxB@h8K>YB5=t#+BYl%O z7-L`a*hwLDPu9`uDl5I-p+87=C+J-pwV6hJD;*zc+SDW&+uS_f?)^ky$Ec5`SD7No zk|2GJ>3OLc!Rsk7`g{FrJ5CT@&XU&0#qgVe=R_&woGMi0d|AKAYs$+n$ZBpU1wm+5 z#jd2S9vco5C4^qAwLF8A8?H0I7=Ch-ixqXVnw;{f_opLhGxqJKmEbd2q6A~@O5jKg z{+WRV_YqIU(k`$Wf4J~s6_dRIeAhbnN)+XYRWew+=3976Og#qhPW*F|J!jo9aj%nQ z|BN=$CL(UF$g{cdw|!-vj3V@Qo?6B!F%PY(n$hx`KTY3D6_P)p!UzseHXvAZmh@(x zC65sEPRlm{XC&Za?(1dHoC>0TaK^U;A3xKleAoV`8|#L5*-SusmQPKnFJzEh&hg3Ir!v7ViUBXj8doAluuwtVXv2hQFF?tOk@B%E>3R>6Tj8@&^jY^dSm1`XqXet|Tb&F7PI<81YzU z+v4m`c+Vercj|LmV#zL7{|QR~vfB0(xU)c2h-KE9Yt8b;H)ZgXEwT%6ce@Q$BvibR zY6e~#5ysO(?_&2^j*UTuIYPErCaO5}#!%gxvoFj%I@-BHM%8 zYl1r&=Kn(ay8!z`E2DI)%Crub$2_S~f{HlT@h~35JDLZgmY-kREcNHJJ+1^H3Y-;D zN3bOsC4X;?Winc;yPtx@QP~mG3oXGP0j8a>BiPM0<{I)6D(8m4-(x)CpJa#Z2RPET zjsvdO=z{VQi#&B6WOC9-DB|0T&eY>G@CNaI0QIq*c44P168=pUW7)kl#Ch3sJD(|T z3|hM*?(N-XLXq>l53B6z(k~!SXTNfdE`<8S=&TorBRTgZV5cyCRZXaUOG&mC6Qg-A zW{^EEt({Hhr4Lv+ZN-B4+C7wI_zy;T>yuJkYidC*tF(Rw zua+H7ooo}-zTWcN=Hf;Zl%G5zL)5Ru_{u!qqj&)RP;2wL%{}{Pi^8|QuJhKYH*)GJ z=ZdRS-gnBnQ_!|A1)^k43%2Ogz|VntcY#B+U($XbEur^bIDfA$j>gOpn3YzZ{47WnY+7Bz zOTh?Jk>tqohQu_}(4a#Kq?P8ZCRrt4Wj$Dss`I78oA+sFp)q&NEPYa>z4Mt)H$L<% zptJJSM|$y=)}EK**?*M|7^H7jLrUOiQio;~=cTm$h#L+Lj4oH`VYUV7mNhY#l3rL< z%l9u*p2e|8hsVj}@QN z8=U*JEq{t${ac`%MR#Ex7%#d2FEeC`Lb@ z2mra&n5^{AINr-*$?@0?U#7|*ay|l1Vl4E$tuBZjO4(%>J4LGGmM9^Nk-R8OhJ{^ zu}I?k0dTE!jqM^Y)*{z@zq}xkQZJU5={LCK8a%=(XIZjGz&r0bsVy*S8Di;#x`k+W zjhzg_P;+}zDK$FZ&U4aY&#$XnYj)2SLIU`+2vQl5_yjKG?06rcVF?->2Zo z7|VvnK!8TI!GTy*taH`tetLrI!I>UO8kjYn&WXaKp68-|LJ6w9pY|vQ{Zw$548!-d zapFu8kre?ZW+rj^5#jq^5gMmYROT-J1bdW&TAAms*xcov_x;s(^rv7Z?%z`yi8<6N zlq@&GC>_aZNdd)ZD>d!;`=363{q^2TWLf9?rXFKXsXmB}8+9#VsiaC@BWc93IPc*XlI83?w- ze_!o-l7W*!nmR_{kYJNdg>uX0O0A$@LHH*8{ptH?t!Dt%cTEaovAt?rxG&JQe2^fE zcTT>usS4vYdTc)Y5u(C z;zmEMsu!Vr@P6t;NO$I8E5F_Di=-}ozIeBjYR!3{(kkdwUM8zB-*|2-WGC;S$m`bb!0xjuy7QVL{_>K;fB2yM-VhK{tUb>?;6509_*(02{cc zQ2|e_7ZqZJ6X&2R5a8T>jbR32r#^Q$|9Ny$m&0f|m$dggmk7rWi0I)0Q+Uhq*3t!B zxr8IH7=PCjuHQx`zR-_mbE!A1j>XKMwV%zSP`PRL0=XY%k8`jD@K8uyAVRn+14+$S zf}A?JjCXJ7WX&`@1wI|k1(HwlwG6nd>7YYlUOohq>^r6Y0ZF`}hyyvvF|pf z`9kh{Dt-4>%9vdDXiNN+_+5#pV0vUf3J(7s653V_%^-d=7%%EWAz5B&$qL5NEw1O9 zImyFyA9+^Tf7#UV6P7!~FE;cwl4IAu1)T#7@_DPi7QFJk{iXJGvm&F7S0-*+R@yc51IqUw9XpMP z`b{96Sh*jdN>G*p7_VI}#4%;g$2TKnl?e(pNAArVPi-c`?)If>WHG89GySRAO@>vN zxz~Ir1Dj=7+p-38!pq%FK+e#%J!fL;X|r+_8p8<@fU;$cu3Ugo^+bNW;HN09VkAM- zRZI5vZU5~#{Y|1rw0B8FRT-f~vB^+u!9F=GC|sGEd*hmhMpZtVR5a*4%-)xH+NXK8 zNY$t6M^WT=a!!YrXVRxFPtv-d>cKzWe^IBA0i0z(XFtHzuAMl(hgp=ZQBAFKb$Pj+ z2f{8Ca4BFxZ(h9x*M&1vQNb$N6D9wo^0Q)O1ovsXyl zjY}V4hS`;Vd)iX)`4WuC#nknzyT2a#;2FIf2w1Z>z_(Bsl zzz7HQn%MC@YmLvE$DM}{H{PY;+*f(BUK?zc3$urBVs9JwQ~-g2e+%81r&4qku{mX9 z?t^Z@CBz(R{yUar!P~|X?iE%TZR}|5!-e1WbtyW2vak=O<+}z9R(TdB0S$OG!;y#x zLz@6cwoI14%qY*bPN$&gBvoBeywaPPCA<0Cvcr$1#9qAml`>5smcn>~eBJrY8HQGI zxSPgr{#b*-`j>)4Ts`HRXgcSXg4E| zlm&Ce=aVfbmh_QBu$}ocmlSp-sn;R=4IBdq#nxxIxTA`KYTD3E%dkNtF1yQY zAeqFnWe~+2(up!OzY$xh?AN8FU{DJr&f`V6N-@!^3+$^J8Cvt91q=msNq#6Avs5~4 zcRQ;&z)V8qL0`~O(jU85;j$V3Vj-c-gl{%wekX-AQNuzn13G87J%%z%;%HcnKF{A= zq^(|Tgi&`7M%R_#(hNfpCC+=g{5t`5URgv7W_6q~c{tgL$M*gvP71~zzGdOi*ab>J0+J5p3E&Y$uS4=IPOpo}e z91NN9@;~S7U|oMWZ2x;;tbIfc4wwT9`AYN#4yExt&33G68SjiC zED7kH09Q8u`r*EA7M{iKR41P@`<7QiGVD;`r|w(#6_VSdC;8AH3&AeX&-5^<2BkTs z^ImQF<2ujaxzEHY5MaTt(T=E`HKcaf}R38fcW$CkzfQ0UcD8K$e3oUsD zO$?Fj=yDxprjDjyEh@$ptkV?IFYwcxlvf$M0i$iOaJ4YPIB{2w$2Se@OAkV79*_EL`s+3#YZO)56>4Kp|2#>nA@1U8+g) z+V3Cn+>AS3=i%^{=@YIloJk0F8(?|2U~365$p=PK(&inR5rQe1bpUK%7IVUP;IBq- zzi0HF2yM1;m~LPp0W5q#udcd<$YsRAw|j-OVtbI9iTG+Yu=2`3``CUe{DGFM)&|230TG#7b3acRrvX`%lU%=)k0Yr|FR_c;9;dbO_qEJ0~gMqRulRG|2tpD6AVhVS|U`Aay6 z4sy4;lXqY=F(0dYebnL9{!X<}N%z9i&a+5dsU+Bqfzj{GXWCYc-jO-#X`cn}@?)_e z{3g~VBYvf$I&v*%WTP8t^!#oKxIie}lpee}BEXD}vBsDzNraK@F{wZXHHJGjRpM;D zA~3>jzTdY*wFW$Jkp(%!{Z_l2`@Rr@_#a*9rW6wA67yuZ2B}@%H?pn3jmk!cR>5*U zL45zqKfj=H@9OuEf-$<7Hc2DvS|cA39q7YBoO-qW6oH^^hkjp(!9l-z_WMX**qBCn zRmV-dsgDrzF0n~nzVC$jcJJuj$(j1wEDA?4MlkUCPAW6qPV8Nmu_KL9 zR;f!iRE~(y|MouIAFmoU1trkDNI_{O9lB}uv4=n%nIn)bK_zN2u#F5Rei0x2jP?{| z%~Vv#3n_VFbLy+`M+yBUzf3me*EQt8V5-{KGBTg)u4;alqp#4Bll<{5?Qs^&jsmlz zZvMC7Uv$l(8VRuw5DNrei@$PGv$Gf$fH9Mb1h74QOu1T3{G zCrOLm{)?4UAKm`ng2U}4c%tbilB1KMOSGzl3%bNTFT%z4eRc3$#M}IxC=Ss5q+s8Q z9E6`O`?Md-^BcUIDR8areC;pILIwisXMii|7qQ5NHPzkwNmSV;{MD7!&t}x{@}2QQ ztAt47#y=!dugnA~GFv21T0CCMXSL7!?baH3VD0E2NVQQI&_*hB5FHvB8l{IL5@mZV zHp-XWE%Cxy6X;@evaAfr@ym};S3l2u+OH%yH~EsenKW}#9MY2oqEB`fN5Odf{9!lb zJy|_ThUNqhwb0ZPBch|&7vui1IDLi)w0ThR&M?x9%kYPBH*1z0)K_Xg(~9&8kF->N zK;~d6Nj>jq@RJrPRsA^_&?h&07P^S9r%ruF?L|b@r-{CW?Om!*mrrlQ{ZO(G{{66> z7HP08Q+>l{@R1Y|vHW6s z%ip_ZkDt;;??-@^>B+KR9 z)h}=9sfv77eX48bD|NQ89*XMj67Do=8O*Z>hMvFvFn2Tp?H0|ALn9^TOW69%AFmtu zow?>o_n***)Irfi1a75PkXZkFPACoehE#*2L`TtJ6s!TdS6Ml$!ko~*XuwDVOfnwX zt)UE6ZQo-`b3cD79J86IOiZ8Hj|sG4y|}W*tI|hI>eIXt%+y3fAC%tu+yCjh2d)t3 zauGYjcLu#P9|uRfPHZLR@8groX>0;^{hLw%jFmG;*8eCKbOSY0-~drS`y#j0l#c)O z^=at!|GOQtvsc*>KNPQOggX@ z1?z;n4o3A%Dw35YA1C>ye1oGo7McvJXMnhhRSWPsSRZ!X5BWbF#SRS}zZXM2=EvI+TSTL^|>AHQ`IK+;|$>>dZE68 z;P^zhu!+pun%%;uz9Do4CoI`M%XOt4+%_F!-Gvj~x$KK1ZfHiILr$q#8J=mbIAW@k zcF*$C1kXL;yB0;|-(OJsw*qjt;62$bQ)z|$<|pPAE-Vo<(VB6v!%B?f0NqYk=pDDg3+CD$OQjc#WW!fEHFJwuG+-a@(f?=nzszvqh zZ{2lVtyDqD0}uQGl`xystkOCg`iSg<0*6u;F^lZ77EDe$ z3d;H(Rh@?;@7c5x=o`cUPw^z1E%cFavIm7gFt#d7^54Ug=ULG5s_(5|le;9ChPbbz z5`aK*y2-UPawm~P#j%7dR7t{aZqGZ};=-b0>5%;ys7L@SN?C=?Pt6cc(9np#tygp7 zCWkQFqk~q`-x9fZd2D?8-t#lnqFUCw(c1i#5N`1n52od`XMRmuGT#%-|4usTJG;Gv?V(0A9xry*YGw`8yiK!w zdF1t#BP>u?KGla;WekRm#n2)0XJTCGCaNGHo=fSdwJx!$lDca(X0ZnffEAE(*-U`H zq~-gnwNCc|W8p91d{m&%aw9dT`+l!@7%UwgslJ>oy8-K7YLpILNrB!*)1x-~6IGE7Ci7@xdSyI3qM2uNn|jU+n~VHDs%kO3L)+$E)C!WXig1mk%<`!8>o zacG&-u0~4j;3%X$xKoTZ!~^O01%ELn`)n^o5I3JNJ+_gTYkX`Qk&MG+nH3bzc&&*d zmuUH1P)uhme8!fv;-7?Y4$P0&>R@2$5c1#Pv+c2iSSgX3QHk3%Gu8eX;!#mRT@+M3 z(`hT}TS^Lof3qq%>wQWwNS`(>{DUUu9;F8IOC`d;2Um&m7GK}g0QBUKfvymsOCM@I zRH7BK&HjlyZE-#t^ZH$Ev_>GCO)?3yl)|>sc%mArK;udygYr zu#vQNSF-3MU(P`R$wx;!{5&+y4yy_VHRVQP>}muXm#`F0IZMHLf!RaclTf;)n(9Wj zMiI+58j)xAWF<>~pVd2MYG6L1G?d)Zerq_+Igc!0*e_(|5&dDI`k5%!RnL5?*vrEr zT@Fge&N7?+s+v{i6{`r}ihVU{T$o~P^IJ^iQA>t9*xKX8S0pqVfU zN&+(I3QF+sCkGEV@Ux3?VoLbS9*O2ih%V4NZFXr%_10NKR2CclK@u$!f)wPPw~WE< z41A8P{DmJS3WDj93%De7k@%b9#kNmQn?RR)BJpd)IOOQ6x^`AQsXFy6ewZb#X#slJ zIu;&-2nxh?h@*VI=cC;Jgp(X!RnvK2G0~hD32j#>@nIoKQ8yo8FS{?^SErGKYmWWF z&S5>vU@nj>vtd|&wm(;K7h+hHxXoXpEPamc9u!{Q9GlqGCQ<(iLGES-$u>=6*N-`l zE*S77wT{@z*hZkGBR-5U)kT~Nf)tWaxz^{KpTHdyygbin5Aq-thu#R#2KPdFe zPsici4|4jU!^*_T7*3~I6RTUEbja|$KJDMxMALc`zPT&O3L?SPx$ujoL0atbr*8r# zAy5*vc;Q5AStd26d`nQS8qRf*(HJEdo5~uIu$TmJfqEB6_VLT``X|wH$p~CAS{Tm= zss{L>UDXXr@J}UjzKJvc^$CkP4`(_Z)e6}*2cN^VZR zR%+mW7FJ(XT5(f~t3O!vAli5c4Lc1`Q~lxvZHL67!Q(1JVV^Y+QTVV>?)e*7`DbXs4Mrri)=o{j=NfnF>KUAu4MK7wO|GGRrXuN{0mnp(01O zf0xens8)aOyl@^T?ZC9(;L;{eSk#fJHRP5%qeaG6YQP1ePOOp zZ^VsNno9R@-AipVw8$uL@9XB90JQBDwDXZDrQ2&T73`(Lf?2xK-(P_K zw(hna8dH+fEIGH^1Q;&u{#sa!ThnrjMWosGRK>!4*ZRA8uGM$O-90gX!8Z(__w=v5 zdiSgX2z#ojxIll;{6`sWmw*;}z z?T-{tNK^k-&M4jukv;J(6q4_)j#H*0*g$e9K3WW>d~i@IA$+2HTM*OL;{7u@;k8zIHdH;wPWpn_wCcUf-uzvXI0|Yg^`Tdq?!~c#7n`5LZ2I zQ}Wb3oHxZm-N5Mu<(dIF?s0uggfo=Dr0yMaQZ4nO*tDlpt^dkAIdp&kJI6=mfI^Va zD4%O1H{&@hTP^TOy#gls<8Or%N(FC(?C0rz>q=~#lr{A6t*SXk7#qCcGO5e=l?u)5 zSmb5ouXp@v0Gaq-89AJ2@K6~rMX;~(=2iM(k`HiL5)>QOXi9Yfq(N$q6Q^B2Y);{O>^(5ac`zJ29rAMS<9etDF| z5U?D@5TjqlU9@_;CKdgjRY?5X-LWGwH;m~4)PA~tvw#40NkKj``Mm!y)^%+%UjLV& z$o^(WR|Z#*t_2cj<0a^Kh`TXz$lo-g|9H|~ZTT%%;Ee;QW=erOV5C=0Zx@42VvG>R z>tA6}?_^jKOfTGP-p8pQ0a#fq)6Q7?h7%&x7%0RF=n>k7+tXdQ<)0;lhPkpH7Vo2e zAEm>WzgS2PtQl`B3llhsm&~lnoY)CS&LY)iUlJ;47k=inD$cLIW_U3p@Y}=XkGkCO z1y1Bu*3VsdAfq4i&Xnw`rSiSJ7bB+emsXY7KTDj96jH-G)T_m z0hgc}@P-}9NAa`44BaB6MPu6?MjZyQ)&h5# z27GGxT7UCgkvV=U?C}~Abw=8bmSy^7Q2sq9QS#?JH~!rR_%s>BxR?=;7u^=$`(4J| z60n({xW%|fV5n3dGKAYmN6LNZ!;w2qXPL%FyneiZVe;(U@4{SFlx~ zchJn{XJAAcax1 zDmcY^c7j!nkl zoSSC@Bo3v92(x=h7MJ^8Qj$L33Umvh)iZ$+Ds~h`TkB4!fFBl>7yJV-g6zf;iRl^k zEAIVNLM+C;o~<_n=JyV_bcn_4%UT*5RW2ojAYefY&jZ1|&y_#$g%G)8{hTQ9$CI*E z89E2cK*UGzC{O`dzTeI|Tz@@ZUIpv>jGW3@`#M{VaK=192H4h5+psM8?o+-gL?o^d z5prLuWST(*VaF+@?$kS#oj@q1SE>7%xAN|IkRID`@f=>dY>;R|$bGPw!8=jq<{3ZKS54Z)4Zk9!a++3Uk- zp_xpMfw&;rcwz!c_1S=b>3;uj85V5L0tIx9S0-M+iegQPm;QCsQskET>xn_-t)j6 z@jq(b6`LYI&7Tpv8DKa(okqHuV(GnawW6R~mxFJa=$-dCSKX&*8P?1WSB(%l2w zg7F)`_**=Gme+c_qZUXDLc+%r1=+c>9I}KVpGEJ3{BaN%&W1Ty!iM4xb^V&WC(;#_ z``f#~RM+}2#u+!mUY5&fF1ALma%EDZNkgkU6$FY6sJu#_L^>TBul+cLc!R3&ACnGs z2=*F@vY40=+20BTv`bA@Wkk#0^sVhp$Nr2ZJow=Y#MPb*4O^tVe2GyXYlJcKwY*K9 z8jYL1(o&b4LbLq&Csup7w82q*<2TN|gpnzuS<6dPoHQ0_nRUq_FCLo4(ADs|^5sci zh)?^dO~b(62^jx={Eu*a>BnEMuCAbuy!b`i22B3Fpo3+od?Q-Ax$sLCL{K>5@2RZt!tkY-hxOL zD-RsyKz&2_mqH8vfj8wut=R1|(ufCS7C94i;Ge8)+sKTZ`8I{YkN-dlR;c6Oyc8!&@&!fe4@H)}7dgj>?9S4E*ADaXBd-=@wBW4=xx79}=^z1nnvNX-}CVaEWT za7e>1gng%Lj9AtvhrA2Y7cAd2Y*|8|G`)U;N+|6Ri}|U7=rA#I%$gpx`7L54=mllg z8d}&YVahV^u6YqOUZ&Qw>77}c@yxZ}^z-cRqYI@+hW$S6XG>_nZ^H4`;qQ1YlfMWe zIf1mWZj1=)T^^-*)P0_75e4ItfJZ*0iG%$Mh%pbqo$meGHIN$;^4}f{+~FBg5@=W% zjjnHe+I=FO?JJr|zjqm1sy+Rb>_v2%fIzH#QmoODvm2(_;i#G4?S8M+-u`*|)=vg( zlIt9zFS+N-U_;_a_W0+d2k;9KiqaU73$7;TbV;%POzAf&c*+`8P+Dn5rsPykopw*z zd#VJnaW%U$_D!(<-*s;rX~0jo?3wy_^r_GCe8UC~srA;e3xCVbUysC$Vy^3V&&GDN zjz{>HgggTg<3GXU{~-{j2&#W9){E;KnJs@zR>BLfwo@-z*K)E*XsW9;6Dd$Z0bJ|( zv82b>=e?%Y`ds8J*QM{H5=-~@iX6A8R@dbU6A?2ay$9}W*8V9YDwayRnDsF+F>qNL z=6iAw?_uJwpyVr>z-wgaGM-_noYC0wm3@rl0o{>c{f~|&H2(9SCnF|Qk+Sis>aP&b zy4_0xQnGaLrzVfg+m>zWn*C6_tA1^LRFRTE)HrHW1cIjjD-3{yLr=Rm%`V$tQ=U$w z3ZK)&6d_TwWnJgW!`K}Rc^GH^vrOx&dVYYdBT4dJoof3@Sv;V+_j+_ibGdWi%K4q! z5Zfic)7E^o^YqidYCN!F+5uA;c$fe_-q2U|@fyo)1;neoCFkt&S^E^vDtTAHPd;4@ zN#OZH#J>a%IO8lAp3(PaG11aCGOLczirN{J7u4735nwh+aZR- zi~Aml$63mvp6QqaNze(_m}+0@NChlUb>EAkd8}-JJq9s$`%qvR|QhIbFu+%iT2H@BGWk$SOl7xOsAyi~n-u5}@ zmXlvF5;^WpnZ?aSR~9$Z$Sd)OSagq-=0(&GM;@Q1`N)W^-iN9` zm9`yd0D)f57jHnqME@WQrANu5W@qLLIHqjwPjFCs7&x|nR(cmm2_Bxe*~)J;qSQL8 zXFlg!D_^%WIlS-0c>`Peg7kjO{QR=5Xzi{UY6P2-0k>5%D8I1KA|O%05rtgvjCPOXrkJTN zp}SW%u&-|^09+QTs0~?<1gY|G)2?y>rVA{$Rw|}t=nXf%z&3IRC~=q_Emis?`nr>- z-Spictx?Zd5^^mLh=f$HoGsHXZfizUKV_1FD3ZKSv`jK-KjU3iv?^=+e;;W=aN@fs z7KRLc3@E#=gyO~l>~Z}et434L1YuUEBohgJ2^k}weeOZ5HT=k+s8e-?plQq9j#qF_ zz((!&C{v*t5M?RmY5$e{N*3}^h>e5F+3oF@4Q+%kf?$OG{dnnWA&|XdOx#1d2C+9= z`Q_-0;w>Yc0`)1RdAg%h&8Y}RDQmm8GMtpRc}0)4w2w%FkdkREP~vhUKxPBHfI@6c zL?WsVjOypPj#R9S@(4w?n=Gh(Ok^%IbSW)PFgcDZo!Q<6i+cl~Iv#gS`HkM-20Tgm zv>P-TV(D2L;5|sz6qF;HY1cI#T{R}0k_cr((o#2$oDr!?tfX< z+VbIRz<*J<1#k{@Oy+U@`xTD)Pe+b=y}zE`1w*adP0#OgVJ%a%j)a0(_VsyRU^`@< z;kzX}CEz$ykk(VFda4wBmxtV*_;Zw5k`S>q3)S(_Exx;Q$QGn!%kuGcPnd!u2inw4z5k(I!(#b= z3NXM51T8^_^`I>+-_E7QI2rU&w7gFOaK$`yUC#kZhSlr%a4WK~v*(Y}af!D4WXNP5 zABLenU|Qe1pvMU{&REfuRkK+<&B;WVk}oW%;*okr)ddMVQF2%wC(+wK=t&_o)fVKl z+8+EwwlKTX$sh;&Dw~G1jy2<$J4Ol( zMcC6LSV?g4zCKn`_dm%d7H5h`zw9)u{)G5DC2vJJ0FOS_(&Fp&OY)&EK>nk2Z8t4iXv|A5(c2l$ z-r;0x^oC&BK(lyBpW=Z>a^U+3^pmxFeU=(cn+Pym=V@pDZTT;$4=+C9D7@7t)&c?R zj6c2fix-M9$v@Q{GxW6E2KCdASUZ{Y}EWvt+nG%NFMe_`(<;t zUXiM~%v`r6vx=OqJsiBRhM(tG;QtWyMwTnA8X!^cBRWr&oxatFA5Tv0ejxrH!epL4 z$-c2qoxY1*BDd-A?EiBC?oSQXiX=uQHOmP@D_Z|Q~xr{23xJ;79r5+Sju*Ive%j`%!r@ETw zDfuKG!_6^{n~qcG(2Hw7t|QDX@kq@<j3xKCDd=Tt_-W^xMkhe9Z5L%G=_F?#T}RwVgwo$@$dd~4LdNtD+-62;~vwJH(IWYriPqvnV2Cr+nF!iqn zbOw5abhk1H*sK*Fg*$%iYRZq{-Pg*)VrNWpz-If?-IS_$blltQtU@!45qbBBl})mk zZhS#h#oQ~lo8JLoV~Mn98cO#mcrJl+2#;|@`oh8iu@CF=%rAQ#S;-kohXwym)kKNX zTl$DxCzjVUn_FMm!w=l7t{g4p^37w2|7rav!M@2sKuGo<^jswmR+QR(t@#-9VEAx2 zwVpkb6o*4cd=_X{9jklsSx7yLWbZJ_=I2wbOokbL0g`s(p?u{4O+IrawAN!lt&XL+cX>a|6q-%m_neDP@dF>~N12&B&jw3yn_zWNgsd$EBYDkV8X87yAF*-W z29^AnRotO1_HQP@W^er9nE0e#gD>b!NKL+G)|2cWq*Hsg4AUsJxmu+8ZgpJHlGY{O zb(?bw=K{!+3_IbV@dGnu;Pd40_3TT%vL#zHlUBaF&C$XW#qXS=E;WnP4s(X5B2EN` z-`V839P*v|f<3+nDyat(B^{4&=~Nuy&e}AiZW@j{OrQL>VfVe&^W%!(?j5m1_oR3_ z3q31WAnHvlVyBTwx#GK_cf-F>tNOW%n(V^C`e>El*d?B304)z@KUd2!Rbk*rUeaO^ zT2#R~N@e(xT4)6k-edRob4`^WPlgydPUb%G*2>t+CTZj^bb9r+E#7AZvHw!#y+?l< z@#oJZfOL1;f_D?=ZzK>m2p4|3ekcGikZQ>s4b$MNN2ugKlfl(Daa}8}?O9CLBptEKaArZI3gUTRFZBcbnSMa&bEvy(mPM>iVt9}kYBl5&wjFh+kY#kACBL-KCK|gHBCIq_a zn%%EsR#+mnnWZr|+qIHWqt4JjPuEnZ)V-taLfsqY)If zPG?ljU`xY{v?Lxr32B^rg=;s9`R6|^Tzkkmi}yz5D~S7v#R(0Q>0v{3=H=vQ`>0{% z;z~Dc2v;9Eg9|zIi zY2)K#)ITMDy=UBY7Yh+oyhp64vX|R5+fZ2N68%v*ChS1X^+GL$^9en_r~-XHzOG5~ zV!9yJeHFkd2~!>`4xie0lhvwb#tM#%@dK|`sDnq0X)kAJlUP~e7&Tk^Mn%c@*QcWT z@+5I^C!|$0$sKEbr&~n);JfsohPOl6!>x8#(fsrY{Y7Q>HptIDY$MdbG`-3)q}ZSR zn^L@{g`S7q;KhzQf+m7;v@M0x$+AIC2u0_U=U#}jeo%lWn@maM9RlPFI_^+f1eH)d z)3?SB_tOi00-?+MuuKUX{+1zAXpsAU(uDA9X(XvGDGMV)`AD^^EQ z+E(pVRhn;qn35!l`bZ|z$-|YX$i2i_5^kDIX7tc#L$}n@U%~=<)jrBy!239ffbI@T z2g99x4pKyYcTOj~@$r^omHo&6A5CW+6=nCmeY&K(ySr0LLZo5nmaYM5=}tkq8|ji9 z1f*2DyCjq@5$S%<^Zl)N{)DwA?wR|XefGYt&vjYmt_+%261Y`xh_6wq!3;sXa1t~H zz&bp4_TJ>H92hx*5eC@Sot1C9_sX!tp7==bxCa8_Qx#xbZQ;AQYb*?9Y1(_|w=}v( zV1@~1F@A%{TAX)Xt2%elK?JH^S9=ds8-at~ICNVg7nBf*C&*JlEIRw1D|-vnsl`dU zsy;_Dra94idB%kFt+(**_O_Q6q#}*;`eG>=SvuYPwaa&qZk>e);1jPPUd# zMUM^Ezo04Xr#j#pm%^eyoE1U%!e%_zNbsP8U0aKcDvoKw$$XVTK~Ka-@=wxA=f^Ue zDzzj(xT+S6p&Oq0&!I4&@;Wz0xv&-+@$33XtRDaNS0oO>%1s{{xM=su3qog;UsW`n{sECD)Lx;_&Z;*Pc#*!#3png&@Vbz z-!6%Sbnn~;J=C|_#Z>gK-OR=b82Q|=YQWKQ-2vP?9~g{%3v1gI+t)<|%<#2i;?*uy z<<6ZCy=~~mXc0`5%PM9@eTVWga%B060Ehsg*0Yg6OB+I0j;N(NOnMdNa9q{D>v60> zhaH@Rj5bf9JV~0hl8M<`azl(6n|#}{gb|yx%q$=j20<2Ay-c2K#j<(sZof8HAj>Kl zXw(@hsJjaaefI$x*nzFsG_L&U$d9Rzs3xftD1S8&wx>d6(wF^|79ya?5$ImQ&&ikJ zZk=aJI-;0(@SzK+m;aDf{nD;RX~0(76DU!e)8Qz)!%X_ht=#o_an%1g&*&${wr4C^6eiXHv2KDP;D8*&_9G&fs;BZ?6QmSdF&&;Q_sIt=`@dy>}L&MOVEO_IN62DzT)$uvsDZvcL^PN!y1t%Hp?XpccjbM_euMO2OiF> zkSd4SP7~?8SVGX*IqITCgQWLx?8;BXPvgrJ&CUJc(Rr&o(=g;YdfG|<=&mt$V8UEl zvgdYFer$*()mI#KE2vKc&5O<2NQX9A1}_SmXJpkw~4tXC)0u1%Zx!D6!7p zhjY-e)(Jw)=?83U23H2A{;{#W{W-mUW=g}hm*@JS1qQv(@Q2-7PgcB~uP6=aCETG- zc^f+U{p-#O5PWo)&?&ZUrQ+ozHFzn{$ojsC=Pj{@k3`Z%l@8@2n058sKNtPz?w0j* zoZ3h44aF^YZq*>{kA_+JYe3p6+%HR*XEeHAOfp1P-5(f>m zWV}y_6e!6qf(Oe7#eu1k+Eu^wjA7kNn%A$^IVKIyNUS*k_1T&5ici)bAER+@so42~ zG)_!*k5avL^er|tS{a|VKTw_QZ>EwKjwwQsIKlB;wxm?d+rXs|+O4G7u$y@x9ITi< zn0#Qckn065uo}?aA-w_Ut6=$hT$7-e)PbAugK=*Eh=e`T_4FQ4?H`Kc&g%n_>{fI{ z>VhYTJr^(Ql&~?^DlQi*bFok8#;?D?XKJ@3%+~9S>v8OO$L;t87`tU#e82{JXj{6ALO-%HFZice;Mz-QD`^N&d8B8V)Oa&asUoylLXyiy(?^|U$_%8$-$S4 zu6pPymJ>nh_|xBp$j2YCziUg7fOky}d48FeuuGZ975o8j&h4#ILSgM1W#2KMq_xTb z<)pywm@?bQu*vyw&?f=eQQ@*6FEQ0`?<>V~>s46LXFYJ=_sXWmo95jY``B_oZkV5K z(TeXgCMn5AtiR`I9qOOk*mGgV${|)jsD5j1ovFlq(c!=fN?2Rl0Q)qJOA4)T8q3?;k zl5bSxg1TGg{VR4BEV1Rg=XYY5FOZt^0H!fJr{LXh_m9FEvEvCze86Q*g?P1=HW^0y ziKlF(?JC#HE=K^#&9sLFv8A{hGcd<%tPEX~yg^HDvDx9+ns#aj^7i_`4*_=nBJwgV z-2KRk^|$J3+?grw(AVG{3cvPA^ z0e>K#SA2R?84L+#gWq|F2C}fIu%n-Xs<6^01kSUEn0~92t3hn%hMQClan-FCy&RjD zMH2)!_(HB)fW+p+`$&YSOEqNUb(LsaM<sQ~~VZvJPaW$);sILGS1dEP&l z>!xw6s1))LvrrLZg!(tl!7SGAV~~`W@*K_WJchIua7Hz4)XQ{&VVKkJ-x^15Qe^9i zFB$LS@|Tfj;|z!nfWsr-l}UDXqq;vvqdbitU@7aFE}1npM73MH$KP<#Ye6c$@VyfM z6Yc%@1ad?+SLaA?i0gHzG{zln3o`BO{{s^I?w7<4I`xg zw_g`Fc}L#4XnVUMS{fcM`9A2r=0zFTRM0NTBFp=gz4iCP4 zX13@(++xFeGwU<-b+Z?nEI}%HKM4!ar`G9!p$Bv`{+pzL0BjuJvZog_fF>nHE^X6U zPnXpHk`jA8`7coQe5gsjRLGv$VLb%eRi7#7^=>%$>uf?mGtta#)IcEtI6Y$YY6b;_ zvE#>!>G07gjsj~yFwZV-Ibyck$46-3bCOK97zl8OS{8PPkROz}-}h_^oCXc) zQF>?~#Yq?2{Q5c3%<1$3-mXpiJ7*zZFSBUj+2om3M)MKcNKE$l1xe1x1tmvQ>0IG2CC}mB^=Pf8MJ|zV)T?YM`Y`#!v)>7uBS0 zbRbM_ndL|dDGZ+SE9^F)bIs2&7(>asPy3g|WLZ`=k2X52-M)zz-J14$xg;H)pE+MB zJ_P#25^lkWS*6`PE6&On3k?=d`(F=<%H1`uk*+Su1a0m4ZCJ8WA05sKu@wTnHd36a z)fxH`w3-pWR_X_0;K0Y@Go5(h5u}3~e2GGf>|t$DP?Fvi;ljg~X840X{Np#)M((^@ zCDXhSwVJ?KCG8lotoYI<-Tb;@!EH+}jXs{z*a?3%0mnw!6%u6#M za!Y%z1>iHKp>Ec7{H;Aw6n8qP<4ba670@v+_e+XPL)V=`^68FAAm#P8JYHccU^3rmmgnpvAqXK-o=` ztAvadLE)hycv)(F2dW;1Q34wT`iuS}y~$25K`^)Wp)#w4In&34G{w++WB=5N}M#!aoF4jZ%HvgmZw$&4br zPPF1>c~={VMP_aRS6(hzr^n$XO6ljNaZ8QM{6?&e@#s}gVp&?vKE~a@ZnBysz85wm z2fyj@^CY@IHgT{L-33|ZZ@E@}&v<2h5pMeXNzL$e%F^QY#_)y|98RL^|=m{#1Y zY1-Z8h_)H01>CD9?E}3cti9w=aDSVzY4IY%r`@UTNBmLYmqAuAE9~s}&Yb#MZi3$k zx!6fBLle8t>#2!)59RV-m9y<^HV>7{WBvi{`|W0&o8!x8lh8<70i*}RIMxTS|B%GJ zP~q41=)HQFGmS>!OT%zNP3kx;i|}lSeFcmkpRJkFP>?zRl`@RAIE=6x8)F$)^6#E8 z1;|f(CVfllUA_TR$jSVopWs#G^&<}_Pe^#FWLo*LhH#?Q`Cds0CN5mlRl49<+st7?j^~}iBNiWZ zXn_#UF#J3L`B`mfHJbL#)N^I#jpDJoz^F6$GupwGdPHw@yWzm(`oJBzgnz!C zBB4sF{q8a_2{xg zzvWpE%mORB5XkGs8`0d-Y7p zTBG7#>(U66qLNZQzwnbV`jQY4ak&K(@VYnxJ*kG6@A<`XrHj{f&XL zs{AZ<3jga>?#FNtqFa#9EQ*@GYA213=f)R!efFMH_=shhv*opsUkEMn>IrO8IFdB{ z;&UP&?R4XimO!0c#!A4fnXXncc~d^|n;)*|G95Bg+|iXKbtp!ki%~kJU~I-}2N|P5 z*R3xW8#(|J$*&$@C?q9AfY5K2%l|B5Q(dKST|9E9=qu)GIiN4? z0ZzPaTeJ#PRv~gdf-F1#E1hcpgCCt=jbjVZq7i({`9_8#*7I)tt(Y1-l2VVD1DNe1 z3VMJ*4A6Uf%szw0Q2}8=hEjq;r=V>*mISO zZy>u_xRWQYGm}uZaxY6GN#MJblyyiZQz-G&jKhPFaa7aq>7bv}mf0`<3@yRM`^@xe zA#65I?Uo5OG@Z5(e9ybtBFL=c;r3W%UQ8O6x$q^Rjlbw zn-5&dX{Hk`+P_YO+NA25R1s{-VaDarNrCc#ct^~r7V3Qp)bpG@*onhrygt0Xrl(6!^|H~IZ z9qK+yB>a7@u%9J^yf{h1rc;8tVOe!8I@frV;cR=5EsX#}lI6qfxvahH1Ef>aMc>)G zWgin^Oj$NaMYu`G$7}|<1mdut61c}4x1101SxDqwX#+?m__)G0ShFW|nc~mdGK0Bn z=@ZZ$OW|ogXW{i!4GXPFmwSs*HB7Olau{>>#tW|$z_!#^)LzELqojx{QuWnnu`a2f zBQ&-1_%)Rw87zl=&Lr-kyyHkGfG|KbY1DbXs`r;&S-qs0y1TX1DM zZw&KGkSLG+sKC9iMT_=qa2RJh!K8^hOX*!6eiF6`nL>h0L7oM!xXdLO7mD?iFRHF$ z2U7BrQL@>zE$#~@5fiGQHA<)@hvgt|(XS3rwom7p4DBd#JYW6u3BBoFCz4#e*OrtEw_y^rpG#*ncTmX&?q#-0F zGtPW!OMq2QuYlv z4Q8p6)c?_KXnu9YMJ5_A+v9)JGQy9ZcJ>TAeKWF*>pAkaKOm&(#OUV2Lhms1Pe1cF zc!g8*4WEu}zqv*ak^ZmJV4@DTEO7Ws{3KXk5rkvy8?_*vZG|9KzOkERX~lBBi=dhV zx{Ig$)ck*G_qgjks~PyIt&HQ}{1T($TXtx&VO!^7LiU&>FtlYb?u;M*rvDIDDmP@} zlB3vV_SrCAgMZ74%CRbr#*eB=lK+#+b*y#@%YJW!6baaNvP76{o5gr+{#MTMvp{mWcJodjl}$7F&My*$EJoyyc+qpG zQ50NKNGs`3@26b#RS}7UloBEcnJB`wu~$jcG+%Usec70-u`|mZV`F`$`}$KF4Pq$g zKOk%pkvq6ZIotJMU0$gl{S~P-`mo)w-%&5Uzf*jworLMTiy`DtLrXHGULkIdYn3Np zq>-egxK$J9L#=s8e3xIff$ZsexI1X};_Wxwp(62G+AD~myTG0yPdKvG#XLToS+ZrjXb{q;z|kQ_4&bGYb2BHmrbSW0n*v>pRkIGsl+HOBjX1X+`N?CpRGVehN2 z!pjvCL4TJ3^2FDnwlT-bTIgkCPE`)%R!$tft~vI<`TzD71E}r&-BEGZ(5`1Dt^pfz z9;%gqjaA72DY{&3?vxY0L4o*rSSW_^rv+t=aHe=JOHVd&Dhq??JVfC*QU5A#Or-_C zLm_!vF=2zExsBu6u^-%$Y`yDYh@#$!3=Q32t8_EQam6m7p7$TO+`G^p0c@HEw*qr6eHxOsSZVC-ZLgU}@h(AZ%rmmhR= zd+{}5zA&!ook{PhkdIp*Z}gh|*Pz;70U+hW{*TI5nY}nWlwS2}AMk~w|Cjv8IEL;_ zxzT0R6k}rb{VNmQr?xNI2(^V%E`l&W_a%B!dcHN6U6faTjDqft^80%ypiPtQ;DN-Z z(w}5na!>!zMm+?3ysk*_P<_W;Dkz3DU^=_7r-Crj_MjS1ilz8KN|)Q1G%Y|zmNHH8 z#`yOR7WAcjxV2F|`C{fX%`Sw%6{Uv&8( z?UlVBTOrnx5ZxZk410SvLx|`Va?JMmayLUbA|u1Am8&syhYx?eI}H)wTO1YmDDn$t z23=g@Z9)Rc0~U>yP{R!j@(+ve==RsGgmLJtJxGg5&FB1B%kI9*&iD?2F6{Aj)A?a~oH$E!05A04?gx5YK|LD6J+bdb z10$T}KhhFqD;xJ5HG1x7z<1>i(nc)zN)-r%2XvX;tD}fr$?Ge2h|ky*5y}zE_UkM= zY1_rYbKh;a8Zys<7GlRp&lw^@v3m#i*YqWuvF%#{O0dOxl8z zES!|q8Jq)p{pUp(N2KyWg~c)H6wb>F@#}k2Kwz%@rUK|z05r4Crnd9-h>46;7$An1 zUrQ3E*gA;+%`AP9Oh8&{HSn{7+PHd3IenfIm=*&4OVUmaP<)t#(l@I7Eh8g5W3&si zrPlNVD%Vr8E7G#f*shP*@-*c=0;L8eBjGB@Z|n)wuu=>fs+2wN8V3P(geHW?0h40{ z!9M|-_*PAQIBac&(#{wuImpQhQ#ZS~z_KrEB_WOZIP4wD|?SD6g;qlwB zWa~d*VCq$!;SJ1*_3|j~qm4j$NaFWDtI?|>a;K12VoX^QEuvgxHS@ui#nyFu z`)6j2nEOugMTOe7t(R;KURppLUYkX1$ZYXLLra9Nd8TvCg-?s;z+y{ua}K1V%6tYr zlF7=Xx-B^JI3teBRaApG#2axp%{7yTswjbtsLc}niZ4;x&(p7?3w$ugDGvFnmyqf& zu0Lf`w?L0}kifUtcb_@!M`JXJBvD4u2bW92(S~hhd)TbF){|T^8Hh_rOSvk#x}S#-muAG} ztpCU?fJ4bw({F@LdeB;d=;WMcy)aHxbzHIsZhtsgl$iX&np&=@d1mPho}9Rx6A^)@8<TBVbd% zV%9d$&8D^0*e(aN3dyaN(T46`k#wh_+?wnt5^eQ<&HLB&hFRT(BC06#0ZYu44f-p7 zkXg_)I>#Y5?90J@yC<*7#OJT5v*)A!slkJW_J^W$(tG3;Y%QBe-Af?|wkJTWr&sXFOEUaVQqBaHlTo01v4CNVVV$ZMTNrbEl*^r z1jfTs44L&i75*ej&hLh(c)9C0^j?^FT+}g(qC=dxhb+Y4eR~ZRlYZKL z{M3`$dHzbFpVT>h#Oa5H^Ukp>s)9zh0E`-jtfa;Z%D^_wFQn%dB z&}uz5KrT@xn&0B%kTc+z_FGC?Y) zabn3#4;VWPBwhy~GeAgXqeV1OJ}fvYqFrZ6PJ7gRWeH|J$UsZl7 zv96b)?3!G-u>IM0fA!Y$2If76Wo&C|yK8#5i_dSsD?*fQu}O*TSy2)lC~;hFWPkmEk8aK~rx%{^HZ2$gof?Z10Ve3DU7;FWm3)FrqUx*amQgpD2@`4wi;lWKX z{JfZcNClS{3aWwI0JH5-AQNX=TC^Y3&mZWyk?-(Ph(zamTqs*dP+v@$FfAl^e4O;! zn@fY5eO@#Z8fnkdc+57zZXO%!L_qF76%;9ZbZ=K3DN6C0h2$lU@vT0P-GR#})&L4g zGmgUA7H!8AZ@P=TD=j<|e0!q_X?Kszu6ylxo@CXsxf1M~xE`4~+kIMwDcxntv!pJe zVuTLA`XF&!1^}P&GSdxsImCOPCOm|XSElnB$=osj0@pds&awAW689cCdf+l}aoOg` zSCV+ih&g(6&uq9UcYSY4fGz#BJ?##>t{@}}*n?`Ux-@Cye#?~^H)-jUllruy{+p~o z+_UU5vpf2&silRFX0|f%<_fW@HH6_2{pkA+(nq&fWFonb`&&f=-xg+l7pJ;vQu^j8M{0XKjXD2-emwsbt~hotf#T;v}!7>WDoEpPR7IH^*|;~JxcUpah)jVW2_EYy1TxBb^I3yEvJ z8+Yp4b-r6*y|*odPPjP6V-XHb4tK3x1=JtkEbR@YCyyJ7jQa|d$K$~&I5f2se80^R1wH$efxVTf zApjGWuCSyuU&`U8frrhrpi}H2r1_QUNLRdPiT8q9QAQi)A8Nd-EDM*g;h;tu0?iIU zJCTJ_7lkg4SCm-#DcR*VQLKC(;*5c+$u)1E|LeJ&d^6Zp>g=nafIhWYs9jy3`rMc1 zCL~tel#qop<(!p+W#Lg9c+?-5!~wF`y6$qGb$W@xXnS}A`BmmV9DK_Q%cc|4MJ%c3 z+kpDkdBk2jht-94@EzDw+NO^B18L7TG?ph`2?vT*{&AOf*Sp;Wu-5+l*;4b)wNMnI zW@j&tbe^{f`@@^-SADqn;-Jt->#Qy$4&$Qrlh3B6azaTRHyP+hox;Iwi@vFt^*L?d zIBn*$-p{Uk5ufen0(e|w@HF5w?d%O4b!sjwIMY>DG8wbXaic~f^TsO+`hn`ad#bXY zWlvYCnfJ-mxEk}e*8pD&{P?1Z|Hrxo{7pf_>k5rt&QB3?9rDpR9lL{D#USqZScvle z(F|}lq~i8Gct|fm-@iIkUxyH-Yu*f4EJbCo6r?TCW;RRz{LMug?FEtzFyu{?O>CyJ zv?mzL>fA;`H9|cu1k-+0-B;nr?J0y!A0>ox%HLR`zG5)1(+d{{=7FbZ-JN?1IoAHF zAgMyh$ejC`d@BNhu;9^6YRO?kSF7FS6Ym%J>dffC*H8mFi5-58J~9j>mU(h&{+#rp zWu#sI(>R9-4?YHc27PQi3QgeF1rNtf)g!?<+##tkYX%judynsFb$aZoHe|9tv>gT1 zvK&dvF)t7igf@ zhJT-PcLCDEu1~PJL~QD#X13Z^p@mAdA1`^Csp#fjfR0;n|O*x90lw?B$X{p zCOPE?tIGpOD7dH;cihXzr0fE!$#i2x$0?W#kW&! zZ&eR<{%;v3VPFgqManf|OV%T>PG~*-H3d4W&mG%#;H_j~`;BL6r>B^)tCsjoDVQR7 zKdkDs>8BtPBId30*^#qPFi-b_h>QZzGPZV!x}bCpx$0o#o5O9?4?}K8ZLP|t z1p?0|ZsWK}&%1NVwE#LMn{_J)whEndjffAgvZiCfH7SVeQ;w!X2mpbBg_}@V?h9NQ1fY*E?*}^Ro|6wO!$(6_+Q$&o)f{w~Xa>wZr z;KacyTb&MQv5vq9lw1#1v`Hj&xH|WDy#u0Z{16a(0D=cd!i`qe4e0?w;dx@)U(QtI zYVC|_mMl~T+eV{>?sqNgS(i2ssl+Lxsfoxs--MgNjsM|0tLAnUxXXQR+#-lFSJVOS zp#^S0p5K#np~h#VVa*-~`)~Emo~tlfbTV{hbgxiph;f}}#6oRq)dbenYx`vn1dN;i z$q$EzY84`QP{18(b^T>-=#LKMH83wu3n{5o=3c>1dmeINjtS z$%{@M)Y5foOhk%CV)_H3r=$`=?MpBA0m6@XmL;NBK;~5O4zyxQCWZ_yZSu}QIFhKw zZMOQP0My z?8hh4y=jsgAtuo|xSdPjYDX3IoDaH#M2Cgg^z-Tg3mykch=PeDRconVrkTje2Vj<( z>ofvHf{(yO$`0PJtR5C#wphFwCGkPaTv0w%6P8Ns#~SUP^Ee;z#h)oL#Q`dI;-2xv zW9O2O&(iP>L>E3E80zNSn`XqL!f4B^*Q?&PE|)fhs~%%e81x9Uq)QDnPVipmv42YR zAhpa)3`C)!J=UY|JP(lKC`+5p0Lhho{#QqrKw9pbJhmp5bScOvr(Mz^=t)NZvrzEu z5Ot(xM{YT&%@4Mmez3Dkk&&t?S1srB8=fm%k-t8MT&)l$Wk1582^ZtqGJDwkN)4S+ z^6u`TwMIkl6QE};W}p4-qMmWyMx|rtjex85+%~7d>g96Ro8guk7%FGvxC2tSTEs_@ zROOaAz*Sv$t9=h0DV@{Q>_f@z@MrI(9XV(j3@ccGZD$?d5Tv}Tv4crBolshbs0iW~ zHG(K!0M{lVgHnA1M5@6JR8qy(VSk1Ge=m50B+_JSnxilk=G#Hzv;~`A3RDi z&~EB~F(Fjl0ujp;ebv2{(ZucDy!=p%2kbG4=t=~%266jYJVR3FoTAeYhL}!tEGB|p z$`6+7FMhYkalv-yG(s8)R{caOAZkt3Ekd$wCFKrz=fL$-1v>an?q5R62igkf>4v-! z-VJ3~vnnw#>q4%yf%y#(%+RZ8N~De<|GSiU$4+Xqgl}G_(kA)#YykcLhlAQA{C|`= z9N%;C(eTdHfe?Z*8*9UvoZhi@NfaeRFL z@{W^{%i9DS;bi1hypm*U%*SCCWECl7kJXamzj)o}!=jzMX$dYl%pLuDXxf)e8ACRE z(P_C^Kp20_oz<#9L?DL1p+zdku z1nfB(&@bTh^u)f^Y{(93I@8x`)Zr{sE9SYn@9KCAzMAkH)?zaJMi;B5d;X;1$5@Q) z8GP}$&zi#&`ctzk7bs%>ut(4e@Dt;JqL$(sPyEb*KnJ0T^8dW41E;^0%J>Y_^L7`PQIPodHMak!^C&K4vu-K zB)2oB6;X`_8g6ml*8Y)92Tu2CUaEu-78hYT8A=aXxl9^;38R(`b$IuQt*?vyU=jtHJ5>oRD-WO1s zaYV^KFmw^JG~)j@Ok@xPL!4uhdH2gxoDA|b(vehtM$2(e0xn6+nx>wEjLfb^!P@;njiS`L=c!>Y+uhW4<3A z^9hE)y}_M?+);S$DK7TORzq?xC<#Z>U}HnG^^~CThih()sK(oR^8CGaQ09+!vvu|8 zn3}#-5n5)l_`}O2yX~4grUN&evllSUaJrBu&<&mYxrax%=ga+kzwu1m?D%$J-9<#K zaPqm<$|$r7mb-+F;F?0-k>GGu5Puqka`9&%DVrzm6_9sace6Bc(KjvKG7%cG;Aiy%FWn#S0AEo~V3uv>VEs7jcuboT zJG$QJ@$#~jZ9?WlYMv3t+|F!NC%KcIKeVy_jWw?Id7eU0 zlfaESUm~S30zN9N`-SA*F+9?SbKYp4F+e5g{hf)+R1EpOa$t;4J40~n_XAn&^4-N^ zn4P`H)?tww7FxA5y7g^K^0%1B4BvM2d-yOWt2$26Y!zPS!_f0CLI@Z0_ez3`?4nA#x3Z2eB_|_O&l*SLlm&a?Cvn|E65UV^O)QPA!811AwX`!_ zNC@#vSUlP(rC)DjfE9-y2T??**WMHB7HRkYe=R_maAT^|pTT|!`ahk3l*QtrSsPg` zg~Gv+r*`~L!6SqRPrMK9ONR*Y{et6{0x_razq^{I_iJrv7E2)~lT7dbNE}?a{idzx z6{jN}n@(uaE<>*r#a_-X6F32x>hodpNPq0jRhK2WvPg|>pB{sJ(l5Fy7vmmmbd%A{ zR#RVJM)XFpXF6EkdWwUO?%ugpD=G!P*I=q~WE#;{l|@t5;wBihGlCBpJD{wqcD&5u z^gtfrF`Lov>S-X*Hz7)>0md{1+gv6!xvjao@4Ui`UZJ(Hf4JTV&VP(& z(juOfs;RDq7`0)g$U&TA#tTPAy_qmj#$Rtwl6%6g* zrd)+iD*J8hCCOA^z|iVemi_D4=Y_yPl|QlZ8%qL;>(AbuWL4PAwTN$W==SYgK9G0V zW$6uP97nbrHg#h=goRw+OCCJXrw~Bi{SE5rRJ}3#G3TkvGwE@Ach~gqCAu^29+2@5 zbOreq7*l-`CSkpP?Fi3-qGACtCE{H}tFCkc9aZjn_=_*jWDF>7RLs$3xeGIg`k~bF*QcYUSJ)<$X zY!+Yc7>x0c`vk*>SUWc2_s-jMi=b@pP*z~+9rb0baat5|kqju~*a(MpCsN&T5+Mav z`hu5WEx2sNGsw+r%bSGBA7Dp+-#6T#sp=0-T-%$e&eMoaXB-9@w$(<3n%Qd@3v_)# zK?8F`g$9idc|Ub=POrV6{97-V^`8WWe?G0R?5Rrb0q`&xToBhhveLG5gDHWpZz0o5Uo(~ki zo<%XKH^)z^JLKeL;pAFY{gmCdYv0^5>;Ng`4(fzAYaz9RMEj-jhs8b~7k3DJuJIHj z=DV@(o}PR%IAcr%_IY6d5Ef_e#!@CmPyPIvXZYOYu!CCd3+J0}jEoVAXmN&0I=Vt+ z29RH9$W)qx6V&!Oir|1fTY5rR`8!wKS7q{|UBH2z^x&C0{iMn%8^x0Q_3fs_e>aiz z-7kojANh^pg&}CED=1_q$>7*Q|0+BE&e>21x4_gKTeRS|H-C{yZ%J-r=*_Th35U*z z?_zaibCM$x>xBPgIg1V#=yN3d>hd3`4ZwV@?4z#krJQ#V_7szeO(yRv$YQWmq3PB7 z;9GkYT+4|A19$04($04;c{u^w6)cC2;hC87uk2;I?$svv4X^p4GY^%}Hfv=J6N!GZ zNbVck1-rn<&6Z;}mf77ABD;PpSTt9GIE}V6?T%zu1moN0V&ey=1}TL5!uMd$mCd! zE6Z`q$ZCmj&G}?`UL8(xa@&tt)|o~#-c(m2u4MntKyc{e48cBos9U zI&ca{GKwjb|5?&YYAipPJBtY^7##SIqOaYeg4oes7Da4j_Q!J#%csVj6Yf?|8`sRk zQcLTrHppY5K+A9+!Abt^3T|z}y=OyWyma-%mgy|gb~O0;k>&aEx)2-z{sb?Y`MZ?N z>?&WbD2#==iBgK+{lO48L{e7~y}Bzk_(jZI((r6hmx|Hw&4j8Mf0BQKIb;_Ic|1%cD%?&eBYz%d3QFp)GAK#kYkFkmJr=y~%gL<~-`Ehc{t`H&CHd*>k z?Z3inJNWtDyzLtBa#FXyJ8~p!HDB;ftM}dpWHfQ=QxR+OX1u(ZJ!l!08Heu>iVFZO zMgRGbacdRycPi$IF`nTwfn*Y_?vGx+FR7VEo3uu-J*urdlnH`Jf;?of2C6xPFR4^} z(?UrR9mhj3rbVC_5lm_m4qQgIeo)pxxh#*B`(vgBl40F?zH5^<{wp|Limh|@cn=YYw~c6=Rg#3RJ9TOnQBGh;tbt=a)UOOPsE7h$mr7I!70$j#^~V9B z7ZX-AhM_8wp~o)Z9k{SVbakay4wPO_xm#1zbhVXb)8&P9)Ju?GEPS`bD(mD_vC9ig zmL|iyTMj@;r4P1~S}kXDxy4qSeXqhUsKx+Y{Yb7qfNcRsCgcHr9q_H&y^IkNT&RJQ zS=BNVm!m6T>P`J=+xr4VSGb;i>;i5dv&!H4l{f1R)Jvwv9DEkY3z*0HY(5gE!c+71|hqD&wJF&iFV;C*%H^cbZ9}7dvPvTOJEp6M-TL|p$ z&lmM#GCq=$Y3oGziP#Aa|C=0t@p>vg?gy{~mdF<*Ss<2se6@nZuC#5kN7lO{eY8{I zh0%zinMyNwN?Bq!E5XgEw~*wPO`4=exEf~U&vXp?Fm93_dI@AJ)U7UMe02?S9rZ8T zOBABw*xF3(GmuU>g03o76v{liWZ2ZC1b}7Y=4<>A1;OMTrp#>Xs-Rj0RnxpVfWED- zycm?}2W>Mi^d_+Lp$p%w&-xaOo_IHVOOjkmFXmz{Fixv2mb~b{nJ*Z|AB+kaQrOXh zbf~2pekj>*`sVLuQPKN_MA(hfAmx2A0{D7;>|3B5AlO(ll1f0^WA21+r-v?QY)U*( zs!%ca-c$VXW+JAg6)N%G_4q^8p1f<}1Tn#oMV2ZyOTN++>77IY{G^?uJeKGE)C8?+ zLb*ebW}u)#n)1>@ICiUYwe?-dlq@qD#J<6!@W7oj5ehVJhPk8N1*7DNk1o%?PzMD* zWX}Oy&u?@9R$G;L%9gb-bPiU`56Ot%pSEA}c0C!?;1L}(UbYFp4G^UiKG9WFeWmb( zWN!Vr=yDU(ai)tiFsLV##vY*#oz|=0cU=4vxuh*Ydt3Ws^&}!i?28B+fXY8M%CO1j zMuQiIba66m?{=R~VpQ1oSY%UgvwzTZ&y2y&o^pB)V{K{(!D!N{#wpG_h3t7CeAx4S&?PCdG0 zH#aw*{z|b%-ng4r=GV?#nOmdiB)i%eDC_nk!zIBL?28J&1%~cckZz=guA!tvKt#GhN{}w41O(sn{oU`wzkG;*VX&h}hBX!S=brfz z9$fau5q)@awQ(YQSZ=k78l=xBCjd_>5K2x1r9<&RSpwM=fF^HkguSCH=k51Hvz<^* zM|R~{DL^d|ib8u8Os33#teFu4R6Tl2*Ur`{O(kQ3(+gj+Ql?^p)O@Qw@YlqC84N)&c)=)J>Ntfy=i^AfR9zclrcHZFP3ZeJ`g$@mb~ zxUWywohOCv{y6)U8anfM8|R!$`?HmmZ-r7vahvR|L17VsT8P%v)q-fSMB#3TOrRa~ zpeq%TnSF^bY+2t8y3yr&MLt}RSbzFKGU0E!Qzb1KFfoy2IqutpCCQ+yFt53kf7+}l z>qTvTAZSQJ=V_EB!>h-Ib#-!7Gjf;uaU(`75(XuW%Wbk8&;i-;*A_oZnyYJfKB?7Z z=T)AB^iVGv?4OTb01%I>P|-iV#!EkH}A*s+#H53na&{kd9$6qvQR$!!R`81?P8K zf9&}nstRi;!)fVi9FrF?Sho^a!aYZS0lgV=arWolFU9BZI2R$eNxQX#o17+zYRgNx z1BZZ8FYF2Yuk|hqt}f?B&N?aI6~op|D=gQeIDcouM(GOg1SdN0%NpG=S!?EfOBxYV z53)4ZvwFwG&^eoVm{Il z!Jaa4YTQBU*OR2=+{_{F(s%_Pb8vcSC2w<_9eg9YLJ*}Mqc2P1P0xCFbnWw=PSJF+ zIPq|;dB0b^c-5wE^(3Kb*RJ}+iG7>PNN$sL?GeJQJ4Lsa9X!GsAuH4PrP%42nb&DBB^6Z2Bx=dmyGPOo>9mvafbRQl5yxr%%n9uoOKZ zJVk*@A~-BmLg@v2ozU~YNli8wA&}kWr&#X%Nx^(LJYGXVtUs5IvUuSw2HYNz*%;S* zT-V@kye$-(1A8WAqmhiQhtkV_l|VBI>vJ1m*S-HC8}qdvwJCXdrztv%l>)P=l#Hlg z&ehJ2cZd^Elr+HZKDUM)7NJFDlM zS@&SZywdBAXVHAkfhA<}-^H1~zM8M{nuYpd0L{S~!sNuEUt*s`JtD~$kt_MS!v0X+ zanG1WlRo|F@-ugN{WZurV)PR}?|gWoClSx{yS^<>ghI0(9!|Qc{kew+Fe%`RugI)c z^M{`HL!tG=5AdP5wAx(RHufLn>%288if*4X_T;dgQ5MYcF_cU+K>JJ^%ecl<$AD&B z5<)6thusPK{>|^E4n$^1$J|WttJjUCf%`s%ifnjY*_wn8-O6u;6OOtTu_^DNugF0^ z=C3Ib5PQZFu#Ail5_`g3+k{yzju>IMPz75kG0o;fBAVJz;2pc zm;iyhV|=z2D-~R^4{wsyB4QI8`9Ad>x>e#LIcZdFojh#2u}(q$<# z!(Ew@Fd_s{i8U0ux-n+KE0_N&(Y)g2;LI^7sA`H)=p)4lim?w)U*;X3Juo>&N~ax4 zAnb0ac7+>fIpm+H{9A3_&VUwZDLA-Alpnh4Hyo}9P#o|oyB{^0ZHSU+EVbUX**b9v zIue+#%t~SpLlQ0_t?KnoQoN6}%DR5#RqkL4bT8}4fE#NhLTYJ8*{TN@rgc{QOk!!j zi@!=QDOHo@ud2fEkG7bB%R6eQ44i_zbhPDN6y4Iz%<6vDz07a^gXlkj0mG5x`&XOH zb&9Ph)gq1J#s1;kzeVF02iv&o-(ASm>{6TK(kSYZF9n;r>-P(_<;3 zkK@acqX{X_Lh#`qAslBu8E5@_FO%o$cF4?1s?4C&Fr3#ye2jBvQkmVQj2r6i`au~~ zN9vXy%y;V4BCek^B~nj*TbLv@B`L9&xg#G#gT65@mhG^T@}^upB??!Vfb$?zF_!1E zCk|}ox&GAmO05s-G+&LJY4EepsaY&nsk3Ah%~I!9kNE_=c(yFQCMtLv9|yRD?GA8> z*vmChG0CQtQkA%EJnP$1wMMNxuU>$cIEU2YZC&yder&G%k?i@y4LZk5Cs9(_JFplW zXre}|uIb5fax#+|gLPwU)?F{E2_H3VYLA`OD(R25b;UMSmlay*NvkJ8QjwO|_2G8K zZ+Q{wu5x{OC*=6@z`vw%J1Iyd#zxg5|E&pYYS+ZAyJ@e3J+6>@$?3xP_CL@_$qba3 zmp`0iR{Q9u3dpm#z5AsIKXvZNOfuRW=AD@J4AeoT;;Bi^q5uT8jL{e+pI*XN`;roH$ZprddXQ}SU9nU{!@d z@xGjF2X>F@^P$ynN`D8lK}FdW2P~ooH3bO9bWcrCvn)jXOWNqp654#5&a{tlRz*ly z*b4$3$K$?8L3a#OR-V*E_**YW|z>8Yl=N%E^J7?V5Q5oyElK&e^l zGkjF368UC9z8y!$(2vC4MZJRq;|hvmFJ{TexVgEh>mV)cjr$Zc5neP`T8CIEO`s$XzDe$qt@Rpsn`K# zUzTLBV^47UCc#peRjg=pgFN!m&+M#oDBP#W@YC&gy{qW^8Cp(0kcIpyx?mZxUTDBq z9eRlG4g5=Sc%IDKCaDh4A#|DZfF{4EOzs;l;6v6e*Yu=Ho!0y#tZ~>`bCFbxHurx% zFd!TYc2+>?vmS-sHzi>fNXpnHw4aau48~D2u{teW#?pIz*N{wvHh&e0dDUSrupds8 zaG`gQ8>~NVYX+CQ#+_je%FOy$CI0!%0aQWuA5O{AcLRcDR+1G*R8n2$39+*54Iu)w`gzt@8X7nBo2c*CSWNNtx42BG zO43|}I9Vx$i78LR~VBhx_`p()YWr zIe&nN7wvH01hKM1Cix8HE{05+wFp;Lo~VOeIBRIv*Ir_Ot*N=Srb(3<@BDLM;zUOJJ0(M;^(;p$Fol*(#=MHA!*Z2R}YU_jg5^KSGhNX_a%(o zH@sg1Rv2Tkp2&RaKO9?y->|4i~r9tC~Rdy;(0 zexReIDH~18{S*-Cc^o`1zp$*SODIt3gm?VS@B4d9^5>!~nejS%503$?nOz3CJ!ybx z)2?8j&qQB#Wc-Jv9?D4W^2Ow%va%#;rUM7KHKze)!Y<0sJw{nU3z!Rf8|6U`dH;A? zN8+9AZ>u*rtiQi|h=PNym*qfwPVN8j>Wq!_MEx-z$7$}c7N!n5(F~JJQ-akio&BZ& zl7k%#uG|V5B#|?`Unt4`hK3gX8|1=iobQSb(JjVz`OdATmKgZqZ?oIzqDQ_<^Gz+F zFd!@DtGssw-Z|N$I}9dSCTgAXQ#4pGEhqapq$&BReg3w8@KpTf!%h#1luxf=a7YCV zwi2imx%feSytADJtB;lb)p?lH$ruJkTVn(4NuzW3 zO&@k_A?AfLSVQ3|3;K?b@th*;Q$meN{7n+ulc}5mH+L~=-2dX=6Wa`>3KXCcQoKSwD+#}LF4loxh^3)6WuNy6y^d9d$|*B2`Z?5ksC*xrY$($9Hy9IN;(T*o8C1O_uOxy^PDr<|UfSJx9U&~?GZR9nhVl#~LlgJSv` z=P@YtV=Lt^BvF#6+JjG1R(H;&#_M*56MSER#93Dv-v25 zmm3v_qtY?fMTF5T3R7*&7fp3AzO~f3{bEoX+g$tW(Pq8H_V6KCyT1HY%vXi_S}l3u zOLvGocF*z&2QsK%`LoO(1}p#D$5~d6oA9^(TYWDtu2V+h%YM<9x7H0^aXM}H_2<*E z_Z=y;dmu*8sdqEByxLx59g2z?w;%D#a`5T3lpHnQGw!@J^2dPKB#CUPNbMqtx5 z$qmJovhlZO@un*6s<|^xH`YI&`&{(pkge7NbcvO-UneQEv5k~a(qI*4Zb;q_GZ0=J zK(?X=90k>GE~_ABE^F|7#)yB-B4?knfsDTSL4ZT*3N>cGdQanTKEo+#N;{>J*^j)M zU@IDy)EUwaq&#@-(dq04g!*l}$v5$>%r!qs$Y5KdXe@VTKlfucig2^^RQ*Mv-wG#E z^lD>$Fhh@Yhv%i#IXOM>`=Z-Nsv>9oY_6AtCA=B)%)e+~mUI4Z{?o^<;KHN9H107{ zENU1WsRj;lf*?@Q-y zyh75atT9Z+Ff*Df>4Pb1Z1d68?W_Kbcr!g>ft2%-rJhef0|AzSAYF_9v(eXCx1xuK zmoetjx=N<;e#kBoR{>I# z3vV~q1kUIZ4p-W%+0}g+a+^D^xy(vLgCG%6k4K3vUBsmT0vfnt=KISHf=~wZK1dHu zwlQpJ__34SpnE>rbgrsz*JTId!y00SPQ#`<903*R#mP1@w8CZH#R?U4KpiQ z(hK)R311PF#X7|IL(4()+3r_0!f>*0dFQAW1LV-|Noji5iaS(}rkaiA{bG)o%0#}>I?UOe%=m}P6__MbKO$de{?k`+5| zzbm7}bT`dANTxUm$x8p=C==9k7UE*biwQRN@n3Fo%a}1kB%;Nzf=T=Tpz!&8L9H$4 z8lN2V_qG7gDcHy$se!9?ZO(RoOSfz^Z=u&K?==$OlP$NxEY`Mt@BmDA0W>M`h`M&s z%Z8Eu3cEOb|B?fUwe8BFVPWT~nA>1cpu76_qez{D&^;X@or@vhqgybwpGGm5m2Mpg zqC?_Gp5!>v0B=NgR1NCN7OtPyLE@NKT~F@-CTbsBPVr$OJJMF?Z%i$vmNLWOm%8jib(>RTcJ0mH$F*IP@j zjI9WK=gsgYvy40sCOS^(%kivXd*=d!A4&d4yVkZr9&{t}Dxv4%Vgoz?YXm^k@5jeN zq$11#O7q|b=U*#2-xQ0vz4+!-7Yz)rYu7LcLq$azL&&bgN?cZXTCP9<2B>@HUvv6c z+V1^J7hy55L5&9I;D36h73vRi z(GNcf|8kwIRScCc^e^=d7i7l}J_na<3G@fow0?Hte*;#Os&sN)nPTF&r$hGLC&VMa z_Dn=?&5|;k)+?}?3s=9?&%I|h+|(|C>WhCg=b05s0mnhhZaWm)o!D1L8*c_4fg5v- zggh3=y7Qs$FghCNXfymxUW14?2>nvDg@&F$mIJon+1aO#otIoVuO*bIZM>iTLoKFh z&VE1RRJCuTC!R-yAg5K(1!W9djICh`6r&DRz0{t}rK)ZLHsp8b{r?_@Uja^VT80>&o;el@th{84cIJKg>L`_i!_iOBRhY93dAFA)Vrc79ttwONaw^;8<;)XV7 zQh64Pw0;5k+1GhC)Vc7Y+x9tLg{a&y#^29h*h3;5pEYPzm$W6*?F+u$es}o!?nM$| zIR_CT;nS8^|E6emc#TB2#K~c{Kf&k3zwiHA`TRqnQ%E?0CL^H| z&O>N_;FYPdTze;b;`()LYY_&&-Z+LL5#ohycS*3%r(IlLV$k^Mmk(Oy|G*T>xmzsrP_q( zW=XwpRDYQ=Dz|kEt8&*m7kON6Xh{#TvUMH>1oMcO0OKQ}ad%&6l? zEGuta@;?>co-*Cc`@CJmk=_|3o9t|UOeLzt1UBx?z3?vluBrOR;FTnc|MW3dwLtgR zd;a^e`SfmDe;+aGkA}6S5~qfTLJT&%avYCd>5@iA{xrt!kb}KRDTRHEFJdpf=pf6bTy+6TSRg27UXi4u+piaz35=6NYb?0NU zc&KM)&uTl@c?-g)9n$Wbjrs~=9X95=sD%u|p@Vlvkw`EweHS}xK^)^BRrBKgAHwI@ zGyR3>7iMx(^sTnHN;7__N@F2do-0ii$%bvxwr*-Anj}-olEen*%ZmrI6gwJn)-7bG z7&-oo+b@LkB1MowaOR96foc~uS>}boiaK*?{+8{t=OXjW;^UNY9{<3dmXnz2AtF2( z=w{P5d=L6Jr=xJ{poYi+fc=?mQk35Y^?u;e@Jwaxov%?ZOt(h=hV>J#w`t7#4%xY| z!Ju!nsa+2T5LY9dtDp1+wT(}8nqL|_uPvGkQ>!c&5sqU3)l*k1Y9b2zyT+Bv>1u+n zm#AjLRA=j~sMOM_%?SjWRnw+Q39E(aTcnHH?2(p1P;&%WL6Yk=C1npT(CNJyFlY{; zd3W{pbSzg@HJ-%agU#IT8y>*DCFtG$o8}zuLw0>Dd$S&GD~+!5wCw0WP?sMr_~}O4 zt8WC&Sib$NVq=cB;&zRVI|%c2L-x2bkTp5xN$fm@gc|3|x5vM#HFE3MYBW*d0-|%E zCn}`;3t$FNzyJ4Y#9}kr{-d2QBCzSt&pExU=8#)Iu)U1R&576iLrS$$F8DSb|M``< z>n5)CNeQ0Nc#C!KZq?k_s-*W!Vw=#bKySR{#3b$}6{RteYQ1ya4;l8a-OquV93W6{ z;gFxl$Kzi^_iv#q2KlFGIs=e4H@UmYYoIy{bX{*U$0!3`vtR`gq0}ceabn?700XiO zikMH0SgIegPRGrPp(oqS2@T$xfT*x^?C}0m4n8k#ToeKF&SU5cEw*HetC2+27rlzx zS-T6KzFyTu)M=yi3oa=;znm-dxPSX*ewe_-F*F?e>BBD(oW&WJ&p72<1u=Kqg6U0A zZ%D67{Y@R+**3@7Kxy-jS7qaK#6B_GJY|G8_)5{%8^Bza%CQO^OgIynnU#>7s#LQa z-u#!DFuUKzwlEzH0f?lXjq@{yr@6f4RItGc zb=eeRxZ4Q5vv*QOpKGoz@Wq283Ay)8gnPO)o!NdPyKS=n{6Pai%^U)2eI9c{Jsx44 zi3*?|Tl$p`JzzxKPln)Y-ZwEdCA)_%9{RnjbP=%r9hf^Xzr4YgA2I(ogSg>YU)hZod90Rm~B~0+&Qs$QS{0=1V+2w~h;L+ew$HccQa;3q!l zfB@I@_HpL8N;#l!_E51`P>8geZZ$PDT)I`EI|UBE?lFxN*Jh=)7bw}xc(z!TgNJ@C zDu`rvlIpXStslXGzCwM zU$zqLD&&tUQ*Dzs;fX9!&tInpb1*Ora(^sj#Rvo5`~S%?U8xT_d>QOpDg&v0VcdTY zp+)lr_A9bM(xCkKfg}(AvN@2LTOMEB4zjnGsdvG@DgVXDB88`m74{#Dauj*~t!T_# z?uq8_zPGPOeOs}ceOLN^2r_H0MxKM=)r}CT312e7h!6-WR(^6dzvz9vP>0-8f0z)~Wjk;H$(F0mFr}BUiybalg%HHctwv%s=3vmAU-?#Q z3f&*=LJ-)!y2@4pR!{kdSxCxw~ODZwr{buBHK;3}oogiG3xgYULlLRbR{p0(B(H#5IllAwp5 zZwK^VAX2 zS+6+S0Ea01JMv`{6u`VG|gv!D!0LxcG3aRdT*btTp!q z++1*kuV2PtRnH`q9C=VlRKHYJ{Ue34eF?nXUs5bO;%l;QE_{iE!RjQRHy{>%T{!sy z{%*ANr(tr=!td<~EQ8&({8n#@f{j0Ptkd!Q*1uju1)MpMm z4%IU5B1PeUY<%Kz9>uBDK6kmoj4!5c{bK0s^5g$;44_FdFl70U|7aWUB5l;b+QRIo z08sAEuXG?=2hv10jng$u6qA{j*?&~QB{cJi1g|m{+8OF1e&2vWIi7j>J$t;P6qNaR zDD!_`e^DY}5Vmd@=W(RXbfZ>n$A7P4BP#83YM6q@HVlp5;2a>c;8}k?_$D>=^Ve%t zi>2BaWzf1gFZ5*`F{9o`x@gV$Mb^A2dLbNoeis5B)TBRh>ZlzSj3^XG)m*U9CFy|U z8Tlk#epv||-+c1@K;B)gh)!0;!tvn~J3)f0(jq=a^&7ZbsMS9;Rqzs(I&&JI|2F3P z7GT%`D!NsuUa%~(RDmi60K0rvf-$Wz@>05`|&a3;8!UTRb2 zi9Bpx8G=%mR|)qInLoaM2Y$&jzY{x$1N|}u$ZsxhYq@`cSN&Z%>mUa;$y$^7EuaSN z6uF~h(70pU{(Qsp-_;dDl0w)5LH7cUg)wc%V1BxeX;c`fgiR*w`q zg>HM+r~1t>UE+xmRP$Du& z`a1zz)BF3m)Z_=k!J7nj$I&2i=PQm)yIV~@tzkP3RUIL%9Rqn`1_lP|Rrq762+sTE z-KL{85?GW@>`ZU`Si>wbWNky=khQ4^vHlD~Sf|gw>4S&y^$+>@K?|%~r26but9Z(Z z*NKkUBcx2&E#qmgyPmL>v~|Hp6L1m&lKuKEC8!ArZ}5=fzM%B^%&j>B+9cvT!?T1nD1i*X8%!|8ITwa6es-WJ)_*Vp5dC2m$wNoy6 zj)uXJ4Ur%zyul$JZ}~38Qo%LOLr`>q-bAqEI`!XmYflmRfkj1shAmvd0V^?8>Q)Ib zL*g|woWSz`k9q$Bni_9jpJxD7t8LOmop*bo49bx~S;jCL-OW}FkOWU6;8eIOk1+z-twU8wc<#BmuZV&`lmN8@;Z*bDc1 zFrPu#mF->9FnCXTm%;V>@cgg{F04Ex`SNcd%oDsyyR%Ag^R}e&2HITj1RjkP$vwmXdbk z{*AMa`eb4Fe&MFO7pY%O3Dgk59isfGbeNcC1MIuxOKpo87cPzE&tU#W>ngDYmJs%o zZqK~0n#oDFSXY6DhQf!;I#APD{SBds5BlQ7s>LtaYN1h>m>Q+~3<~0wwz3$XYo2m% z@Xv9UW#94*Uf~0^_`3pSU6kOnw(0-;7}vV5y1%`KzZu}gT=Wk+o9>%7W$b%jW|PrX z7Z1w0|CUzbR}{A4+lpzbn$$Wdv==_4*=#iDfcp?dr`-u+N#tXtaJQ-;B6UBZ9*^*%C9qW!*@2kT3l-n^0hn2vHM zo&R3$ui9B7^V}8I0H{IVEh%zyb+Nc2zM<;BQ9j*t2*TL)|EgqEQJ9+?9lnCA{^TH` zJgGrww&Ui*5+FP{nqFR@aW!|^;g*HO5Gt=96^3C-l{aFWQj{E&|Z8;^Q)L+ z{n{dQ)^VB?0Y>qFQjF;BCHQ}tZ{F^1-?=}6A%w?i(q9QM*ayhk!sA_?{K=jt0#aMs zU5qOADPp%nBE3?Yju4`4CrHM+#Oqz|8X;l~QtSiQM8SU_iSGlj1GXufjMRL7J2@BH zT!zrEs$T=n7*-7DBn=j+1ox`8E#E$84PIE^z#kak5z8zqnn@!|oA&Qpsl5n9A@aG` z#lx5Iop=FKP7z&PBEW`vgg(*^1hZ^m`{OY(g8OeeoTuJ=N??X^T*(9nd<~G|ZkaNQ zNyxmy6f-ls+Nf)5i(DhJ zbcls~H>LekM^wRxnG&HKKtPjsQS+=+z9UXF% zoK)4Sqx<4wh(S zh)lsZ{At75GWN3a?zkJ}_J&xOcjRFgAXVKzyT8SU(?r%oJ6JBMZM*}>JkUN{8JpL_!`(Kdh#G#RdoaEI^#X4e+DM#Zx z$O>MAVZaf~cU~xSP&)2$fqj05dHHL6lAoe-x82;Qh?KPka@h+>?unQswdCEa!hY+0 z`d3t&CXr_N^dn`aNh*rp)MSy4@CS+Hg$4x!B4N6QhJezdPJ02S6aEwAYV*xMq))8| zScCX=Eu&~W*bVXOd+%^4y<#^Ppi_#=|Bwsq{n?5sVrRw6x)lPtuUU9KDCPk2Ay4!| zAHxvr+iifk&b7*aBe?I3XKr4#3YRuq1bmyRF=z{YWawm$GKV}*W@6xeQnbc2SdUII z%T$+v!WF@=`|nPe$-3WTgF3p-Jl{BBo*_;MR0=Csa0;aaVDvqo$4Ipntd7>YK6Y*Q zFR+DqLBZekx4`2}y^pMn^gOLl)_nqS|0#tNsW;bHl?>1!1032zrt5qPj=9KbD7%Rn zSx@X?0oiPo5cI7o4H^x2`HW%yk-@-a=kgazvsK(sDUs;074ZK-bJAdOAEJG+S4LO5 zLq^`yaAR#=WR2HMrIHU4@Bg*m9EqRQv)vRk*=nd*bH8xtLhj}Wa?w68}T z^WR+X`A7CdkA`k;xtV8=1+j85d7Rmvod>AQIk>v@aQSUq6UZjk;@oM#gZy>suY!Z2 zrPp*gW(rUQD?_WaF(fcaukXbA>tjba*EZY8-^Uz&12M2_;brZDt}!eO8*WrJ%D1O< za5t;(9?r3oRJNU7nq04!618&P`X_T`s5qHlS$9t_?eV2-wSAZt^yTKCo26gN&u&ZI zo!R9w0!nkV5aJa9s92Bw;*nzJF-$R>d3XDz_T#iPPeg;?D*o}0A|>W-89k(ubgZie zLMzj@Z@0NEhJ4(9?NIKETqUK+{Y##EsR+0GL-IXxoVGjfGn<{!Jp#PDuoF+%vLw=HjU}>1hUTOHJN^v+)3(fHPa42@bFB&A`=gRzYfjUKb>eV@pDf>i;=qd@1H?jU+X5`hy6&T!Z!uJtIKU}5jH~NLZxIj ze`93=56;Qa=oI~DNbubck?m-Lb!e#8F-q%!1(LR*D;LI90)xZ`X_JZqic8)itY4c; zg37=is8D3pdMZlvIxB}Gtl7r}&IL2HN|0T%_syM`$$i^NgU+s2KPZ23?ZE^KC@h^f z^e>?Qd_@GuTV4tmmcZ#yb~3cD&E^2WbVsK!HjwQ;>18dmDvmv5EJ$=5Nov31ttH z5HqkvwxhVmb^hXdO|BJs7Fd#mCocA;D6n^yVPLtWk15?@WmnU{ihiu+eP0T=Zrj79 zZR-N|MC>?8pNVvOd>X3Bb@^}ZWZZQ+6^+*Rr#;iKd(S%*jA@~^XN9g-Sfnid84fFW zYeEY_4m&bht4h-s)yLgyVfs^_bMz@F;oviU6uubs4uyq|oej!9|Z0t37adRjeY#}7jF>F;y|Xb9kKu3fv*hKB7XZJCL;vsi#}jCv^k z{4YEyJuSJCRbxs{*QH{{L(A~T-} zh906L@icj3%%#ZP5%L(aDx+ei<}%R2lG5qpwIBN{F2tMc^&t<`6Q89o1mM&YY;{ZX zpoB=?Aw;ZJoB$C&2+fPl`Zi{yxp#5gP@cdU7(^dE z@6QzmVri~nzT2l6M)dHauPg(Yz-9_0#{ZfqK?TL6u)4&>Dz!Aj2u+%I?IT}fi6RG= z&{Cqp74wpp7G=Jmbj0`S;oLuzO+8&i@UxlTn(6wJmj|StK5*P5Vmgn5lk(8|0j8lN zeRW%E;P)T~Kq)C&QR7;WRtf1Tr}p)MitXd3C&0K}ul=nBE==-YHd+mNGCjRM7v5w6<4e)lXh-0lU7#oNsNSf0E}% zf5`db_rT@ny}DfexMvDrsMSJ+yAEpD1Cl5ZE-IvYM6#f9!(4pV`U~0I)sqa*pL;1=$D=xKk@8ed;;h%)3 zJg3GZgyXvq%DIKnNW*v0&CDYhj&P$|Z=?RczV0Fsf&{pysbZ4D@HYYP!lvhmKhPm7 zY;df~m?>HWHH%iP%qJWB^dX0zK&YYPGnzB12OkSY zckF`r^(6qOF7N*Snq1y_bSitit=Ywqk=;ystb9Km_sT7pBOn|))q%^8iw)hpgrs*$luFDrX^o)h+W?^lJ z?>$3tksA$#FyYnIH^rylp-0R_vM=~lx15HSbxoM9w=X-xq(52ngj-XiEluA}`0m`@ z6vufENoQSbNv55m;xdM2|GSClQuH|X6*?V4roPh0RW8~edH=Dw2oC6s83Vg;(myMN z@gj{(s;@l-RW|OK4`p|utv2pQ z%&U`Kd~0ZlRqIa~j5@WgW!TeT)9he3ZcbciAD!P1GKht2a>*=lcz zB+FSd;XQKQg4HrlnDdoqzUsXMPNB{QpY<**MxzBfWerNZ8cRc|pCAVk_V4@&ZIgWJ zHMnrzL8&gpA>l0Of0zICvQQl1BT4bgu`zWt*vy;TRlbRWLnMuixFYTCrsdn+4pyf6 zTrQGtdp6H&q17Nlm3!)nFezM8JCsn+PfM-XLaq*Bfhl1-`iB< zK+h#5D7#?JG8R$>rR#75dv_88fx6se305q+1@cjo1Rspf_y?3bJ zcq`TUZ(A#SRqctZ1it2pbl-$4l5}%vltV}3Q%a|p2Sg_#0g3>mI-`P^EQyGUa>jF2 z8;{cwXO|oM_m^2$N^^5-qlAXO8B=EJY{mC=ukK;W@Y`bK6N{7AGGI4`Wgrvlu*|O+ z^skcCWvSh0PH1&y!qW>k`0(Y}cRtAj+E^2NB@We~at##>BICbO<2{VBxW83Y4(CsP zTw68~_k(Uu4_@U7VutLQi=Vi$w`)mY2j=%!QZs zpJR7vUOTxR$n3Y@x^};5CuAwsqWhASeEi)h&sxoBJs=FrWSFJZ198gBMOj%N=k9De zDl4=xJ-APsp9$!PwV>JLti<1D`n3v!*q6!L>hNJ}use5+)!f0S~F{Kzf%Jj#TE{9L}%jsqHea;qh)vD0lORVS4LLb-uta2R!6$7Cs7R9XqBj3+wcmB;vf}<#`>v_1*c4>)?@7$ z8LC^&bE$Mompz40q^FmXVnn; zLE(m(e|K6loZWc9C?oy?^D}b?MrBML{J3P})~N>pb0j^us-G0FqapG)F}QWV#P>M+ zW!!&ddNzNydgaqtHCJ=YyO)cGa5NM9bNKbEM`=Govws~oGbLAcVSXCu37O5p zD@)cb6;v%KW>tI6f2!|jli7{hQP4hOYMoQ^FB z!WaE}YmmzJ{Z?OFT~U=PVHyUx+v$;PIgJ**b-DIbjZQuny=oXa=&;M7`soFn6gbIc zH}POveb|1VLs5OXe(|y9kq{~96J?`xRla;&#;9lcS`5dPTN~r2TRx`Xj{!7?_)atP z$EC>YQ^7k!L&K_GLf6lWq-G>7t1tDe)8?%2r&om-N*>&W#iEo$u{z%&uX zfH9$VID&FSS96QTRDF#RD;5iLjt#S`&39lv)7gL53vyr-_ZNGFr$;cMpGbLDI$(p9 z&p%FH)~x5tyC{qi?zg!~%f3-bj-}_tmxVIN7`BhQW?5iAQ9@O(Wq{>| zZ8Rq~NJ@14egA|@B-Y`l4YhNWrf#2&(??85eZc3#3jE^wu$`Zf3fukdc(7tN}pk3k(IMBx5M%kr&*U)tt#K%Ib;2>T}2J9picnbjVsj za!k%iBeqVoAjR|Gp7rFZ27a%R1`_whP=@-W*s<7B z^Kd^LX7G&rbgWErzna^Z|2aK+X*+W%P7cNW)i+!El_36~o>JijyuFZ_^t180;MoJ}SM&#=12{*S~d&gTcYhwe^IbxoD0u zC66$`DH+LR1Y(+{)9y3}S_i2llb}};-YOQ0-`rLX$JMl>MAbJO4_>Nuq^GV-R`iL6 z7A3%SteP~}1lQvxloG|QXg5>J%2hCeU6A|QpGBVf2uOlinPAHm0ic#AchRt>HR`h8>l;x010HlVHZDd;!~l*IzkZF&6fFnw?9a z;P1as1et|wI@`3>vczG&C_gNXgEeNw5odq5O(F<$9g`5(qUG~S`T$RPVUo9VhgZ!8 z=L`pZuA46zQy~^hK=OOKu(4fYcfJ%7@WoYfY`ME3ki}Zno3XN0UV8^P6A-MTN!CCK7BwdkgSx|Zni$z*MiU}v-FV-BR!gvw@coL>$|r!I{`dZvR}K^ z^}Y}5R`kZwj}D%ibmep$&;cyEbNDFABJm<8_ zKW{RQ>))uDy+Fyohw?NI-$~y}YZmGLqkP;n;ruFz`s!8Rnz8`*(+7FF4-kBv|7QP* zUm>{!(dpAa@~TQagJ2ZiwRXM9F58^zMwOXYgTzO7y()Kmut+M)Da4bxx;bvv1ul-@VOrv4U+zYRjvIQ5#K+V^(U4PR%^bThD~9akF{cgpFbcGymjYl zRU-N7_>=H!M?BZj`X|1_6<2D_E4%aIyPK5IcFZG|LG5RmAHckZlV?%}SnBk2h=_|r zbv&E-8}M_k=xSTO9{`=n*;T}}A5LH!jB-C+X zMyJwJmrIUC-+g_xtUBVMQo^EcC02Mw4#+ z{lh7Pr1sCbe70yc9ZmVz`kmGUf9EX-yWa=x*LoLkV(lm3Kf|^VXO`rtL+*OayP%9M zmw<;D8mGknY`e7jh;~ElkSX1{P`VZm>iePN1WBFbCelc&pjBh@$>%J!l`Jm-fFu+<;& z^g|@zUS{Rtr|y%r{3jky@oXUSViHD6hZ`}Ow~wZR+$tB;Pi`{1*=o6#iTtyI=F!{9 zi=T-koR@`AS7#<+6LoHKpQN}~gH)?ijw_n4D07(dkV<;xaJn_8EIpoGPyOM3QDWU) zyO$h4SwIvS+*{@h7pu!(CvBN8ozQTMk`#qy&iKP)J9N)q*E6F`^$yh?49-)m=C4x* zk2r<&(9|uCx5XHIA=foi8Ey(i#-VY^Al9-##R!`4?_RSP-?XwZm=#@8rt`}?t1ORc zB~-=7K@qx2E1!vjqHY+!GcYdXSH!PUnA4y&-b>EBs2PZ6Fuc_u>_IL*Cr=a6QO5mf zgj;E2iyajn2f1402o+XwoF@TPa>IejnEl|7(0jr>SA79{ZL*MVo&&QV}R=1`~R{vWoC*ykQlp!W0uHkPL^Bn&4N zWezWKpOJ{vFp$%Gi{#(LqXi@DfJy(+mi9dLkZ!nPa+3Pg= zrM}vNvk~Pfot^few}C@>A)lfq77Z^3X(pIh8FdHg0m2U1DTR;H*U8MrQYC`RYoSQN z76am2fku_}!2-!yH`Q4Ua~?Ar!1i2@^J0h;@8rd>W|^!S(_yP(|L;>tN)06STM$W? zX&$3#9fowGJT#Ws8gA_O_0Y~9Y>^vqEMy}J3W9~lg0<$2z_vEvF`C3mq$3JQEPP_X zd3k|K`YOVe{||<5$WhD6)X?+NQ9hJudf0jVBtHt(g>&6qaRs6gWTuwwk@2UfQ26-O zcENfat^5I_sxN|*09zNU-{u;zAHGcH#&i4bd8HVuzl~BFf;S~>(1qx>iP|V ziLG-_J|>$0J^`V%2|x5M(NK#=H=}2Vx2__epl^ACik+rCm&X=k*|o9~d6BW*D+~ts z*4X;}VGwbEjf2d{NrCVSGT6@*N^QA!F+rM8oih`J%6RatYEjC{qWKHzz5mxlDmLoN zi&1(-u1$a%)}s-G;vmyFmll+~XJ$U2v8;qXI5br`?5~$&G+cAfZTUos9EYjau>D)D z7SRxj>rPxiOtGgTy(55q)phs zRzC`^VvYe34<|5WBvd6N+iS#HY|i!Tny+~U3(?jHAj+SNpd9Sk3vd?W@;7?0Ie3Y^WLEDBqr|y|*#7GV311pG>MA5C#k=Drjbou=5 zc^0BKUgh7Ep4@hd7%)abwBlIM93!8`AM_&21-woKFlqn;Td)Q;(_RO}CUDsK=5-9w zy)*c#;CKZ+-hSh0l3;>^^OX@Q#KiE|dp#m8wD6KJDC`x?+oW=~+s+`h?Sb|Jq?+;o z5ejt9$2L;+dIasl>3yXK&w8`%8zdArf(7TMWZGWnWkYg@#B@lU*}wFz=ZxtS&9joq zP9AoPz+0TUbjSkSWGm&&$>e!#XWQ)y*t0~bGH~IDGkg4%{|5e8VM}_AG{S%H9^xHv z#qUJD!YzUJSKNv*;01w53-z_!>sl91K$a(N$P$uP&3eb2_e}c_h4mHt>WjbilgBbs z>%>0XFS|41%0i1dPFHJV%+kBUWize&s5Ms zp4xjo3Y;;3Uhv&{i5{)Qal#SOxe9cy-$(6xj(C~l8P2>_-giFvsc1Ov-TI8*mmK1K z{iYRvN~m*GOMB4!mmUa(5el8(kSS~>POD|#Y@Gi1VHlr|rQ%$pjSW|hF#b3tq;5A`M^Y9Pz;JT_1P;t zHP!g2}loW7!~EiqJubO%6SQ>Ti!Kq?mQj=x#(&fHs%_LUlua9 zT&Hj!H^2rH#WO$2Z-u_@t`l#B$aXfSW!a17T&p5V{!do53}aMG$Jl@-Gm>b4sgeAWkMufBqg)WhA-7f|f2k=2Xf+s@2E5+|0rg7# zDu`D7b?FmV^)@dt(TuKmqskRi0Zh^&>XJsYaZEB=mp?2+^7oVr5rilI%-4lJgg6L6 zPCDTI-~%){hs4e^{#YiQH{~V+I_vrwuk=q~9j|QH*U?PQ;3YyG{4)<+j%!{jZAZgv z?Gv*7pAKyDe371C!oo<*_9yZ&rO5QPDB!BB%8H67F|Ac`#P#-E7$(-;!m<)L82Zx4 z4i${dK>k__f!JJY{G-x)*+A$$d28V#L#@m4g= zO!9_wHePk}GcDIXN4FGMdjn_4>8DR6!2*TD;X8AhJ1xtOyJP6>IAbj?OHEW?6nJSC zNiz_$Qno*1mI95R7`EXs7^3UwPnF3y4V;{Qk|)sk)`5dEl7aw4)h2ke$G^y@*4S)=qvYW>p zApU@n5ugr8P?9Eyefm~R%k}`F-v1_sK*!r;-(0v>P4`zftTTc)hwmJvRYH=_l(BpUl2yBZz?z|6N_k$JBI#H$$w zR+4sr{H%3VA|)t_kQX<&LC_dTiUH&N<(2b8^|~GCCsv5I7`F=FbA!}{e-HlEq??6O zMsU^RQ_jMK*0M6IPN2Sj?9SI%_#JD-=^;m4^{ei}U#jY^I4#ihJG7S!$d17k(+j;zh5Yw*C1+B|kJ& z9mVjyJ32`cu0Kb8xoX^d&W9Fshim_IFvS6aFe?MnusY$HUr=ww6oIXGc9!5+H9={5 zfCa^b`sSn$+hL^{r@Zat4IcB14ei(_WtO0U^t3_!E_Ckg3r>oS+VZTAT+J!YiCDw!yW7U1>_)KGQ2>*&fa7 zDt)H*8Y<}ccv%}}h*G_XIh)xep;>VpNmNmd3d34=_S5q{W4{A${pEx7waTg-@%DI;>^RybuGva`0?x4z%4UutmY z3li_rW`jX}A}#hB(?>Sgc_-S_WNv(c{Gb{fIADoIReNdlTIt;*^4Y`vD7iU8ZUsol+glDRP3ms&zsa^UGA=NbV+80*H&t_d6b z(agA%$Dcyw0|NU2QXG+tXH07gzIjV7D8Z2isVI{P^tEhfFGbU4HF{k7|HmFtcqgLn z8dgOPVxvT96FLiXkY>)ARZ_u&U!$CM`%qJ?@OaIPezAx6E#cGUe;?*HMB!0WZkw$M z9Tti`01R8eS@ow&?sEM6&#v;MM72?lHWB8Jp*$e;R259-o}0G)BC-oJ^2yU+dfFyaIP5IRuMCSOQi91L`QxnQ*P0%+I-ryf_?Ll8ED=_+lh<>eB_$01pojUxb4K z#n>UN%-7wNtEUMA*daFw7{%Mqd13>RHI)~C zpdAj0+n-Om92})3-jJ0XC+&ZunrL#?;1CHK)ns)V&YhvxSob|&W!YzDz+P-Rw9%-K zFOg=9T1)p97la1E6LOTg-DP{kfSe5&t;!kOUg-f(Hu&V8!Dt7~lCQcPoX!04mTNS@>SLBVrO}%{D3$S&j*B*RQS5$OG z^U1}F2ywsC1H&<&_H4b_N!S0DfUjr)xMq!0fef85;1$E)=3c>Kr=_MTY{S-*?;f#v&mo+KXs1j;U<;?icBaA zVKpqDBFQq42J(uQ_2C~B^9rbI@0a*yb8$GM87-h1F_F!D8Vc`*0B@Jjl|lB(zcWrD z-0A11DY$~hKk*--7M&N~@>VkltLpTjZ`pP_W<)uaXzfr=*uS4k{sS;w&(Wy=3^J0g zA3T<@5%|YPvfYbjHk8P}SJm?Hr?DqwO_c4B{)A{g4f8>V|D)bFL?e0a3ggR~*qg0Q*Mt|eyaQd1*8 zD*DG7=ty2JR*R6F(OhG)b<570k>)TpV%aB_9X(fwnLS-I*Pg&?3hd~q8ISYk72+EE z+KI!zPZxv(v&K?i@OvMD?I;o}a`WR+l}_$lio-a?!7PZFZ+I?QKxz1YUcz{_gOR4K z&nuIu&JAUgSFdHS01S$W0)_rv2hkNejbhA@!X-|n`Y8NQ&+~Pp*0pyn7gyVwpeXwwhHeo%ym9Id2Sk-$6XNBC& z5O)0Pv#{qg<{VVaqFerGZ~x50cb__{^ar0|)9`+NDOdkcjJQ{DDRadQB6z$*>Op*h z=RsK=XC>>L{V=rbEinIH02ad=kZr&P*1B^?|1cwZWy;F<33;;-;h$TKK?w|!RL1*f zWtqSw_mvCK#JBXHJDQr>Wd@b7(YXglxYp*|sGm2Y z8kIpu^_`Yh=H!!bVN)q|VU?-c-E;YGh!Z+}R`0%_C=R}iE*46j(oNQO!z3Xmh37Z zJ0>INp-=_T@V1Y_zK{Xse1pITzG4gvzF-UGRBKbrY+}p*0H?K;EEE2mtJTN#d#Tgx zPkzJM4me~@M|sV1$H~ecg&F0HOt0!tsB0ejPyHnS`LvG! zE2?Zu;o^^JJ~3)sQ;rlZ9gniH2mde7=rB^6skw#TgMUD1_}1?!8TwQTUn^#T_FV7KKqf(QmjiUJU@7;U|l^tkli zGV?YTVG_>&`U(H$^Q!S1D`Ofzr`I`A^y$tj6mMfy0|bNZt3S~ppYXAaf@<=<5Ey_# zGR2s9s#z+Ig3!#!%`N)a9!xm4Wc(20q{ z=g33K2hQMV)WL5{oV}!z4#JUWEpuL?J6BL0wCX8ZGBQ&QxYo9qN42vq-#6?4;a|3T51b=QXp3mQ{`a+a7X&>Sam2vsTt<&Xe_|f$VkofH9q9R4Ei5UFiGN zs*HR{cyF<@Y}dx8ef`^(s)LS~t9XZYo58H!xyBhi_HK?LFta0ft85l>d;S$ED8!z-cS{+;Z%067_mG*c zxa^8CAcMx&ote;|4&4tl;FqSr05?z5;A6b(vP6bF%`kxWw@;2K`|XP6_kuBp*y7j+ zZv)Xx_bmH|~;wp5o=IRN%5sUE738!q(?-1(H`1rw6ferCU z^loniwyLAy{QJH1_s`0WnQt$Qi?bJ!3hFz7j1#n9;Pq{UPzIGYm`ZI3qW%_OH<4zs zC!b~P{^FM7AEj21o74@jUgLXN0yN2v0NG&FY5BQ$o;(>>3BA8J%-{?GbDb3=htfgN z@cj6P@dlUj{LqWad3)siXI5rygm;wOo*d`b&?gsV+TE2?kRBzThhAKy)E$m&o@z~l zCBO8$MRvNx;^5UsE@C?*$J^2fk7)UUyVNw}G%uxxHg}Yq=W}#D`dt-}#TMEF(f6}D z7Vdg0^-4Af(KDoB8q3L2VZd$qVx0?NVSaA^KU;_{sp1gQr?;Qms$11a{V{p0P*Q-0&bRrwRPO9}?oh_`si8XfsGRT3fwMlxpm0{O4WF(FBfh5Dc(yYV|} z&aHIBI8N!Q4bYSkBzvqzW zt}82n1}?_i@7|*cd1pqKopJ2pcNW>W<^aPHxv)-=h0>1v^MF012)f?UZR}{{a=d$z z{S6XZ&>5sMki}Km3c?IcdIVjMzDYSaIbbhNlh-j6A&GROrMK_Ct(ipDuu|Q+JtFB~ z2h9TT75@fp_j{lH?ZR7$tK8DrmTUxPB5e{W&#^G+|G~Yoz|^AgMV{iOy0k*)4yoO# z`Tf+Rq4Kt0_IwZVAH$Sf#EJrj+8Qo;Qt}wI+4%X)`bv!h6Ekm_MvVLf=P}bXYf+=9 zCW0;=1oz6U;#%FDquxvN24Qq+OqP<|yWfGDeR+y=zq<@9*;;rnB~|4;u?R|}BRKgQPyHvDNyr$gMYBU_4|O(+`+pQHFjxsQF+ig7m@c+| z-2Kk=OB2*mb5+-$UcG)3%@P_i{9`SVeing6fr}{irbnwn_3(V}DPf>XdkIO#53`Jn z5q3VdU{)8dg%DHu%paGUHu6i0Te2T=iWb6#@s(pFs$^5`cIh+&pSKH3P~9V(UF;`2 z3MVQgFcgLwY{e()QG6)r+k5EcE-4x19)($;yoC_#ZVBRiM4gacPU^a6V`2xGeR z^RH{Y0ktZk<-qsL7*RWgizG~Du&mwx8tHh*gMg9brQ$DH%RkyRKc&xSHRrr=%O|yx zyn@3N(largv26%rTzNU)o2hC*90qD}5CD6*4dAOF30m_SyOvn*;Rir(5}mqL8t=R? z5(b8-C{LNrv#^e`n~BaWu=VV5e%O}2^1+m}KQ`*Vqr0ub_-JYOQyH5#=eCAyqWowi zmp~;ZQV$dOkb#C0?eH_8cE2&X?J_-Zs>t`g1yHBIuJhl?4d~prc}8HjwswbRRxIDn z1nuf4y`<54k=HIp<8@FJinNaBrij7Y84$~QQF&1-s#{erYo4mmCr-_8yMKcw26r*zd?! z-*Sg4iOlG288!P;koGDSZTMHFdy@vuDq$DA`|n4woS$R*7ylAfP-5MK+}?v-avy0p zQ&X2_%xtVg1p8YrshbJB-=Y;NxFH4D-cDU#w&|TSi@y;J9>}xacTZ5tHCwz*mOpk} zxgp%C5cd6Gr|zmaNSiz+1KVuEnKo0f@)Asap^`%rx!0FXu9UKO3EE5(m-gX<-Ga4Z zyx_h>ax6Qru3Xj^10E}D9UV{zk!j$enpFiqnmd_+{}tn9v|$Nvm{` zRET#zTyXAt>q_TKE{_(W&ygk@PCM@tw&@v@9#M}Yk7d{M;N{!#@ap=@Q3JORwE^Kr z0UV~mEx-IJvV_`EU)^;$v(D2};x%p7vvapdE_oAaS1W+e@TsCPpHL!&q@It-2NpI%luWN8}H6w4kV&UzVW3(MdtS`!jb}B|#X~ycCtH?4^^Whlgd(C@~j&t)m8tpcd z=CMt`Z+BX<+?vqWnn#C7zl@uso3AZ3U8eqno%;#D06BE$E)P$r+PPaDT8e*hXG?}U zBla`P9=_63ET)t{`r*cN{)*-QsgZx{Xi=8wG9o`O9)2}U>!YZ^%wgDA%yVVyx_*7& zX(|p|m^JPCJSaJk*tH9vx~z?G@C+99@XRFrQG2QC`ZKibS4+?5ndOPGG-9!? zBRyy53;wKEZwP5v0__VA_qBP&(H39LHH4TisU(#mpFwP%`4>YW%;xxR%*Xg!+O-k) zoJ;<58U)x`oh&yBDNA#oJ6=?qs~vvEo@j0c2QiT?ec~^3h_oKa(=&tyNAb(Wwm$b&SNUEG}PD+ zQnewC6h^<128#oPYslcJ z=G6bAhO|~GY{Fih@omx0*hCXJ=5hsbaD{Z#dT~<2fo&7izccWT?aNZ=c5#;}xApUP z8FT)R`alkW6E`Y4m*z)sa@6(;u(W&|Ag->V$KhUCAY+c#E^ zU5M&}gp>WKqOc>mj|yqi24G7}XS|knwC7c&AE%ooN^w|aTGXF1x|bYR&FWw}mP0eO z%!jAfn#k=4sBRr_ayCI9UJaU3^yt%LheS!$FyH3Q&Fzu*fcHW!jTYf`4zT}OsxB2B z!z8eRrvUo^Rr4KjJ;J`m$KHNX(F@5mUXBHruz^uAI-L?{SE_Wr zW3-4rR_)2O$@X^ddbGQ?id-`@i|xx-TIo{D)box{AAWv-vg3$-GCHB+CEtl*JzcM( zLuTQFeWj-)r3w(yQt)raiN}cMm+P3TD8$8?6@gLo|HDjebhXpbQF21_7rZL0a&&p} z9)-7jJ=I8P!Aq0ELz+daiZZ*{*!V^hWkUtgbz_`kDb`zDpzYNd3o|gdtUB-@^J-gO zGb0O`@yy2?6ZyULk_G4ETe0Zsw@X4XhAvm>IOE+WgrnG{sPR&2-8S`6{_n|ijH8ex z*6I7V?QEkSlu3UVGq>h*CLb`{rOS9K#mk~+7VmOZy?$SSBbU?$Zs>n*_PzurR~%PP zc%P*pOZ{#kj)uW|bilOwUiUlY zOVtRDQgmYMq=D5c%mK}O2mG46m$UGU1&2eY1>RXfgf7Z$${QE-Z@oVw2$A^7Zm%x> z^0uxd>6XWt@WggviE3FGA>QLUP|f)Xa>v3_dewM;Y5y7H#`K{j6QRp9*%)fm zxiCINz3sb`Y3btlw*&fa)_w}XB;~-Do}d&L$4k8==_On52@Z|0+IRZqJqPyo{we}Z z!+)9ij*W&2F3kLf-%+O-{Wlf|13$uQ1zRLf4pr-0$1{7)yE0@ArtXbj*O()T^xc?9 zTaTh%5k*MAkoGb=aH}XHRM5z)GE$oMr=JGk0e*fBQs<|qr!gaWn)uQCf(m075YI!D z92DWakfDT$eA3&=PI2M8{!2)$SA+JQ8KbbP6>RsTC?FYRI2CXI5h|GtOYHh0M47dT zaf+}L!^KsaTQ*iEI6)W~H-8M5fjv*g`A&0`F0@ugA72~q@#R^8`a^rRZ%yL>TX5gO zua2n2^iG~h?lw$WZVh^1v}?W^CGV1K)Bhs6v&yjE(rVHmcxO60gWRRlkA)JysV)=mBv}+R&#(P{d0Atx_gb6k3U77G+k#Jv z?HAigeP4%9ESKaTg-bgs{A29kO7%S&9O`&f8Q?YsrVn{<)%Ctaof?)+sLB_ zVE_0O<3HMn?{PQl#c?{$?rY<$)Kb+nGGV3Tu!JiRD6hEVIY00ecRH%NmHFs9b|!n? z0~du>EBE>9ZglQTXfWE?E@+KruY70*EH+dMmC~IKGeMnabo@-dY;No_@E}JuP8t38 zdjb!n$Im#bEm`qfJCtFw%=sH|^70fc23T;r6IQBfO{ZLv9|L^Aluna^;gr~dN6{f) zsKA**BC>$w?N^G-G7(|ph>ua-IvgH+v94G0kEm0Dp(&2blfTH_QEN-a_T>(J+f4z0 zdne@}P>A5H{y9TL{vYEmF=j=uUz*(hi2kBiK}SgHO`jx@-ext;q>LB$Jh@@1K+>}b z71h%XU{%7BHR9a=W*?N1yuGVJ&~RXMm0 z9k@Qwu+N2a`yiNnp$%Ejk^N?yDkB6$(WZ9M(jYV1TXR?o`NU9$HzKHam-AmfT!;Kt z#B8)IOWD(G$ai-7n*A%YHfviIU4)TVcO!=*?gquJiltAPvDyOu|FI3=BLiQDu5Zr&qm|A#hxdpTrL;m80K3H&O&b)6kd%}}ZO$*c z@V<_-dpSV@pAZV`x=|< zE!B$@%tiF^U9(-e{<+~0_El?@f4XvL5@3!^Bgz-C(o5~=*t~R!N$Zzl{Tj0smt*<& zdO7mmE?Q&{#Mx05dZij&` zQ!wllANvq3`1Vyeubx96KcPpyHX11@SInt$X#EPzuHFqeYA*0_z%f;y6LGPFi z6A+zlSXX>q$B6-klQ9BO z^7{7q?!}Um%!hGLd0F>Tjprn@gid;T``SCO@ce{yo(er92vo6^5BVfkI*Pc`NR~04~N~Te^P>&vvMLVWqXZjc#xv5(!W_!@oDXCT=nq zrFh(SoJ9DqC5zI((40xs(rXz76QDwn_-Atz$(L*8LrZ7e@_x1@M^CZ`zN6%~%#l-q zVbfsdQmD~tOH-ly%lYf}!UYUpVY4PZa#2|vFEqL|K&IkLDoe0!r^#Z0&Akbh09UCI z8tl?8PlMq|8Y!KLl6KVV91qt94VlH7@a*O|d>PY29&QDL!rz|2L5%wD!K9|q!@N!2 zUp<;HXmuN_)Hxu}{YaFH;8m}FUh=8{mSKypyE{{;TSC4aU4Xh{U}!KRS?5&qT<-bQ z{qk3*RIGwb((gfLnGP4*+uL-j89j0pPd;tSj|<+9vbH1<9onQ(8A>V`Y}Rsv(uZE+ zU>g;}W^hq>!x78=+UX;G3GpbPtAub|J`aqxs51RiyGBKVn1XAmdkc|$&k}h^bWw6- zDexxI?3MGJ%@xs|&B`9qf~}m>D$lxPvfSO2^IU;UnBh~KV@`LOjM)f^(-+-G8+o;1 zCRr8N<)(^@Xn{#c)!w%i29l@q89p zq>d*sOkp&m_jH$NFWr!|cRq?XAM}bW+9Z>41tq=xwt}-;EiAu}FO1>G>Mc8bpAI-yy5}M~>o^gWzHFUYwVlj?N2MKm8{EF0j$5XAEHi_zZO}REb?sK-Z$M25G zAQCO>MagTQlWX2s$e0UwaJ-FOmpVkFqob>nkuJm=9g9bpk`)oNQ*Ae!Kas270>VVF z^4I=8_YhW<7Pvt{)kT!7t&?&<@%4bMGOaZxx+=~(UVegj!csS1w=Q98FLcuO1!L!I zCGAUu)+-b4!5`{denZdlpcud`9(yY^XNh&&a3q&z zIVwt0CsnMkE@y;ltOJR|8w#}l|Fr<=o)oRy5vI*Y2?>n?5DzTDZ@-*q(3P6(Jl*`n zOeR_Nn?6vriSzPJha!VU+b>obMpdgfO z{h>G^T^h73IY3-H?&d$Vjjcnx{rnnR30;SJP2Wj4YqQ%$DVUT|D#K#w)~qsWXmf8? zRGQyhF}R#L(P`<>E1)!M3t_mk?aG8brP%lcC94)<$}LTR{qRb4&T=nM5k9I4;j&S% zs-T~Nzmnq+zjfcLA(J~o5%aEShd&QAH~B7^R)fa)wSvss;ZEk6nC)w14;_vpX1~Gg z3iCyLy)LIks`rR&#VH?fqnc29zc4$xNr=NfwUpH#wOO+9 zpd2PsLCWS4s3}(Gv2G$AbYY|6N|E(eJTq%s_Ch6u(qQ4pr@!6(E-YY%Jb!1n~NGJb@lCOu|c z`!l7OKDuW;zZaz0hi-AeW*N9PH?)+5!5p8lsh{RF(m7Tdz{+3^mw;=3ulwk2Q5@9k=>ywm=kL#^ z?7NH+r}eN)WfWsk^8?$Q0`0|r$~4k`nILO=6iZWi(l~(Mbq5KxTgc&lP5k(Ng0(=m zQn&nI#uE4DYu~UbAJt8j;%*n=bjt4tkt@ouO2QC)23);rvUQ95-2FSbP)n6TPi}38 zFL{o6zb*&N6dM=+1zzZo4Vq_Zp}a&jw5!~7NUqcq8&s?yMn5c=oj9ObJ@)1+*ve+k zMgsEp%Riq`?(&bzFOW$Bg$(NsDG}GJHBvEatrZWf9vTum?7RN%xHpV;q!>G_G4>)k zawyeOK!_MNI>>v0sribN4YDYgtWCyw5+pR5d9}nJ zPn`C+lZd)$OSu$ zzgQBrl`N5BEh_f$me4*&`N0-bKC_q5;5Sa&29A20=sWPZzJ~r=ls;}r7`Qn-Wr*lC zPY%2VB+3kk>4{;!I(b|OIi)~%L+%ayEl@K!zg|H_)SR1-(>OVBJ!tE4pBvx!(3}Kd z9V-m(l9#a#Uv4hgVC>guwYJb=_rYf@Avnx#7E`FBp!fQg`U}0t6+b zb$%l76D0ml;LBuT+T zxWl5|ut-&K zXhIi)4_c52c$WNMLglvHhoZD$5?k(ImPdoMy4}K&&v;hi;1b;tc|U8tb1O*}zT>Kb z@s+E#J;JBKsO-3G{}xY~VN<~U-4V#oD#bVivQnS~l9!j1l47;Aop=nI;^fEk@y0fO zWE!vhh4*h;4pWSoCN2NAT_ZZ7aCg0FuT;HH=PCZP)V-RX?5;^*6`;KZh~-#yu0B~n za@1G>p`4K5%a~95E43|OV9CY8^MJnX04Hz>NTnt%aOiHxwGUY}f|+_gRTj0e?i?l(yJ zO!iF^eDO_8-yEwrvX%zeZr6j(2n>C{p-tN;7;au7+T#Qxo=ap&OjEXI5)*1H-Or*_ zqpb+>BH1rvF21y1LeoxEO@A!;{J?k?qfoJ6jrE9lWyj?iktBBL74&V~C9jQAr=Cy&V3L!ZvI&cdjsX$j!ktO~1JMU?K7qvC1@3kxJPbY|>= z(#sif1YE`)#gMB)W1)M>jXWL_oCue`fso!@41@-If($1JCxV*~K3fEH2J~eAIz9Bv zrv7FZx+T3C`0uF=aXk?Q1=4LjI^f1HB`Nth8iw2N)#m&rJe1SwmK$TMME{<;L0Q@v zZU1ZU6m~YGMyCL>YKgjG(&MQOzpnq;iSivu7#DYhx~#*{-2ccowxdR$wc4d|QHU%Z zrXqnQPC5^--MNn~T67XN=uEm}OrM>pFDonDy{~?DuYG8#Nx!Z>DjH2=gt#|{((yje ziX%*TQFkjh5n`QA;+IFWW{jn()@xC0_SWLstL=f5LjdoWB!#PVe<3kijPI_%EmPl3 zU|K@qmUhIFX2BzlYWH@+i%+u_YMK1KtKiC3%WXyk+n}D`r#Vi~$+HNh+4Tpb)J_Bm zLSXK-FAr)(uQZk>NOo@hVS|e9LG&xB`LnX0`1T7DCNi)rb)hcJSs*f!WLFs>+xz@L z);Q7qZ%#p2P8XOd&mIH`KOVu(b!1XR!|ZslO)5D$&3y19mXG1@57bQ#JR18nq;Y6! zt(%m9{_H>LbBM#1Yppb|E*~0mgtTJ6g%hg^TygOC)Q)-+ijWl>m}wVfVUe_Xoj=@WA;uVRfMS1C`64*8S8V@!l7J!Kv{JWj^*HGex~Im*>_InXlldh$j)1N2+n?uci@w~|@=;8}JvB+u)<&WCmd-(FvT zQhvuzC|bib`nI813l^t2_Siege~iQGh*9$k0j*Z`)h3C3*w&}`-(?c>5t3e=%r&_U zmL>34aPqbRS%rTp*)UsM{8c>@KfA_E&-6Qq{Xj9@^8V0Y1HIDub*WPw5#tm*r}r1^ z{&)FFPR|$0QS2v6-gK0Ec2#J2tEw0#;_c}56AO9be_)ZfEY>$`_QZc;E!{%9MC4Q6 zZNg&KBC0R7fX=ghWFf;4{4a?VZjq-C2*$a!QZnhq;0yhpC+#LOP|_n#C<#>kqmAPG!{8fw zuUp)GaVx3x;cWKk&|}>3p$-*>N-30dGFl(p9FQEx)j6A=$uosDN%l;RAVH{33xxMR z(aP%ROfC!q#5^EFa-FN^EEj2-_B6&K@c*)rj+(;ex;4R7GAVeOhjtL9Nu7ssjFA=G z5!s;jd89r1+bDeW3rqQugqiRUr@PZn_es=8ROwfhZr(cOaz6eAo0F`6$RGxGe4t0J z#KWi5a5Pt$(seW+sYQHM74h~HU0)rcJ0nZ_WN`HHI_pmX3yWCEvX$bIpR=n5iDU_b za$tM$%4AsBN95w5&Ll#hRG18XJbGW*qF#_?%umHmrdGc%o_;v&*FQ!e1Le9c8K$;( zpvEP~9c~MGmeq)px_9Zi(~@ZT^Mj1k4@BoRpN^`9llyV!aonW-~)%r!64hD-!5PG8oHwa|q8c9t8-uj7&9doCMVXz}6<%M_^~ z`GA3`Zhk)#wd30GCV%5~z3eOGo#>sW?ez1|T`Dlx7Oq0|b?-}Mx3IapNw;}`B7$1e zcRvJ*!*B5A(nn(WexFaKI<)v}$No+t_lZ+knZw-^^wY;5v7&OGRTJ)PL8gv2@5_hO zXFO%dJWx5}uSb^E#ju_(FvW8VE zltb0ZPTx`x##e&)Xrw}Dh*lK%P>!-+j9CGhaa(5X$o4%($$93@;IT;=#g`>9vu@4! z+cC2zm^aa5)>f6ncO+xjaEEZC;oI1-j_7XSATaQ+xM ztiEtr1e04$e)*!YJ)Q@3bN%W$kojb|O=2lexAvjgC>tB;0s#)qlxZ11n6O)xmjMh#43KmeJ9Nua!1QaD!W$!I zGG2$!u@I(WGB;|o!gNToWtUOgC9%8zyCP6j>!*+rg^Knms zg%7sY&)AlD78p-Q49=^D|Y;KPf5L z!@tv$qu5U#9=M(!;Hy@vbVw#SxrXFiWTp6~+-dJ4)-_FNaOdhFX{LU3=*3zWt7itk z>{3xo=<{jo(7@U$t#W!>T)(%rFio;w+fWk(>VR>E;I;Lm9rxnPp5g|`t}wm{B)1jw zuopP>>XZtIKOSPmA5Q&ETWp|w+te<0GRMX1V<;bq*@h?9#ZoIUyE8$!W)x$m;}I3J z(3fEn#dIz}ius8$@uwVXuNB%q{XrWZ&x6mOK#kNJ*24XDPcVj^`Ne`~uiT(bku(%7 zA2kNkvrP{>&_hT^tML|nn5zcqTc2q$-IIq{yR)k*o>){CaiHOX;21qYK1y{$0fJ}b z##p4nZKhmy;-$~BZjxm-Y3W8&d&BaFdJRS;;caRrC0=J#m^+4w#THM*Z}T7{Eu5(&|;6H zG16%BJ2Cz}zFDe|>jjDo4sP}kp-KYEQvK?OW+nEF=S*R4m0re9xKS};EoMQ*(%uNE zQAacX0$CYmQOa5hf*RHRMkCU1a^+-5fM>E(TRM@jz@hI#6^>E0BQ&Ej;N9@~Kz*Fl zi1)=A5$}Fk?IVwr0J2z(aQV9AN5#<0w>BoB($z9q>87PW(u)JyR-(8*7;1OjA(eTT z>X+=pFAYFN`OTsfjNFx*p~|jP03Gf~=V+!cF((GNs>0iD#$l`R8J-Kg;>yNKIrB!fewJ810p) z;7{%O9>y6QRuC_f_bg*lUlgTRdbKwFI5hFuO8~~#Eizf1`uC;-^An5B`}WsRY_wp7 zael5H)EZXHcpqhyc3P8u*><(N+^H(vc}>g+_rAno6?CfoA5ot|45kZWY24L>T4@-L zFXj|0s~j4K5>96perT|IzD-fYsr&OCYO5uo@*UBL5>RyWMm#u_{y(1HGAhcpZ5yUj zy1Tnm1_Wv8?vRuiLZoYuZYk*=q-H2-0Z9=MX&6#K1*Jixr1U$k`&sYz&p)i?oN=7{ zzU3YE{os4QONnSwxMV@`8=geQ^ zC?;iZYv{~hDZ(OzPZ4RNssH(bb;I5itX{30xQ>5KceK?NeIB&O_e_6aduAqQ19B;R zn}v0=@srg4;9@kaMcm{ z@TH?Jm6rd%^1#Tl6#v5TP#|~SNIGqm0CnmLnh@{55)4# zC1zZjj>Q>56f7FdeqUd&7+ria0U;@t%#<=&%_i)-*05sin>I!B7T>`zxQDJ%>mgxr zM3K03>q8YL!pu#6Tw z_)LBE(;9vZ!^n>)AZaX9AL^nDb*cB&U#5u z7EeTf8$AsUU!^8k*`0Z+Ou;v@xII9LgxAg`jeWi(FM`dVNLK8jUUb>4UdYv4(evw2 zuRUy#XjI@Yf9^OGeXTUl3OR3Q?`%BmQfFHYZTB7IO2&!d^%wenm)d&*+MC1f{oX#T zVpV-5=U<<3BBy#7b;TH}799K=IBK>&=Z*p8oo8R0Ot(%0>_OLUr&v!N9}L z#FKgK*{pCEE^mIbqt?xrNzc8GxB4Uc5vw~tdx;>ZPhQD`qceA8{1uRA$I@D(g#|TE zCk&af?|Rs(3stM&RppeuXwb$Wx=DrcHrG&gBaZX9znQMKPfycoeVw}s59$v5|opA1Ph?{V^ z<|0aWmKVDDUper#6J%gG!|)_?KkUi>`X4X|uZnq1B`B8p-H#X^dN6Cwt9Ns(_L4bl zv&!Py}AOZ?l` zX2D9$(7{2{#!=v)D}qt-tAw@lt!DMwS^Hk_Ci+lo{?2TT%eiMt}}t2=t?Z5wh$oNUK5J!wq_GRHBEu z7D_K`Hg<6By zPOlLr?M98RQv|i!HfP{(OVE0`W$f^)$!6bl!$7e&(*tu-lkuAmp8#?2X1Dk~p(jZ0 zbBfcl5Mi_6pa0>|ZUyw-;}@ z%f3TrQwn=6I1y)H^g29Z1nc9uZXU0yi;C@1jQd{V=eL8NdNTqsefd?g@=R@SOy;_` zB4Znjla{Jx#WF4 zTHjr&j6KC0^sVku6LT88_P8wuT79!NzsS~rTD+4{5OTboxM#>#WnL{ss+Bky?I%jA z@mewX$^MMZDBmQR%p`9;`LEOGkrM*Fk7d3ZbIH<*4e-Jdb$Ze$uRQwNqn+EdK7voa z!u#bPKp>lySe@{I2|h@ocnw6H&Ax_(@?U&6nJf4e96n!D^%jy)&cjXcd; z2EH#+bLBHgx<&{BR+{)m@b_{hGe7DVV7}G2%H!cVo0Wcg7SaE2whGU%bLlN?q)xfB zC;2Wh3b_6*i?zspRWvx<*f(=@I7V`tB_pIa%IRy+6JUDk$s4a`5>yp2dvBQp=IE*-nfmXbZ=2E`%%6> z22)n*#-ylcgy1x&v7OfAVZzXZi4l|hrx`d)2d02#Nz}MBEa?cC-dq`Z79aj}VtSfN zG{rMcZSwp5&5hRYW4uzY*RLI)cm_n7iQoJ)w)t09ZY6i+ht%?jbz#z=|Akxl^$R&3 z0*+ZJwhKAIA(x@WQ~Y6RUN#xQdQT?qH$MmmQ>55L2ue7{602 z3WM#S*U|lnaIyKJ?*ySLp+gg2i|;3P;$@l6BMPGiPJoZqK-ydTY8B)HBKY1nVOfjzu6VX! zy)${Y=jWimoB61#&wAU=2t2Xvy?>nMXJEU9YPvYa^WL@u_Ri3830gx~AckC{5K>j~e@YCz0W~IKg!e7v(< zf{hL>JP%$%(E7!EPTPa(*KZZbl`p<|WSD93XfOxvPF!}bLyhFp(HftrCTWzYdbuI{hz}!{F?b zSGmj@DoG0zI(VroWx`?QuF+=`h8)s7;uBPjfjP97afxlvPmt0$g(rUeN+S6r{^;e; z?bid;{S;C(Or+#^lwW_`{W1KAhW{&8AJ(tkqX*bY&&;!E+1dZKwQ+IFOtQuF~FQ@ezZcvTVx`(31>8FPuerKAx+PR zwiA0_Y=U@$r^MJwuzAV;g+#Bj3YeeRr^9rTKS^Pv@rn&;w__8+EB@Ev<}0Dtp{`j1$f6_f1e)jPf6 z&&&V9nQswD{+(U+MdLJ3L}XAj$j+YZM|v@Q+46FfQbXmMDee%ma@VIMg`a1W{DBwy zp>AvHCwg?w6Sr40e&M>yST9FuOI%`z>s3DbH_t9BxO(lb=(2cEjHQImvwh`x32jUCbt z-^H6i1M~fA5$G3qx5^~0d=UI-90*QZ4$fgkgMNpJ4V3i!!r&JaDP9Q(p$9Nd^tr1h zY6X-Uu|8pu^k*gGg9cuZR{5~_@^ANvpTFZ3nu|NzA*^@IVy+(3OvJR0siqi7T?5^~ z_~_?Pg(jq2W2df!u@g^36`DEy+lLbGmSrP^cg~*P)SL}67V)ur4HRh+M(#fhq$joC z@lKgRer@7!VW597@3MXN#L2?u=GH3;>Y1@&`pSjr`cB6}sa`0LW%Te^co^rKm__vd z!vQ(kaz<86Jllxgr!=-n8u+Qi>ijvc6D6cN+zJw{1t*p~mbu)1VxWJ^dQXs0t+&Rg z=-2kTj-AoCL0uzNp2@o57<2P*7W<5_TFOVJxu}YB!6xYg-*BO%lkks~-^Zdcd(G;h zP%r#e_R@K~P+Tw84a7nH%oaK1v913>zMtW+J|Ycq{#~1y7C707Egn9{uaP}AbBnWx@a^i7dVOi@7F@< zFp39f6tSqh$ga11c0s;dOsadb3i0?Ba56>vVe(qaPH@BMtosj(P^ zaJNajty81tE>eAsf3#U4YHaiD%X=K;I%GPJt2V+xvGbLoc6d-ez)c!eLqTezzbqFO z?Usajo$0UQA(LOTtoZNtm=08OtoavC6$cvi%X%Ajv^o2%8upUGrg(p!ZAxwOCXe9a zdA8L$6G61bh?yQb7S*J5a&uWT^Z-1WDAbAg7a61v4Pokf*<&i(wM#GP3e$6NIi+z4y z*3Hftzcv9g41=Sl(tb78d>{j zoXne2?acC&io7L^E%m^<>pXJxTZ>S;UvDXMJ z(z+2awKqoZO|~()&HCzS@?Lf#Qr4gQD7yb^@!;ZQoTe+aps&t9*3-w>x0BfG9(pJ# z=aV8?7H+&LSX7O^&IFau+KlO|G$ws2i5n;$fbQhV!VmA8!3D*ORU9eBfAnxSllf%z zAyYD=q>t+7k!r6hM!y_C$q#i=p7L9zV+%E*fi8=TKb15v;to6LzIqTm-<9DJs&!%b zcX|xa#44k2Aqk?)f8?^1>QGwJMY<-D8vT1cSc`?fdpWNXly3Q#E7(x)`O51SA(Wdn z^egU|pVIT~>Hmc0Oqv0U;#>EjVPsGc_BT!myU-Eq786Z(?aVf z(5fqPxkkC;OVF$~BD+c|A+0s6yO~B53q!f-b`qD6O`fNj50jhpleUSjRGi<_bcD?N zDU^@kB$#ROByrBZ3q7nNLQhgUFkU;PnI09T&FK_SJbH&k|X$+Zl(NksBAv_=sBwHz=J9Xj7_7ro6seohjJE- zQUZec$n_>Qkbt$2;DOJ`Q`eReNi1`?z!6+m1gfX6TIEU=*FqAU}TX$P9q7PfNXO z?@ehr_{#5-if>cS__ghDGxjKtW%TWO%R!y zULYR`gcKA12XAGs;O}9$lzP zO)Rr^hz#S#1_^%Dr75_b2X*he6HDTW>&Zm5(eLodw;%&;N!d9|8CvaQ`V2;HiicWX zo2QiPHf&wxQ2X7D)|oRwAU^`M@`ZhM%PI*opLs}xV%P~se5t`jW?CHu^Oo!XW;VvW zaPOtweXZ`6=wU%F`XSQMcUMX1w?xnCvkwLq#0{)PoBACJ!-L;tjT(-Eu%ji7OSPSF z=IQZHMUy_@9O@E>J>&-3!IN*Oo0oo(1pIvjhw(wW>KQU9>l~kCRQ=5O%?DUG~dUf+hT-YFQ7IJuT{zh@qk zUYj%7T&A*AHvxfEo-6m`TeJ=ff`3FWF}}5Aqi_81P3rIpwPA_FUmd?Wgq36y&Kd$O zTf=hxUxoGGlr7XfeST20xrO|Tb&17TF`fw~oS#Z@9Pg|e{+cxWj0Yh<+B1$*S93H# zWNq|}kp{~ItCp`6f=_k>HI~`Nnyo`28wdpTbYyn@o)=cP7qR~<6|f8_x`%hlnnVQv zezJGL77so)`^4)hg~#Ggdnw)djrk#;J(9%{%A}_o-?G8;qqSMh;qg~T3wyh{oF|P{ z=iLCY-v)S~VAlPUaUZ_T5VVVNMDW_DECd`e8B<|SrOyP7_SCSSbc@I%4SAX%+Fr&y z8tSogBM7act@^g^pkzL}lT=H2GM7e&e4pRt3PD0ROyRAhhUX;=y6EjELG^7bE;4=A znBOKz#{lZ$UFir&=g?H zqlw&Nk*F4o6Ct&lMM)HRN#oPYNR%eI);b9p{ZPoqQ8aZtXO1ts%9OIZi9{XU$`fr61yJ>;f0hkd=vPX>2 zj&4h8r#KaSkaOsXg&Az8oeZApodpW(MDe?zTv=x#j5T=#M@ie4-EFw>j# zM0kT|NFtf$$wMal$XByZbVb2!N_OQ7XlF{i+r^%MTfO=jsSz}H#N=?ppE9D&DEaKR z|KDGRyO3@SW~-#5oA)yk8m*3+%0v$H1{c+Z`u2<;2)5{LcG~Nxua2~`nf}pwG z?N0SX`iO~%Wdtw_T2X%nQw4*t-4CX_wro}ev4j}5#t105u0>FCJKl-w&Z}>+p|CKr zCj$foCk9FXsC&ZWFlO`y`tnMLi|IcGQ_j$9)sjD#E;1uJ@t38VcdZ%4d@$LOLp-AQ z_H^kWKO?jK+#mif zxb=WheJdk-E!$W@8<3BJ+ie|IYw5st8ROh}4Jb4(>&)t260)z@>-mpr7o!~A=}3v^ zx*JnSvEJkb@1onuZZy!~CubBu7Aium*57lwMIjbyk^6ZYgVFEHZd3>SlJ>_cC?|8`H!!U1VHkGjZhZUZY_Z5F4+?Y0}BkaZ& zG&Pcs!-Hy`CQ+ySNKIvGR`Ya{YXTb%@GZsdGbL#T^;e(g?4v1B0ToPtDD4Q&{!8aC zPst4aZ6cmqv5e!L!r2sa(qhn}lCy4>eQ{8!{VHFaUYNzJ=C-< zJI^M(KbWyxB%D*6j+(|_G;5TgHB0qkq)`}{*;X>AqUQk^hSW}mM##r>J`jWOS!VjY z+^QrC(Xn>44PYP&)|uE5E@dBa8oIsa|M224X?SYV15ZJYj3z&N_HLNN~g<5y4HH1e* zbm%e5$5ox%_?2`uCVZ_ufBGRy>G|rOKePsiRWwMpq_XkA8CuG?-?0HT@AudGDky}X zf0~h{`Ms5+>zo06Fx@9XZbZSKD9Ahw9KSB(?|~TnzCDYO)oI{Ioz8m8pX3?*O_N)t zI=8E%rL+!x|+^?xb?JIwqX8?-F?{|UmR7wKHn1wqlbf=^e>`Orll0%c%}tGxekuEMAmn*0n}yq!i|nGA1^ zuC1uA0nvJx=S@cGGxs7jqRHG3A9#kM#6Yt8na{n61;mQ6w!g&mxOGU9k}~ZtddMZMl)5 zkIGTyqfe^Pu!)v+n+I@y{0`ChdJ)&Op8Utf+X;Us+OQrV>~HwheQ6b&UNzu|W2UJr zxTwNyw#(l`clj^sgzw<~#YThjhNhZQE-JIrRjEg$Uh)xbaB}C&-RnZ>4ZP0k)%`)w zNc(Ru8h^E#6D!bLYZxd{qo>A;C-YPaei98~_vl#{jv9?+B`eCpXtXqj4U;%{S3q-&<@^W?kaZ=Eq^{rpUm(a=y9Hkt8f^4&BTvHb5m4D z^@Ha0WxStS#3-(>zwYnu?j)cb+^pQj-rPjHbVRxu6+sS0!2Ot#&oAXMPNm}^s=t`$ z>Bw}ctKnYsy_@>7IQ^3l3_Znru{?l zrP{~m6tombYd@^Ct<&KDvZQbC6+#qk*>z_783))(=2zVctLNDON&=MUp*w-yoOz5WlTgmtw{Fi3h(lh)_&FgM4YE}a$ z4d%Ok!XLb`_$C&;kBFr2PC;Bj|F;f0dAxplf2iIT1eI3BYf}ywKR|7{-*&tveQqz( z-AIBu1?MQxo113*1J{l!-){u`FxwUBK}Z_fwhaNfpSrY%CY4Z-S*)jcE0=N1s~zN2 zIgWz!&k`b9`OK?NlpI59Ii7lo6RN6KP+h&9{>l$NsU9m}+=kvTlFn1Ntaw)NTVl<& zErbJfLh~bK2gXf93RJ&BYA^#0WNLceU5T+B zwuz`i{gkS)@-XOk!Z_nw3p;PiG{2Jn#d6`FV_x>KA#O=ELNZR7`>PH%xjoEEFZ^pn z!rQ+# z;)==R$z~7Rn^OUhagZ1Yky+L=e;%+4iHiS7Zju*8x&varuk_OPq3s7@M(1X&jrOTC zkBi6?n?KrX-By46^b~a^dRvW<$&F}yfZmr}P#<+k%-~i#@Qiquz=QgqduD3T zkawrYD{X|Oapq=%IOSk-Bh{)IpNR$W-WTGeV-At^>*iNoyMzrx2OeIUnx`H{%1 zefy!WS0!vRIJBCI>Mst1D#MOy;!D>H*WR}NGdd^QQPKf zP&ID{CFPy;c(x})<^i!o4CItv(nTE)g9h6o*^e-lPBS7qQ!ZJ4OW2O(l*g#A*Cb2E zz4jD$cuMq#zwJM0C9ji9HY$=dw@d#mVDnb%yN5AGRj117H<>fLv;Ey~s;`+9hnQIU z^)X`ZWjoD2L1HQz=`>#F1EH#W4D{xg5d-oIKtv-i-Q?bX`cq}#B+TdL8u``sGx)#~@qisPe5`9df_k zECHwMuKrpOZ%XBe8^6=Mp<)u7t-e0v)WQ}IVU}<=vCJBo0)L@LL~GFI&`VBR!ugw- zqs1R13K~8pr__07!-i7L*u+RLg!J2{*Q>euF&nk4{Bq-=ml+0T|t zyM`!~fz|)V!GQq3=5G*L!X0Dt`gt|l-=MSu`B6v8|EC3D>#D8vCbvtoQnX(ya#29f-@1GH4RX@mLc%eY5`^#Lw@BFi&emlXd6 znKGw)v5W#^DqumRdCU28F(!y~=;~{*Dk#~UDC55e_Our`5x+O$$FIL==D&ov+c$rW zZc+=v^&L}F zDtC$ve7LJ|a`Wr@=EgEQ^7l27df+6BAa*%P4f)%euruSkh|5W(eZdp2OauI+|Fh!W z-0U;?&&Jp6664|d5dj54 zT~I8rC-31_9iQl*=19vBYBFqbl2B=yES*A2`rv?{??fqmDiS&w`t=uxsc%rJd6I8m zJTZGtc+(cz>{|7Ga`m*)Kh85Ufd%ogW;I`{!rH zDk%rlHRh>P<3(9CQ~~SUNnG!R_rN#7sQn^Buuzt$$&UtYM>Fpf_;S)VNbAm1HVdCa zias1WJv72bA!@QS+5obt_CDQ6sR{ClYeVU9S@J05V|^!QE?0XDg^J;MRKfwl4A>RV zulz3pNd2$S&-Ia-#xcnX>LW`js#4!Vd$}1fJDxUK-?;Pj%F~~hR=syEch$_5{i$4+ zkzO^Tt0;woGM;XTV;z6VaZPfvbNGFFDm7EIrYaMB80hovb2Bp z&%eg4``G!|AG`P9uMA@k)&}*qQoS*QICC8#n`RYU>njZHz`1qBd9RF_!@aU@p^4J7 za3CUYUw;_fY2WjtgUo;vwEPaPv?Em~-fG92v>8p-Rtqar)9gRY7|qRL{GX7T@;|Xg zTTWc!TZTRyb$8I-cqgjNcJ^!IHW^1hnB4Qv0y!)M{m-NJ6n3*E9}y>Kg@#EQZA#p; z80>-WXI6T1y6#7V993Rie*xllOZiF~@t>>6T9 z|AeQ3vjjK&RRxLcGp^IEEwC=$NFBf7+&#R?2#eo;^#;f5oymoT5K_v;=Y#A7o5he^ zB=Y9^nhy57Z;`aY?ZnR>rui{H;(MbaH}}lmen}$|NmLCV9FYxW4E-rZHt?$}#ZT;U zC0?)!)~)hQQvw=K-tbg$jC*F;X-Oh+pKRM>u>g_?LH}kj{sD+(hm%9TCVC|>2lLTq zN@-_2T_!uM(;ai_C2j2QB%6M#-knNu_$-8JK{2GP6TEl1gGvEq5k|!#ybc`ydO5rDenImhCsOw z^r<;R1>G(tvOU0K-2od>iTxVPEo}f{HfoQsTSymV58id7uDdH{d=d>RrrNDuEvjc@tUghU z73>sRP^%%w=oS+|8DAH)y#`sxAPi;3^2QtQ6;00+Te4N7RQluS$?ewiB9aJe1)jOf z)`^uW)ss4nE2gq&Cl^c(T(F+D?HbprFp@76*^8x?2uTZ6{+~2E)b*?3NA*ZP?6x_F zq?Knao6T1fR+5MfzcDR65WI!09S=kz9haTYB? zZD(T*ehi*$C&R4O`{?7@Ea74P=`~U#ca5q~?7!dCfiffY0N+tk8v$6jt6uh@zMH|} zKqiaC%9Mf-TkY1D38t@p$K4~=m)C+UAjw+pItE{4xqvH*F`aa?wl04_5XxYceD#mV zdv<;%?uSyT*1V(=Lt4xpFz?|n$~fD$L8FhCSCY1yAhxT~jMjGoIo(GbYmtmEFVs zkr|(GfbUOM6Rn8uH>?}^ytOG*)iO_rCnS5GI9X$@=jkf5ohW1N`>S?_La}dFF@;bC zXS+9xO{lxkA=(^h$(RTvsScJy`DR=-LRGz}TC)*9n*huD?aPtyC!yJ9_|x%dL8)kE zm%4TW=6E2f?LS^B2@OW=!7ms6-tRdVANn#_YP!~riX&);UP_auZsn~+yqqB*vh=5o zpLE%(AGj9(>`djznP?y%b&x}`Wkmm-3?|5{Rbv_66^gZy!)>hVl=XieFzNOHRKtFy zRW{I8v=zRY>%U%KE;!&;#=12!sY6KWqSA71!*&$!f4+AnS09fES?@57Ce6doVsdK7 zK%3l9mT-ak;G{LvT&KZvpcZ9`0dvf8{r49asAR^#3k(swCy9;m>G)&)YXg)tc5E&N z21K^Y7tjA=%`0Dz4-Ee39_+*227wyj4$LJacxvE8Ca}k|KlzDl`fANvPX6DZ@`%}T zPjy1e{}t^%CUVcfMJt9HDzPcA#26OsS_ZA&wkQvk_~Gzbfmyse?FBxc#3npV)N+pW z-Kx{OV*~!K=Y<&mjzddjT5IMJrYd&7Hg>hqX_7qb-bM|4$@`+IBy}PnQ;5;^hq(r?5naSh>dJm?Jlc4{m7zX3|p9aD{|D674A)=uJ*WFylo&I_7^vj; zXf>E}7@zAVur4JEseaa)6!x3$E(BthA3&LNAMqSJ@2AGa>=l)=-BcX7us@SY)}Nbr zUzCwDpx<&Yly+g@?S5n19pC6C&%>Ipoy;|8w-A-TNqiAaiP~@c8<8og^;Lxe4OV{p z#1GC1ZHoVFzH)M9c~c(nyI=Js#!H@ua{3+zZm>M^8-&_O)k}}4Y4W%iH2xgUq1g}u zlYg*!A^QghKpf6ua+|6U&w6R_OFof}qa!}DbZfa3nLn96IHrWY$k9?O{)($Z`KvOC zH^IzH8)zWt%l!Pmc$8K9Le?}@bv|5EK@I0h(n@*^+p1Sm5p6NUvaj!$(|~KoGY4UK zkV@v2+F-WEc)JV@pjp@aC)cJOy$9irqTc?HQEuh@9(pnJwO3#KC9kh>zvk{~xRz30 zlQPTseT4pEbe6z%8G()6c$l=39P4fD@Jqf+=H~BWOLdF%mZFc}nss}2>OPQL{JH7< z5PXV{w(~bOdYkw^xTMO1b#tx~^2eR`u{nppfG*0k`m zYE$DuEy>Wg9JM+-F4(E)E`lChmk7~q$bUFH7Twe$K=)1AKqulFNdV}+@{wu#CwIQO z3nu`Pu1Xr_{cYEDePus3U%Vww_;9#%6#ZzIz^#Y$ziGv`bzW`Gq;Ry+>~?kE2&RA< zBfktxZK}9WGRb)cUa;?pLU$z#N{>&JN_7)MUHX)`vlc7z@HtWA@qQp(<>}JTt zjMIb`tv&3hwKw?Ygp0d2jZ6+Cb~j+wpa&SGQ>i?Ul0snq1+{g_$8UMAgFJ0_Z>q#y z`(l>GY->x`-X6ZehJOt|v0j;z_kz7%`rl;z`pU*(lBC%d7G9tL%>I^fsTvr`I-N_p zc5#p z!f=<@b*(EWfG0C4t%3U*`9*eD4?i_L^<5zeTs{3(gRM|?k?hl7a^R_F8GN?@)3$n7 zshXK-j@ITHx}vRArggD(il>lISN4RINT_f}>H8~!OCaWxILV1*=oky_^~MfXeDEfL zGb?1;_I{2#-YAGhkaX{5pGR!!C_z_R2J|9>-%jJ@Q-br^u*AWIh35?t(cS(_{e7eaml&v{a}*@C)3Hr zUtIj0xbk!i9=(dsr`lpMW)|yXj=^NcLgQOBL{Ny>;m(px{&Kz`j@Y zwzh6h>?(D)vQ%5)nYKNzKb$v+p&B37ArcipXKyF&@*HrJUb*$@ApHNyb95E~=i%H- zI8Y#q9K~;B+SDG(ph^``(7e~nRQ?H(6YD^y{^-MMyaBGlk6N@5m3N^sHCX()?C!Va zhV#Owr8@*zw4~euLgmz-9$Ps9oWwM{PpORv(A!drtm=UCVm5DIeMSL4Wl4Vm2*A(v zd)S&8%EN@vfMe)|0et?8(#27rwu`F`ZPCr7Wknn_$h)+%dxqyz>6dyaXo_WOKHpPb zCT}c9t8ca%;*BW{V#?OwGu=X5jWIzV{%>u#dDe}MvFQp3&%y`G45ZN8i|N_FGz=Cb z1W1){6f6ff@=Gza;xR4%>zo3xUWj;Q5{Fb$G^db+NU%*D~<}7 zm$Ve{MW`9}^bsN-qOQQ2Ux~gBdhw_4I)tiO7BBypq#5tmhEKj;gEL`~P| z;yTE~L#X^#~HGo7St8(*#r~T01=#!nq`_m{T_r#+IUE~)E5!5Z8a+^d!BYuO5R5Z;6Ajw0h2b-0o*S1cI%|n*XF#)$Wzz<4g@q&CAO=U6x09y=7!!0o7hH(U;$DTul! za9Bz6@|~7yV=KezKZ?1u>f;yhlOJbpuryX7`N4lf36?_}!N7r262+DAloZ308+qBr z<8!$mu)QzUfJ7dJ95A%SrH<}nXmaIAG$MY&rtvHc5&V4swa@OGwaI~IwaC;JO;)!n zIiWqF>-j6p(cups{(VICk`8tjCcAiu_`B)YOb{iYtR`_~v-McTKFy2M`xR3&_Bkcv zB6b-nO^eC5xw$PdL#KtPkH3?++bN?J`cbA-_}4mwxGZZ_Wq0Fb0kv;*(xocOom}v% ztgK88s|X^dn!+Z97cS+*_4CHYii*$L+JN);rqy8kX9AdE(}KaW_IvZ?zxI{j=tpa^ zEh}b=$1~$klc51iT^o7ODcU>`wpKD66eS_rxsFP69#_ORn*apnbVhSnQ2XOdE39_s zm(j!6jy--2IvyEq90)Nk|L>tnPc!mCD9_QCa}#W4V?nuCDW;*mq1?iqZPw3AHCF2h zIC_CA0WmPdk1oc;8La@}n}=nVzk~7AJqsK$R5DTsE}`Wo$&vL)_wo>@A#TckfVd3 z6BJw;Jxg?~4&igJ$Hb^V(fjhN8bX;76--#)n{$XZ9oEd{vvpL)bt%{laD4G{+cCRdQV8)O_{=wT9H5Oro^_YWZgsBqzEYAzS60Py0ISwm{={cw}( ztV6=~ zeFD4;5()|B9$o-b4`ex40#DQ!t-kp$JyG2Y&L?x3XYPV*+zpX(SioWqUQIw1Q9eA& zSG9+dEiew=M6&=w+>BF-dtjVsoge6W_mrZf79xDo0GOW(k0N1cc{n-bk@)VXOY5m# z-&+SC+1lxR`tci>y63g`TmAT+lXoIC`GlZGQ5w3p#daQ*vmfHdZ=siS1&JKTm=0=P zdA|@`)#ni(O99J*o8s|z@p1ysFaRAu+P%kavoU<>c^HW-FYpj~8Dz&GUjl7;ME?`q zd6)inJ*=4}+lt8$p096}shFImB{rF{mlSr_=CGByWAX>(ZtLCY!S>?y+>RJXyK{vf z;}|u6*NDAxU`RtJXN7O!s|cS-gmGSrdc@(eCf&ETB%~v zdl3}p_W#lJm0?jfec#e4NH@|A(%mK9xqy^($VDo!bT>$M#}d*@NJ)1$2r8W_UHXpq zbG)DU#w)Zo($VOzAl_d_QHb|9b;1=k9fGV>j~Yc`qY zd*SkS~Dpn7DhA!^)5 z{|@Zgp9WOSwRN9w32Ows)LHhbCeQDria=g4A`(H|)}yl#!T(m3RE-NsJmEOa-;V)< zY(l_WVmF6wWryVp$0+4|-m}GsB)VF4({-XAfa5ZiScj9Kp)Eoe;R^Q{;lH4DG;Qx9 z#0F24f zwht>QgrWu0ebHEwTUSl~hIKx}H^bac=;)^Ut&_GF?DT4bZhf~P;K*f;mk3}^KlAAw z7KLuft09wt9tP!oz;ozuYfanYty(Alo{c6XyAc-xZ%GBmHBzk_vPv2ALk1rQ^$Hp$ zk)%XTk^~XHT|_S5y`35pqKgPfpZBTO!)F&`NILx(e?9y80rgHU)Dad8#qK8^D&6_@ zq5}Yu-dTPDB;Ob~ze^MSN~`=zF7lNb!B$`2WiZA7arr_n^$;`?g92hOj#%uss0IR8 zw@pc;W48CmbatAzZMtu4@m!}<4N>+jCPLno0iHVRSY z#T}&|7N?1IlpICzmQ@Mi-m{6|1~Bd1R|&W?e9=V+lx5%)dxh5WNY%%p$+E?WwovxQ zaMS{VsB(6zxu!WslaGbhce(mINy5RefSbsY0oXxhoXZ$?aV6&21sf}TxFhn14j7hi zKxF>$wkyeqJxP;LSJ!^Z$88fK6*795OLj@~k(A>EQ;|TiqOIg(b{cV;#(aZPg zbW&3+!E~M~ENM_mq_EHBkdNx~o@X^JfjYEB>^`9?5Vyew2rTEgJjy8yTn#6)=;vh0;?SG|gs7jk%^@kbt~ z9!;q}g`Mim_bs{Ol)y;!z0@6CM)zrSsuaC3g$^lwJEFZ9srQwd>}7aY!-mrFXHDcQ%fi+e_} zPYDquELu4x)xBGmkNWtbXKQ_|`5osW&3I%PM1tDh3>|~v1tuNqBRFKAvIhaoRn6Qx z-3O6wl5u+82I3(<1w3ehtIv^a6#jdSgRj@D5I1)Rc+w;ALMfcNY@+31u0^u_n@r|k zE6wqadGs9@)*ZL%PuVyr)o1T3RNfY;$6`g;8fq`tp$X-vH!f|K66a6YIQM83fb^hg zq*j+HfR%T@HTnAk^yO4J$US@Vn4qqrT>%WRWC8Fh+&w<#-n$0`OWKDYQBc6B-|-^9?%1C9brC_4fHx`pI?^R&J7f&{wVtMmR9$$w)CuOsS*a0 zFj2i=K6omx;9I=dI=?x!@1Hws=btH2aoVN4uMpUC&(jV4Fi$&}3)pT@lA!5Kj4$X(Cj=GY=4Zta9m&>IuEbiOrVA|*U%o5x`Q&~@%CjXe7 z9!i{s-?>?r_vEA+1!B?SM&>rE+v0+YnOO%4Ka;!9^;kbBS_gGyv$g*y*3g!*P`6BU zneKbUHmnyH*u6HxE8&>zeE9Q+IJrPYR&DE50&kkr@}uWt=*lzZ^e--`^SY~wYzqhc zYT>yUN)nBHHrv^lQQyihnOEq`zIw3I%no%k{_5Fo0UDXq?W!n=ASRrX-?Nq|CFC#X zzk?)cMfd4sXN4MRwVM#tEFLkU%_Snx<7w(lKeB1NAr2-lRU6@obx}!>9 z@!N09Rl$uy(~4N-T<;|=vwXuoS;ojkwjL!F#$!|hO6~d(hmI`7G5JrhR{;`9Om=k- z3at0yPL1vBfPI`ujDD&lH6ppkQ8FnYJpHE4{_Vh;a{zV&(oy(ilHA?ss%GBYtt2(> zKisS4rw$Cxt>3J^(7HgRcX5+V=lsRs)Cc-Cq&aGpBC7dv7P%p~+j4nTQ-R)lF5;ay zaUZ|_xBhS3tVpqvUPkh|^gh*U|2FW#YFQ^a%xyVvS=5!*d>MQ9=% zV$PBl8SfRkz¬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

+EC3sN24nEYN&`Oi> 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

- + - +
diff --git a/public/js/requests.js b/public/js/requests.js index 7aa9ca6..0c41e62 100644 --- a/public/js/requests.js +++ b/public/js/requests.js @@ -24,8 +24,7 @@ const listaMensagem = (uniqueid) => new Promise((resolve) => { localStorage.setItem('obj_contact', JSON.stringify(res)) resolve() }, - error: function (res, status, error) { - console.error(status + ': ', res) + error: function (res) { alert('Nao foi possivel carregar as listas de mensagens.') } }) @@ -192,20 +191,25 @@ const statusAgente = (matricula) => new Promise((resolve) => { data: JSON.stringify({ matricula }), - success: function (res) { + success: function(res) { localStorage.removeItem('obj_status') localStorage.setItem('obj_status', JSON.stringify(res)) resolve(res) }, - error: function (res) { - console.error('Não foi possível reconectar ao agente supervisor!') + error: function(res) { + console.log(res) + alertModal( + `

RECONECTANDO, AGUARDE  

`, + 'Estamos restabelecendo a conex o com nosso sistema!' + ) + console.log('statusAgente: ' + res.responseText) } }); }) const atualizaAgente = () => new Promise((resolve) => { $.ajax({ - url: window.location.href + `index.php?idProg=14&idSubProg=3&ajax=1&acao=atualiza`, + url: window.location.origin + '/index.php?idProg=14&idSubProg=3&ajax=1&acao=atualiza', type: "GET", success: function(res){ resolve(res) @@ -256,4 +260,4 @@ const marcarMensagemVista = (uniqueid) => { alert('Nao foi possivel carregar as informacoes do agente.') } }); -} \ No newline at end of file +} diff --git a/public/js/util.js b/public/js/util.js index 80124bd..4f3449e 100644 --- a/public/js/util.js +++ b/public/js/util.js @@ -80,7 +80,7 @@ const alertModal = (title, message) => { $('.modal-content-body').append(``) $('#footer-content-left').append(``) - $('#footer-content-right').append(`
`) + $('#footer-content-right').append(``) } $('#modalselect').show() }) @@ -110,7 +110,7 @@ const startSendFile = () => { $('#footername').remove() $('#footersend').remove() $('#footer-content-left').append(``) - $('#footer-content-right').append(``) + $('#footer-content-right').append(``) } $('#modalselect').show() }) @@ -139,7 +139,7 @@ const startPause = () => { }); $('.modal-header-title').append(`Selecione uma Pausa:`) - $('#footer-content-right').append(``) + $('#footer-content-right').append(``) $('#modalselect').show() }) }) @@ -179,7 +179,7 @@ const startTransfer = () => { }) $('.modal-header-title').append(`Selecione um agente para transferir:`) - $('#footer-content-right').append(``) + $('#footer-content-right').append(``) $('#modalselect').show() }) @@ -227,7 +227,7 @@ function recorderVoice () { `) - $('#footer-content-right').append(``) + $('#footer-content-right').append(``) navigator.mediaDevices.getUserMedia({video: false, audio: true}).then( stream => { @@ -368,16 +368,16 @@ const buildNotification = (data = {}) => { const alertNotification = (uniqueid, type = 'add') => { $('#' + uniqueid.replace('.', `\\.`) + " .chat-right-bottom-right").empty() if(type != 'remove'){ - listaMensagem(uniqueid) - const mensagens = JSON.parse(localStorage.getItem('obj_contact')) - const countMsg = mensagens.data.filter(e => { - if(e.event.mensagem.status != 'read'){ - return true - } + listaMensagem(uniqueid).then(mensagens => { + const countMsg = mensagens.data.filter(e => { + if(e.event.mensagem.status != 'read'){ + return true + } + }) + + let notf = countMsg.length + 1 + $('#' + uniqueid.replace('.', `\\.`) + " .chat-right-bottom-right").append(`${notf}`) }) - - let notf = countMsg.length + 1 - $('#' + uniqueid.replace('.', `\\.`) + " .chat-right-bottom-right").append(`${notf}`) } } @@ -420,6 +420,10 @@ const notifications = (obj = {}) => { let chatList = '' $('#chats').empty() + if(!notification.data){ + return + } + if(Object.values(obj).length > 0) { if(obj.action == "mensagem"){ notification.data.push(obj) @@ -491,20 +495,20 @@ const monitorPausaAgente = () => { const supervisorAgente = () => { /** MONITORA AS CONFIGURACOES */ - setTimeout(() => { + setInterval(() => { + atualizaAgente().then((res) => { - if(res.indexOf('close') >= 0){ - window.close() + if (res.indexOf('close') >= 0) { + window.close() } }) statusAgente(localStorage.getItem('my_uniqueid')).then((agente) => { - if(agente.status == 'error' && agente.message == 'Agente não encontrado'){ + if (agente.status == 'error' && agente.message == 'Agente não encontrado') { window.close() } }) - supervisorAgente() - }, 40000); + }, 30000 ); } /** CONNECT TO WS */ @@ -520,7 +524,7 @@ const connect = (wsserver) => { ws.onerror = function(err) { alertModal( - `

DESCONECTADO, TENTANTO RECONECTAR  

`, + `

DESCONECTADO, TENTANTO RECONECTAR  

`, 'Estamos tentando restabelecer a conexão com nosso sistema, por favor aguarde!' ) $("#status_agent").addClass("status-desconnect").text('DESCONECTADO'); @@ -531,7 +535,7 @@ const connect = (wsserver) => { $("#status_agent").addClass("status-reconnect").text('CONECTANDO ...'); entrar(localStorage.getItem('my_uniqueid'), localStorage.getItem('obj_queue')).then((login) => { - if(login.status == 'success' || login.status.indexOf('autenticado') === -1){ + if(login.status == 'success' || login.message.indexOf('autenticado') >= 0){ $('#modalselect').css({display: 'none'}) monitorPausaAgente() ws.send(JSON.stringify({matricula: localStorage.getItem('my_uniqueid')})); diff --git a/service/ServiceSupervisorPBx.php b/service/ServiceSupervisorPBx.php index 5fcb496..df43b11 100644 --- a/service/ServiceSupervisorPBx.php +++ b/service/ServiceSupervisorPBx.php @@ -23,7 +23,7 @@ class ServiceSupervisorPBx implements IService $this->atualizaTabelaSupervisor($agente, $agentPbx); } } - $agentesPbx = $this->supervisorModel->findAllAgentesPBX(); + $agentesPbx = $this->supervisorModel->findAllAgentesPBX(null, CONF_MEDIA_PBX); foreach ($agentesPbx as $key => $pbx) { $age = $this->supervisorModel->findAgentByMatricula($pbx->matricula); if (empty($age)) { From 94dba6475b21d20279c3b9aaa48230614787c0b7 Mon Sep 17 00:00:00 2001 From: lucas cardoso Date: Wed, 6 Apr 2022 17:07:38 -0400 Subject: [PATCH 140/144] add fila nas consulta de moment --- app/Controllers/SystemMessageController.php | 7 +++++-- app/Core/CoreMedia.php | 9 ++++++--- app/Middleware/ApiAgente.php | 3 ++- app/Middleware/Middleware.php | 2 +- app/Models/SystemMessage.php | 18 +++++++++++++++--- 5 files changed, 29 insertions(+), 10 deletions(-) diff --git a/app/Controllers/SystemMessageController.php b/app/Controllers/SystemMessageController.php index 065c269..a2f05cd 100644 --- a/app/Controllers/SystemMessageController.php +++ b/app/Controllers/SystemMessageController.php @@ -21,10 +21,13 @@ class SystemMessageController extends Controller { $this->sysMessage = new SystemMessage(); } - public function sendMessageSystem($momento, $variavels, IApiMedia $api, $numero) + public function sendMessageSystem($momento, $variavels, IApiMedia $api, $numero, $fila = null) { //$variavels = [["nome" => '@cliente', "valor" => 'afonso']] try { - $msgs = $this->sysMessage->findMessage($momento); + $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); diff --git a/app/Core/CoreMedia.php b/app/Core/CoreMedia.php index ded19dc..ef23a9e 100644 --- a/app/Core/CoreMedia.php +++ b/app/Core/CoreMedia.php @@ -186,7 +186,8 @@ class CoreMedia CONF_MOMENT_ENTRAR_FILA_SEM, [], $this->api, - $numero + $numero, + $fila ); return null; } @@ -196,7 +197,8 @@ class CoreMedia CONF_MOMENT_ENTRAR_FILA_COM, [], $this->api, - $numero + $numero, + $fila ); $this->mostraPosiscaoNaFila($numero, $fila); return null; @@ -233,7 +235,8 @@ class CoreMedia CONF_MOMENT_INICIAR_ATENDIMENTO, [["nome" => "@agente_name", "valor" => utf8_encode($agent[0]->nome)]], $this->api, - $numero + $numero, + $fila ); $this->api->enviarMsg( $numero, diff --git a/app/Middleware/ApiAgente.php b/app/Middleware/ApiAgente.php index 4beda2a..8230d23 100644 --- a/app/Middleware/ApiAgente.php +++ b/app/Middleware/ApiAgente.php @@ -304,7 +304,8 @@ class ApiAgente implements IApi CONF_MOMENT_ERRO_ATEND, [], $provedor, - $client_id + $client_id, + $fila ); $this->retorno( $ret ? "Finalizado com sucesso" : "Erro", diff --git a/app/Middleware/Middleware.php b/app/Middleware/Middleware.php index 4ff0fd3..1f4efff 100644 --- a/app/Middleware/Middleware.php +++ b/app/Middleware/Middleware.php @@ -55,7 +55,7 @@ class Middleware extends Http switch (strtolower($this->param()[1])) { case 'whatsapp': $coremedia->inicia($this->request, new Positus()); - echo json_encode(['success' => true]); + echo json_encode(['success' => true, "servidor" => CONF_DB_HOST]); return null; // case 'telegram': // $coremedia->inicia($this->request, new ApiTelegram()); diff --git a/app/Models/SystemMessage.php b/app/Models/SystemMessage.php index c1bc4eb..1593aa1 100644 --- a/app/Models/SystemMessage.php +++ b/app/Models/SystemMessage.php @@ -14,9 +14,21 @@ class SystemMessage extends Model private $table = 'md_system_message'; - public function findMessage($momento) + public function findMessage($momento, $fila = null) { - $this->query = "SELECT * FROM {$this->table} m WHERE m.momento = :momento ORDER BY ordem;"; - return $this->read($this->query, ['momento' => $momento])->fetchAll(); + $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(); } } \ No newline at end of file From 98fc9da194bb410ebbbfed31aec886ff2d2780a0 Mon Sep 17 00:00:00 2001 From: lucas cardoso Date: Thu, 7 Apr 2022 11:15:52 -0400 Subject: [PATCH 141/144] =?UTF-8?q?add=20api=20para=20consultar=20informa?= =?UTF-8?q?=C3=A7=C3=B5es=20da=20aplica=C3=A7=C3=A3o?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Middleware/ApiInfo.php | 38 +++++++++++++++++++++++++++++++++++ app/Middleware/Middleware.php | 7 ++++++- config/app.php | 9 ++++++++- 3 files changed, 52 insertions(+), 2 deletions(-) create mode 100644 app/Middleware/ApiInfo.php diff --git a/app/Middleware/ApiInfo.php b/app/Middleware/ApiInfo.php new file mode 100644 index 0000000..18fd0e0 --- /dev/null +++ b/app/Middleware/ApiInfo.php @@ -0,0 +1,38 @@ +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]); + } +} \ No newline at end of file diff --git a/app/Middleware/Middleware.php b/app/Middleware/Middleware.php index 1f4efff..85911da 100644 --- a/app/Middleware/Middleware.php +++ b/app/Middleware/Middleware.php @@ -50,12 +50,13 @@ class Middleware extends Http { $coremedia = new CoreMedia(); $apiAgente = new ApiAgente(); + $apiInfo = new ApiInfo(); $apiSupervisor = new ApiSupervisor(); $this->baseUri(); switch (strtolower($this->param()[1])) { case 'whatsapp': $coremedia->inicia($this->request, new Positus()); - echo json_encode(['success' => true, "servidor" => CONF_DB_HOST]); + echo json_encode(['success' => true]); return null; // case 'telegram': // $coremedia->inicia($this->request, new ApiTelegram()); @@ -75,8 +76,12 @@ class Middleware extends Http case 'link': $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); } } diff --git a/config/app.php b/config/app.php index a853937..d103497 100644 --- a/config/app.php +++ b/config/app.php @@ -68,4 +68,11 @@ define('CONF_MEDIA_PBX', 99); */ define('CONF_PROTOCOL_TENTATIVAS_GERAR', 5); -define('CONF_PATH_FILES', '/var/www/html/aplicativo/integracao/media/storage/files/'); \ No newline at end of file +define('CONF_PATH_FILES', '/var/www/html/aplicativo/integracao/media/storage/files/'); + +/* +|-------------------------------------------------------------------------- +| Informações da aplicação +|-------------------------------------------------------------------------- +*/ +define('INFO_VERSION_SYSTEM', '1.0.0'); \ No newline at end of file From 77ea5aac54a7ee97a5c07b5c5d9de2ed712583d4 Mon Sep 17 00:00:00 2001 From: lucas cardoso Date: Thu, 7 Apr 2022 15:46:18 -0400 Subject: [PATCH 142/144] add update do pereira --- database/att-v3.sql | 84 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/database/att-v3.sql b/database/att-v3.sql index 7194ece..484e6f2 100644 --- a/database/att-v3.sql +++ b/database/att-v3.sql @@ -14,6 +14,90 @@ ALTER TABLE 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 From 4a126699609dd27a226f78b1bd2ecff564deb403 Mon Sep 17 00:00:00 2001 From: lucas cardoso Date: Thu, 7 Apr 2022 15:46:33 -0400 Subject: [PATCH 143/144] Update helpers.php --- config/helpers.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/helpers.php b/config/helpers.php index 97491ba..28d39d9 100644 --- a/config/helpers.php +++ b/config/helpers.php @@ -26,7 +26,7 @@ function whereIn($array) * @return IApiMedia * @throws conditon **/ -function returnChannel(string $channel) +function returnChannel($channel) { switch ($channel) { case CONF_WHATSAPP_CHANNEL: From 159893037b8ce3ae50b08ce0f5ca209c6bcee789 Mon Sep 17 00:00:00 2001 From: lucas cardoso Date: Thu, 7 Apr 2022 15:46:55 -0400 Subject: [PATCH 144/144] =?UTF-8?q?Corre=C3=A7=C3=B5es=20no=20retorno=20de?= =?UTF-8?q?=20api=20agente?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Middleware/ApiAgente.php | 6 +++--- app/Providers/ApiTwilio.php | 8 ++++++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/app/Middleware/ApiAgente.php b/app/Middleware/ApiAgente.php index 577992b..4de8653 100644 --- a/app/Middleware/ApiAgente.php +++ b/app/Middleware/ApiAgente.php @@ -279,7 +279,7 @@ class ApiAgente implements IApi $this->retorno( $ret ? "Finalizado com sucesso" : "Erro", $ret ? $ret : null, - $ret ? ["id" => $ret] : null + $ret ? [$ret] : null ); } catch (\Exception $th) { $this->retorno($th->getMessage()); @@ -411,7 +411,7 @@ class ApiAgente implements IApi $this->retorno( $retuno ? "Sucesso" : "Erro", $retuno ? $retuno : null, - $retuno ? $retuno : null + $retuno ? [["message" => $retuno]] : null ); } else { $this->retorno('Atendimento já foi finalizado'); @@ -640,7 +640,7 @@ class ApiAgente implements IApi $this->retorno( $ret ? "Sucesso" : "Erro", $ret ? $ret : null, - $ret ? $ret : null + $ret ? [$ret] : null ); } catch (\Exception $th) { $this->retorno($th->getMessage()); diff --git a/app/Providers/ApiTwilio.php b/app/Providers/ApiTwilio.php index bd5fa98..6f170f6 100644 --- a/app/Providers/ApiTwilio.php +++ b/app/Providers/ApiTwilio.php @@ -25,6 +25,14 @@ class ApiTwilio implements IApiMedia } 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