From 41c3b3b86e3e6de337c3c2f1e2c93fa4c9f38fdf Mon Sep 17 00:00:00 2001 From: osaajani <> Date: Tue, 23 Mar 2021 17:39:13 +0100 Subject: [PATCH] Add callback for phone calls, not tested yet --- adapters/AdapterInterface.php | 41 +++++++ adapters/BenchmarkAdapter.php | 20 ++++ adapters/GammuAdapter.php | 20 ++++ adapters/OctopushShortcodeAdapter.php | 20 ++++ adapters/OctopushVirtualNumberAdapter.php | 20 ++++ adapters/OvhSmsShortcodeAdapter.php | 20 ++++ adapters/OvhSmsVirtualNumberAdapter.php | 20 ++++ adapters/TestAdapter.php | 64 +++++++++++ adapters/TwilioVirtualNumberAdapter.php | 20 ++++ controllers/{publics => internals}/Call.php | 52 ++++++++- controllers/publics/Callback.php | 114 +++++++++++--------- models/Call.php | 20 ++++ models/StandardModel.php | 4 +- 13 files changed, 378 insertions(+), 57 deletions(-) rename controllers/{publics => internals}/Call.php (62%) diff --git a/adapters/AdapterInterface.php b/adapters/AdapterInterface.php index 2940452..b7fa981 100644 --- a/adapters/AdapterInterface.php +++ b/adapters/AdapterInterface.php @@ -92,6 +92,16 @@ interface AdapterInterface * Does the implemented service support mms sending */ public static function meta_support_mms_sending(): bool; + + /** + * Does the implemented service support inbound call callback + */ + public static function meta_support_inbound_call_callback(): bool; + + /** + * Does the implemented service support end call callback + */ + public static function meta_support_end_call_callback(): bool; /** * Method called to send a SMS to a number. @@ -162,4 +172,35 @@ interface AdapterInterface * ] */ public static function reception_callback(): array; + + /** + * Method called on reception of an inbound_call notification + * + * @return array : [ + * bool 'error' => false on success, true on error + * ?string 'error_message' => null on success, error message else + * array 'call' => array [ + * string 'uid' : Uid of the call on the adapter plateform + * string 'start' : Start of the call date format Y-m-d H:i:s, + * ?string 'end' : End of the call date format Y-m-d H:i:s. If no known end, NULL + * string 'origin' : Emitter phone call number. International format. + * ] + * ] + */ + public function inbound_call_callback(): array; + + + /** + * Method called on reception of a end call notification + * + * @return array : [ + * bool 'error' => false on success, true on error + * ?string 'error_message' => null on success, error message else + * array 'call' => array [ + * string 'uid' : Uid of the call on the adapter plateform. Used to find the raspisms local call to update. + * string 'end' : End of the call date format Y-m-d H:i:s. + * ] + * ] + */ + public function end_call_callback(): array; } diff --git a/adapters/BenchmarkAdapter.php b/adapters/BenchmarkAdapter.php index 45b7ef6..c872782 100644 --- a/adapters/BenchmarkAdapter.php +++ b/adapters/BenchmarkAdapter.php @@ -141,6 +141,16 @@ namespace adapters; { return false; } + + public static function meta_support_inbound_call_callback(): bool + { + return false; + } + + public static function meta_support_end_call_callback(): bool + { + return false; + } public function send(string $destination, string $text, bool $flash = false, bool $mms = false, array $medias = []) : array { @@ -220,4 +230,14 @@ namespace adapters; { return true; } + + public function inbound_call_callback(): array + { + return []; + } + + public function end_call_callback(): array + { + return []; + } } diff --git a/adapters/GammuAdapter.php b/adapters/GammuAdapter.php index 4829ab1..3fd8a45 100644 --- a/adapters/GammuAdapter.php +++ b/adapters/GammuAdapter.php @@ -151,6 +151,16 @@ namespace adapters; { return false; } + + public static function meta_support_inbound_call_callback(): bool + { + return false; + } + + public static function meta_support_end_call_callback(): bool + { + return false; + } public function send(string $destination, string $text, bool $flash = false, bool $mms = false, array $medias = []) : array { @@ -296,6 +306,16 @@ namespace adapters; { return []; } + + public function inbound_call_callback(): array + { + return []; + } + + public function end_call_callback(): array + { + return []; + } /** * Function to unlock pin. diff --git a/adapters/OctopushShortcodeAdapter.php b/adapters/OctopushShortcodeAdapter.php index 04e54bc..e2d4240 100644 --- a/adapters/OctopushShortcodeAdapter.php +++ b/adapters/OctopushShortcodeAdapter.php @@ -188,6 +188,16 @@ class OctopushShortcodeAdapter implements AdapterInterface { return false; } + + public static function meta_support_inbound_call_callback(): bool + { + return false; + } + + public static function meta_support_end_call_callback(): bool + { + return false; + } public function send(string $destination, string $text, bool $flash = false, bool $mms = false, array $medias = []) : array { @@ -407,4 +417,14 @@ class OctopushShortcodeAdapter implements AdapterInterface return $response; } + + public function inbound_call_callback(): array + { + return []; + } + + public function end_call_callback(): array + { + return []; + } } diff --git a/adapters/OctopushVirtualNumberAdapter.php b/adapters/OctopushVirtualNumberAdapter.php index d0d7cdc..d38eb29 100644 --- a/adapters/OctopushVirtualNumberAdapter.php +++ b/adapters/OctopushVirtualNumberAdapter.php @@ -193,6 +193,16 @@ class OctopushVirtualNumberAdapter implements AdapterInterface { return false; } + + public static function meta_support_inbound_call_callback(): bool + { + return false; + } + + public static function meta_support_end_call_callback(): bool + { + return false; + } public function send(string $destination, string $text, bool $flash = false, bool $mms = false, array $medias = []) : array { @@ -407,4 +417,14 @@ class OctopushVirtualNumberAdapter implements AdapterInterface return $response; } + + public function inbound_call_callback(): array + { + return []; + } + + public function end_call_callback(): array + { + return []; + } } diff --git a/adapters/OvhSmsShortcodeAdapter.php b/adapters/OvhSmsShortcodeAdapter.php index aa98bbd..9c6137b 100644 --- a/adapters/OvhSmsShortcodeAdapter.php +++ b/adapters/OvhSmsShortcodeAdapter.php @@ -185,6 +185,16 @@ namespace adapters; { return false; } + + public static function meta_support_inbound_call_callback(): bool + { + return false; + } + + public static function meta_support_end_call_callback(): bool + { + return false; + } public function send(string $destination, string $text, bool $flash = false, bool $mms = false, array $medias = []) : array { @@ -359,4 +369,14 @@ namespace adapters; { return []; } + + public function inbound_call_callback(): array + { + return []; + } + + public function end_call_callback(): array + { + return []; + } } diff --git a/adapters/OvhSmsVirtualNumberAdapter.php b/adapters/OvhSmsVirtualNumberAdapter.php index 1f8995f..35925a3 100644 --- a/adapters/OvhSmsVirtualNumberAdapter.php +++ b/adapters/OvhSmsVirtualNumberAdapter.php @@ -196,6 +196,16 @@ namespace adapters; { return false; } + + public static function meta_support_inbound_call_callback(): bool + { + return false; + } + + public static function meta_support_end_call_callback(): bool + { + return false; + } public function send(string $destination, string $text, bool $flash = false, bool $mms = false, array $medias = []) : array { @@ -358,4 +368,14 @@ namespace adapters; { return []; } + + public function inbound_call_callback(): array + { + return []; + } + + public function end_call_callback(): array + { + return []; + } } diff --git a/adapters/TestAdapter.php b/adapters/TestAdapter.php index 6a20baf..8a318a4 100644 --- a/adapters/TestAdapter.php +++ b/adapters/TestAdapter.php @@ -146,6 +146,16 @@ namespace adapters; { return true; } + + public static function meta_support_inbound_call_callback(): bool + { + return false; + } + + public static function meta_support_end_call_callback(): bool + { + return false; + } public function send(string $destination, string $text, bool $flash = false, bool $mms = false, array $medias = []) : array { @@ -272,4 +282,58 @@ namespace adapters; { return []; } + + public function inbound_call_callback(): array + { + $response = [ + 'error' => false, + 'error_message' => null, + 'call' => [], + ]; + + $uid = $_POST['uid'] ?? false; + $start = $_POST['start'] ?? false; + $end = $_POST['end'] ?? null; + $origin = $_POST['origin'] ?? false; + + if (!$uid || !$start || !$origin) + { + $response['error'] = true; + $response['error_message'] = 'Missing required argument.'; + + return $response; + } + + $response['call']['uid'] = $uid; + $response['call']['start'] = $start; + $response['call']['end'] = $end; + $response['call']['origin'] = $origin; + + return $response; + } + + public function end_call_callback(): array + { + $response = [ + 'error' => false, + 'error_message' => null, + 'call' => [], + ]; + + $uid = $_POST['uid'] ?? false; + $end = $_POST['end'] ?? null; + + if (!$uid || !$end) + { + $response['error'] = true; + $response['error_message'] = 'Missing required argument.'; + + return $response; + } + + $response['call']['uid'] = $uid; + $response['call']['end'] = $end; + + return $response; + } } diff --git a/adapters/TwilioVirtualNumberAdapter.php b/adapters/TwilioVirtualNumberAdapter.php index b5065e7..a753d9e 100644 --- a/adapters/TwilioVirtualNumberAdapter.php +++ b/adapters/TwilioVirtualNumberAdapter.php @@ -190,6 +190,16 @@ class TwilioVirtualNumberAdapter implements AdapterInterface { return false; } + + public static function meta_support_inbound_call_callback(): bool + { + return false; + } + + public static function meta_support_end_call_callback(): bool + { + return false; + } public function send(string $destination, string $text, bool $flash = false, bool $mms = false, array $medias = []) : array { @@ -336,4 +346,14 @@ class TwilioVirtualNumberAdapter implements AdapterInterface { return []; } + + public function inbound_call_callback(): array + { + return []; + } + + public function end_call_callback(): array + { + return []; + } } diff --git a/controllers/publics/Call.php b/controllers/internals/Call.php similarity index 62% rename from controllers/publics/Call.php rename to controllers/internals/Call.php index 55acbb2..5e3dcf7 100644 --- a/controllers/publics/Call.php +++ b/controllers/internals/Call.php @@ -16,7 +16,7 @@ namespace controllers\internals; protected $model; /** - * Create a media. + * Create a call. * * @param int $id_user : Id of the user * @param int $id_phone : Id of the phone that emitted (outbound) or received (inbound) the call @@ -50,11 +50,11 @@ namespace controllers\internals; switch ($direction) { case \models\Call::DIRECTION_OUTBOUND : - null === $destination ?: return false; + if (null === $destination) { return false; } break; case \models\Call::DIRECTION_INBOUND : - null === $origin ?: return false; + if (null === $origin) { return false; } break; default : @@ -78,4 +78,50 @@ namespace controllers\internals; return $this->get_model()->insert($call); } + + + /** + * End a call + * + * @param int $id_user : Id of the user to end call for + * @param int $id_phone : If of the phone to end call for + * @param string $uid : Uid of the call to end + * @param string $end : End date of the call, format Y-m-d H:i:s + * + * @return bool : False if cannot end phone call, true else + */ + public function end(int $id_user, int $id_phone, string $uid, string $end) + { + if (!\controllers\internals\Tool::validate_date($end, 'Y-m-d H:i:s')) + { + return false; + } + + $call = $this->get_model()->get_by_uid_and_phone_for_user($id_user, $id_phone, $uid); + if (!$call) + { + return false; + } + + if (new \DateTime($end) < new \DateTime($call['start'])) + { + return false; + } + + $datas = [ + 'end' => $end, + ]; + + return (bool) $this->get_model()->update_for_user($id_user, $call['id'], $datas); + } + + /** + * Get the model for the Controller. + */ + protected function get_model(): \descartes\Model + { + $this->model = $this->model ?? new \models\Call($this->bdd); + + return $this->model; + } } diff --git a/controllers/publics/Callback.php b/controllers/publics/Callback.php index 5c6e32e..4f2160c 100644 --- a/controllers/publics/Callback.php +++ b/controllers/publics/Callback.php @@ -27,6 +27,7 @@ use Monolog\Logger; private $internal_adapter; private $internal_media; private $internal_phone; + private $internal_call; public function __construct() { @@ -37,7 +38,8 @@ 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(); + $this->internal_phone = new \controllers\internals\Phone($bdd); + $this->internal_call = new \controllers\internals\Call($bdd); //Logger $this->logger = new Logger('Callback ' . uniqid()); @@ -283,73 +285,81 @@ use Monolog\Logger; $response = $phone['adapter']::inbound_call_callback(); if ($response['error']) { - $this->logger->error('Callback reception with adapter ' . $adapter_uid . ' failed : ' . $response['error_message']); + $this->logger->error('Callback inbound_call failed : ' . $response['error_message']); return false; } - $sms = $response['sms']; - $mms = !empty($sms['mms']); - $medias = empty($sms['medias']) ? [] : $sms['medias']; - $media_ids = []; + $call = $response['call']; + $result = $this->internal_call->create($this->user['id'], $id_phone, $call['uid'], \models\Call::DIRECTION_INBOUND, $call['start'], $call['end'] ?? null, $call['origin']); - //We create medias to link to the sms - if ($mms) + if (!$result) { - foreach ($medias as $media) - { - try - { - $media['mimetype'] = empty($media['mimetype']) ? mime_content_type($media['filepath']) : $media['mimetype']; + $this->logger->error('Callback inbound_call failed because cannot create call ' . json_encode($call)); - $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; - } - } + return false; } - $response = $this->internal_received->receive($this->user['id'], $id_phone, $sms['text'], $sms['origin'], $sms['at'], \models\Received::STATUS_UNREAD, $mms, $media_ids); + $this->logger->info('Callback inbound_call successfully received inbound call : ' . json_encode($call)); + + return true; + } + + + /** + * Function call on end call 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 end_call(int $id_phone) + { + $this->logger->info('Callback end call with phone : ' . $id_phone); + $phone = $this->internal_phone->get_for_user($this->user['id'], $id_phone); + + if (!$phone) + { + $this->logger->error('Callback end call use non existing phone : ' . $id_phone); + + return false; + } + + if (!class_exists($phone['adapter'])) + { + $this->logger->error('Callback end call use non existing adapter : ' . $phone['adapter']); + + return false; + } + + if (!$phone['adapter']::meta_support_end_call_callback()) + { + $this->logger->error('Callback end call use adapter ' . $phone['adapter'] . ' which does not support end call callback.'); + + return false; + } + + $response = $phone['adapter']::end_call_callback(); if ($response['error']) { - $this->logger->error('Failed receive message : ' . json_encode($sms) . ' with error : ' . $response['error_message']); + $this->logger->error('Callback end call failed : ' . $response['error_message']); return false; } - $this->logger->info('Callback reception successfully received message : ' . json_encode($sms)); + $call = $response['call']; + $result = $this->internal_call->end($this->user['id'], $id_phone, $call['uid'], $call['end']); + if (!$result) + { + $this->logger->error('Callback end call failed because cannot update call ' . json_encode($call)); + + return false; + } + + $this->logger->info('Callback end call successfully update call : ' . json_encode($call)); + return true; } } diff --git a/models/Call.php b/models/Call.php index fee82b3..dd3e8a9 100644 --- a/models/Call.php +++ b/models/Call.php @@ -19,6 +19,26 @@ namespace models; const DIRECTION_INBOUND = 'inbound'; const DIRECTION_OUTBOUND = 'outbound'; + /** + * Get a call for a user by his phone and uid + * + * @param int $id_user : user id + * @param int $id_phone : phone id + * @param int $uid : call uid + * + * @return array : the call or an empty array + */ + public function get_by_uid_and_phone_for_user($id_user, $id_phone, $uid) + { + $where = [ + 'id_user' => $id_user, + 'id_phone' => $id_phone, + 'uid' => $uid, + ]; + + return $this->_select_one($this->get_table_name(), $where); + } + /** * Return table name. */ diff --git a/models/StandardModel.php b/models/StandardModel.php index 32b9686..45bf967 100644 --- a/models/StandardModel.php +++ b/models/StandardModel.php @@ -158,9 +158,9 @@ namespace models; * * @return int : number of modified rows */ - public function update_for_user(int $id_user, int $id, array $entry) + public function update_for_user(int $id_user, int $id, array $data) { - return $this->_update($this->get_table_name(), $entry, ['id_user' => $id_user, 'id' => $id]); + return $this->_update($this->get_table_name(), $data, ['id_user' => $id_user, 'id' => $id]); } /**