Skip to content

Commit

Permalink
Added SmsGlobal messaging adapter
Browse files Browse the repository at this point in the history
  • Loading branch information
Biswajit Mondal committed Oct 4, 2023
1 parent 2d0f474 commit 616da2d
Show file tree
Hide file tree
Showing 4 changed files with 190 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ jobs:
VONAGE_API_SECRET: ${{ secrets.VONAGE_API_SECRET }}
VONAGE_TO: ${{ secrets.VONAGE_TO }}
VONAGE_FROM: ${{ secrets.VONAGE_FROM }}
SMS_GLOBAL_API_KEY: ${{ secrets.SMS_GLOBAL_API_KEY }}
SMS_GLOBAL_API_SECRET: ${{ secrets.SMS_GLOBAL_API_SECRET }}
SMS_GLOBAL_TO: ${{ secrets.SMS_GLOBAL_TO }}
SMS_GLOBAL_FROM: ${{ secrets.SMS_GLOBAL_FROM }}
run: |
docker compose up -d --build
sleep 5
Expand Down
4 changes: 4 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ services:
- VONAGE_API_SECRET
- VONAGE_TO
- VONAGE_FROM
- SMS_GLOBAL_API_KEY
- SMS_GLOBAL_API_SECRET
- SMS_GLOBAL_TO
- SMS_GLOBAL_FROM
build:
context: .
volumes:
Expand Down
146 changes: 146 additions & 0 deletions src/Utopia/Messaging/Adapters/SMS/SmsGlobal.php
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,
];
}
}
}
36 changes: 36 additions & 0 deletions tests/e2e/SMS/SmsGlobalTest.php
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":""}]}';
}
}

0 comments on commit 616da2d

Please sign in to comment.