-
Notifications
You must be signed in to change notification settings - Fork 64
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Biswajit Mondal
committed
Oct 4, 2023
1 parent
2d0f474
commit 616da2d
Showing
4 changed files
with
190 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
<?php | ||
|
||
namespace Utopia\Messaging\Adapters\SMS; | ||
|
||
use Utopia\Messaging\Adapters\SMS as SMSAdapter; | ||
use Utopia\Messaging\Messages\SMS; | ||
|
||
// Reference Material | ||
// https://www.smsglobal.com/rest-api/ | ||
class SmsGlobal extends SMSAdapter | ||
{ | ||
/** | ||
* Hash Algorithm for API Authentication | ||
*/ | ||
const HASH_ALGO = 'sha256'; | ||
|
||
/** | ||
* @param string $apiKey REST API key from MXT https://mxt.smsglobal.com/integrations | ||
* @param string $apiSecret REST API Secret from MXT https://mxt.smsglobal.com/integrations | ||
*/ | ||
public function __construct( | ||
private string $apiKey, | ||
private string $apiSecret | ||
) { | ||
} | ||
|
||
public function getName(): string | ||
{ | ||
return 'SmsGlobal'; | ||
} | ||
|
||
public function getMaxMessagesPerRequest(): int | ||
{ | ||
//TODO:: Didn't find the limit for REST API in SmsGlobal documentation | ||
return 1000; | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
* | ||
* @throws \Exception | ||
*/ | ||
protected function process(SMS $message): string | ||
{ | ||
$authorizationHeader = $this->getAuthorizationHeader( | ||
method: 'POST', | ||
requestUri: '/v2/sms/', | ||
host: 'api.smsglobal.com' | ||
); | ||
|
||
return $this->request( | ||
method: 'POST', | ||
url: "https://api.smsglobal.com/v2/sms/", | ||
headers: [ | ||
'Authorization: ' . $authorizationHeader, | ||
'Content-Type: application/json', | ||
], | ||
body: \json_encode( | ||
$this->getRequestBody( | ||
to: $message->getTo(), | ||
text: $message->getContent(), | ||
from: $message->getFrom() | ||
) | ||
), | ||
); | ||
} | ||
|
||
/** | ||
* Get the value to use for the Authorization header | ||
* | ||
* @param string $method HTTP method (e.g. POST) | ||
* @param string $requestUri Request URI (e.g. /v2/sms/) | ||
* @param string $host Hostname | ||
* @return string | ||
*/ | ||
public function getAuthorizationHeader(string $method, string $requestUri, string $host): string | ||
{ | ||
// Server or computer time should match with the current Unix timestamp otherwise authentication will fail | ||
$timestamp = time(); | ||
$nonce = md5(microtime() . mt_rand()); | ||
|
||
$hash = $this->getRequestHash( | ||
timestamp: $timestamp, | ||
nonce: $nonce, | ||
method: $method, | ||
requestUri: $requestUri, | ||
host: $host | ||
); | ||
|
||
$headerFormat = 'MAC id="%s", ts="%s", nonce="%s", mac="%s"'; | ||
$header = sprintf($headerFormat, $this->apiKey, $timestamp, $nonce, $hash); | ||
return $header; | ||
} | ||
|
||
/** | ||
* Hashes a request using the API secret, to use in the Authorization header | ||
* | ||
* @param int $timestamp Unix timestamp of request time | ||
* @param string $nonce Random unique string | ||
* @param string $method HTTP method (e.g. POST) | ||
* @param string $requestUri Request URI (e.g. /v1/sms/) | ||
* @param string $host Hostname | ||
* @param int $port Port (e.g. 443) | ||
* @return string | ||
*/ | ||
private function getRequestHash( | ||
int $timestamp, | ||
string $nonce, | ||
string $method, | ||
string $requestUri, | ||
string $host, | ||
int $port = 443 | ||
) { | ||
$string = array($timestamp, $nonce, $method, $requestUri, $host, $port, ''); | ||
$string = sprintf("%s\n", implode("\n", $string)); | ||
$hash = hash_hmac(self::HASH_ALGO, $string, $this->apiSecret, true); | ||
$hash = base64_encode($hash); | ||
return $hash; | ||
} | ||
|
||
/** | ||
* Get the request body | ||
* | ||
* @param array $to | ||
* @param string $text | ||
* @param string|null $from | ||
* @return array | ||
*/ | ||
private function getRequestBody(array $to, string $text, string $from = null): array | ||
{ | ||
$origin = !empty($from) ? $from : ''; | ||
if (count($to) == 1) { | ||
return [ | ||
"destination" => $to[0], | ||
"message" => $text, | ||
"origin" => $origin, | ||
]; | ||
} else { | ||
return [ | ||
"destinations" => $to, | ||
"message" => $text, | ||
"origin" => $origin, | ||
]; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
<?php | ||
|
||
namespace Tests\E2E; | ||
|
||
use Utopia\Messaging\Adapters\SMS\SmsGlobal; | ||
use Utopia\Messaging\Messages\SMS; | ||
|
||
class SmsGlobalTest extends Base | ||
{ | ||
/** | ||
* @throws \Exception | ||
*/ | ||
public function testSendSMS() | ||
{ | ||
$apiKey = getenv('SMS_GLOBAL_API_KEY'); | ||
$apiSecret = getenv('SMS_GLOBAL_API_SECRET'); | ||
|
||
$to = [getenv('SMS_GLOBAL_TO')]; | ||
$from = getenv('SMS_GLOBAL_FROM'); | ||
|
||
$sender = new SmsGlobal($apiKey, $apiSecret); | ||
$message = new SMS( | ||
to: $to, | ||
content: 'Test Content', | ||
from: $from | ||
); | ||
|
||
$response = $sender->send($message); | ||
$result = \json_decode($response, true); | ||
|
||
$this->assertArrayHasKey('messages', $result); | ||
$this->assertEquals(count($to), count($result['messages'])); | ||
|
||
// $dummyResponseStructure = '{"messages":[{"id":"154","outgoing_id":1,"origin":"origin","destination":"destination","message":"Test Content","status":"sent","dateTime":""}]}'; | ||
} | ||
} |