diff --git a/controllers/internals/Received.php b/controllers/internals/Received.php index 0f0388a..c685adb 100644 --- a/controllers/internals/Received.php +++ b/controllers/internals/Received.php @@ -287,11 +287,11 @@ namespace controllers\internals; 'origin' => $origin, 'command' => $is_command, 'mms' => $mms, - 'medias' => $media_ids, + 'medias' => $this->get_model()->gets_in_for_user($id_user, $media_ids), ]; $internal_webhook = new Webhook($this->bdd); - $internal_webhook->trigger($id_user, \models\Webhook::TYPE_RECEIVE, $received); + $internal_webhook->trigger($id_user, \models\Webhook::TYPE_RECEIVE_SMS, $received); $internal_user = new User($this->bdd); $internal_user->transfer_received($id_user, $received); diff --git a/controllers/internals/Sended.php b/controllers/internals/Sended.php index dfac195..ee3ea04 100644 --- a/controllers/internals/Sended.php +++ b/controllers/internals/Sended.php @@ -236,10 +236,12 @@ namespace controllers\internals; 'text' => $text, 'destination' => $destination, 'origin' => $id_phone, + 'mms' => $mms, + 'medias' => $medias, ]; $internal_webhook = new Webhook($this->bdd); - $internal_webhook->trigger($id_user, \models\Webhook::TYPE_SEND, $sended); + $internal_webhook->trigger($id_user, \models\Webhook::TYPE_SEND_SMS, $sended); return $return; } diff --git a/controllers/internals/Webhook.php b/controllers/internals/Webhook.php index 8d25108..3cbaff0 100644 --- a/controllers/internals/Webhook.php +++ b/controllers/internals/Webhook.php @@ -94,17 +94,11 @@ class Webhook extends StandardController * * @param int $id_user : User to trigger the webhook for * @param string $type : Type of webhook to trigger - * @param array $sms : The sms [ - * int 'id' => SMS id, - * string 'at' => SMS date, - * string 'text' => sms body, - * string 'origin' => sms origin (number or phone id) - * string 'destination' => sms destination (number or phone id) - * ] + * @param array $body : The body, an array depending on webhook type * * @return bool : False if no trigger, true else */ - public function trigger(int $id_user, string $type, array $sms) + public function trigger(int $id_user, string $type, array $body) { $internal_setting = new Setting($this->bdd); $internal_user = new User($this->bdd); @@ -137,11 +131,7 @@ class Webhook extends StandardController 'webhook_type' => $webhook['type'], 'webhook_random_id' => $webhook_random_id, 'webhook_signature' => $webhook_signature, - 'id' => $sms['id'], - 'at' => $sms['at'], - 'text' => $sms['text'], - 'origin' => $sms['origin'], - 'destination' => $sms['destination'], + 'body' => json_encode($body), ], ]; diff --git a/controllers/publics/Call.php b/controllers/publics/Call.php new file mode 100644 index 0000000..55acbb2 --- /dev/null +++ b/controllers/publics/Call.php @@ -0,0 +1,81 @@ + + * + * This source file is subject to the GPL-3.0 license that is bundled + * with this source code in the file LICENSE. + */ + +namespace controllers\internals; + + class Call extends StandardController + { + protected $model; + + /** + * Create a media. + * + * @param int $id_user : Id of the user + * @param int $id_phone : Id of the phone that emitted (outbound) or received (inbound) the call + * @param string $uid : Uid of the phone call + * @param string $direction : Direction of the call, \models\Call::DIRECTION_INBOUND | \models\Call::DIRECTION_OUTBOUND + * @param string $start : Date of the call beginning + * @param ?string $end : Date of the call end + * @param ?string $origin : Origin of the call or null if outbound + * @param ?string $destination : Destination of the call or null if inbound + * + * @return mixed bool|int : false on error, new call id else + */ + public function create(int $id_user, int $id_phone, string $uid, string $direction, string $start, ?string $end = null, ?string $origin = null, ?string $destination = null) + { + $call = [ + 'id_user' => $id_user, + 'id_phone' => $id_phone, + 'uid' => $uid, + 'start' => $start, + 'end' => $end, + 'direction' => $direction, + 'origin' => $origin, + 'destination' => $destination, + ]; + + if (!$origin && !$destination) + { + return false; + } + + switch ($direction) + { + case \models\Call::DIRECTION_OUTBOUND : + null === $destination ?: return false; + break; + + case \models\Call::DIRECTION_INBOUND : + null === $origin ?: return false; + break; + + default : + return false; + } + + if (!\controllers\internals\Tool::validate_date($start, 'Y-m-d H:i:s')) + { + return false; + } + + if (null !== $end && !\controllers\internals\Tool::validate_date($end, 'Y-m-d H:i:s')) + { + return false; + } + + if (null !== $end && new \DateTime($end) < new \DateTime($start)) + { + return false; + } + + return $this->get_model()->insert($call); + } + } diff --git a/controllers/publics/Callback.php b/controllers/publics/Callback.php index 1a4c951..5c6e32e 100644 --- a/controllers/publics/Callback.php +++ b/controllers/publics/Callback.php @@ -26,6 +26,7 @@ use Monolog\Logger; private $internal_received; private $internal_adapter; private $internal_media; + private $internal_phone; public function __construct() { @@ -36,6 +37,7 @@ use Monolog\Logger; $this->internal_received = new \controllers\internals\Received($bdd); $this->internal_media = new \controllers\internals\Media($bdd); $this->internal_adapter = new \controllers\internals\Adapter(); + $this->internal_phone = new \controllers\internals\Phone(); //Logger $this->logger = new Logger('Callback ' . uniqid()); @@ -242,4 +244,112 @@ use Monolog\Logger; return true; } + + + /** + * Function call on call reception notification + * We return nothing, and we let the adapter do his things. + * + * @param int $id_phone : Phone id + * + * @return bool : true on success, false on error + */ + public function inbound_call(int $id_phone) + { + $this->logger->info('Callback reception call with phone : ' . $id_phone); + $phone = $this->internal_phone->get_for_user($this->user['id'], $id_phone); + + if (!$phone) + { + $this->logger->error('Callback inbound_call use non existing phone : ' . $id_phone); + + return false; + } + + if (!class_exists($phone['adapter'])) + { + $this->logger->error('Callback inbound_call use non existing adapter : ' . $phone['adapter']); + + return false; + } + + if (!$phone['adapter']::meta_support_inbound_call_callback()) + { + $this->logger->error('Callback inbound_call use adapter ' . $phone['adapter'] . ' which does not support inbound_call callback.'); + + return false; + } + + $response = $phone['adapter']::inbound_call_callback(); + if ($response['error']) + { + $this->logger->error('Callback reception with adapter ' . $adapter_uid . ' failed : ' . $response['error_message']); + + return false; + } + + $sms = $response['sms']; + $mms = !empty($sms['mms']); + $medias = empty($sms['medias']) ? [] : $sms['medias']; + $media_ids = []; + + //We create medias to link to the sms + if ($mms) + { + foreach ($medias as $media) + { + try + { + $media['mimetype'] = empty($media['mimetype']) ? mime_content_type($media['filepath']) : $media['mimetype']; + + $mimey = new \Mimey\MimeTypes; + $extension = empty($media['extension']) ? $mimey->getExtension($media['mimetype']) : $media['extension']; + + $new_filename = \controllers\internals\Tool::random_uuid() . '.' . $extension; + $new_filedir = PWD_DATA . '/medias/' . $this->user['id']; + $new_filerelpath = 'medias/' . $this->user['id'] . '/' . $new_filename; + $new_filepath = $new_filedir . '/' . $new_filename; + + //Create user dir if not exists + if (!file_exists($new_filedir)) + { + if (!mkdir($new_filedir, fileperms(PWD_DATA), true)) + { + throw new \Exception('Cannot create dir ' . $new_filedir . ' to copy media : ' . json_encode($media)); + } + } + + if (!rename($media['filepath'], $new_filepath)) + { + throw new \Exception('Cannot copy media : ' . json_encode($media) . ' to ' . $new_filepath); + } + + $new_media_id = $this->internal_media->create($this->user['id'], $new_filerelpath); + if (!$new_media_id) + { + throw new \Exception('Cannot save into db media : ' . json_encode($media)); + } + + $media_ids[] = $new_media_id; + } + catch (\Throwable $t) + { + $this->logger->error($t->getMessage()); + continue; + } + } + } + + $response = $this->internal_received->receive($this->user['id'], $id_phone, $sms['text'], $sms['origin'], $sms['at'], \models\Received::STATUS_UNREAD, $mms, $media_ids); + if ($response['error']) + { + $this->logger->error('Failed receive message : ' . json_encode($sms) . ' with error : ' . $response['error_message']); + + return false; + } + + $this->logger->info('Callback reception successfully received message : ' . json_encode($sms)); + + return true; + } } diff --git a/db/migrations/20210322193953_update_webhook_types.php b/db/migrations/20210322193953_update_webhook_types.php new file mode 100644 index 0000000..80f2392 --- /dev/null +++ b/db/migrations/20210322193953_update_webhook_types.php @@ -0,0 +1,16 @@ +execute('ALTER TABLE `webhook` MODIFY `type` ENUM(\'send_sms\', \'receive_sms\', \'inbound_call\')'); + } + + public function down() + { + $this->execute('ALTER TABLE `webhook` MODIFY `type` ENUM(\'send_sms\', \'receive_sms\')'); + } +} diff --git a/db/migrations/20210322223812_create_call.php b/db/migrations/20210322223812_create_call.php new file mode 100644 index 0000000..dda93d0 --- /dev/null +++ b/db/migrations/20210322223812_create_call.php @@ -0,0 +1,47 @@ +table('call'); + $table->addColumn('id_user', 'integer') + ->addColumn('id_phone', 'integer', ['null' => true]) + ->addColumn('uid', 'string', ['limit' => 500]) + ->addColumn('start', 'datetime') + ->addColumn('end', 'datetime', ['null' => true]) + ->addColumn('direction', 'enum', ['values' => ['inbound', 'outbound']]) + ->addColumn('origin', 'string', ['limit' => 20, 'null' => true]) + ->addColumn('destination', 'string', ['limit' => 20, 'null' => true]) + ->addForeignKey('id_user', 'user', 'id', ['delete' => 'CASCADE', 'update' => 'CASCADE']) + ->addForeignKey('id_phone', 'user', 'id', ['delete' => 'SET_NULL', 'update' => 'CASCADE']) + ->create(); + } +} diff --git a/models/Call.php b/models/Call.php new file mode 100644 index 0000000..fee82b3 --- /dev/null +++ b/models/Call.php @@ -0,0 +1,29 @@ + + * + * This source file is subject to the GPL-3.0 license that is bundled + * with this source code in the file LICENSE. + */ + +namespace models; + + /** + * Manage bdd operations for calls + */ + class Call extends StandardModel + { + const DIRECTION_INBOUND = 'inbound'; + const DIRECTION_OUTBOUND = 'outbound'; + + /** + * Return table name. + */ + protected function get_table_name(): string + { + return 'call'; + } + } diff --git a/models/Webhook.php b/models/Webhook.php index 945cd82..48d4998 100644 --- a/models/Webhook.php +++ b/models/Webhook.php @@ -13,8 +13,9 @@ namespace models; class Webhook extends StandardModel { - const TYPE_SEND = 'send_sms'; - const TYPE_RECEIVE = 'receive_sms'; + const TYPE_SEND_SMS = 'send_sms'; + const TYPE_RECEIVE_SMS = 'receive_sms'; + const TYPE_INBOUND_CALL = 'inbound_call'; /** * Find all webhooks for a user and for a type of webhook. diff --git a/routes.php b/routes.php index 121d4ba..bf66579 100644 --- a/routes.php +++ b/routes.php @@ -168,6 +168,7 @@ 'Callback' => [ 'update_sended_status' => '/callback/status/{adapter_uid}/', 'reception' => '/callback/reception/{adapter_uid}/{id_phone}/', + 'inbound_call' => '/callback/inbound_call/{id_phone}/', ], 'Api' => [ diff --git a/templates/webhook/add.php b/templates/webhook/add.php index 3fc35b9..6e53c43 100644 --- a/templates/webhook/add.php +++ b/templates/webhook/add.php @@ -47,6 +47,7 @@ Annuler diff --git a/templates/webhook/list.php b/templates/webhook/list.php index 0b306dc..20a99b9 100644 --- a/templates/webhook/list.php +++ b/templates/webhook/list.php @@ -92,6 +92,8 @@ jQuery(document).ready(function () return 'Envoi de SMS'; case 'receive_sms': return 'Réception de SMS'; + case 'inbound_call': + return 'Réception d\'un appel téléphonique'; default: return 'Inconnu'; }