Skip to content

Commit

Permalink
init
Browse files Browse the repository at this point in the history
  • Loading branch information
rogeriolino committed Mar 7, 2024
0 parents commit 13d7a8b
Show file tree
Hide file tree
Showing 7 changed files with 281 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
vendor/
composer.lock
phpunit.xml
.phpunit.result.cache
39 changes: 39 additions & 0 deletions GoveOptions.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

namespace Mangati\Notifier\Gove;

use Symfony\Component\Notifier\Message\MessageOptionsInterface;

final class GoveOptions implements MessageOptionsInterface
{
/** @param string[] */
public function __construct(
private readonly string $recipientId,
private readonly string $template,
private readonly array $variables,
) {}

public function getTemplate(): string
{
return $this->template;
}

/** @return string[] */
public function getVariables(): array
{
return $this->variables;
}

public function toArray(): array {
return [
'slug_template' => $this->template,
'to' => $this->recipientId,
'variables' => $this->variables,
];
}

public function getRecipientId(): ?string
{
return $this->recipientId;
}
}
110 changes: 110 additions & 0 deletions GoveTransport.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
<?php

namespace Mangati\Notifier\Gove;

use Symfony\Component\Notifier\Exception\TransportException;
use Symfony\Component\Notifier\Exception\UnsupportedMessageTypeException;
use Symfony\Component\Notifier\Message\MessageInterface;
use Symfony\Component\Notifier\Message\SentMessage;
use Symfony\Component\Notifier\Message\ChatMessage;
use Symfony\Component\Notifier\Transport\AbstractTransport;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
use Symfony\Contracts\HttpClient\HttpClientInterface;
use Symfony\Component\Notifier\Exception\MissingRequiredOptionException;


final class GoveTransport extends AbstractTransport
{
protected const HOST = 'api.gove.digital';

public function __construct(
private readonly string $email,
private readonly string $password,
HttpClientInterface $client = null,
EventDispatcherInterface $dispatcher = null
) {
parent::__construct($client, $dispatcher);
}

public function supports(MessageInterface $message): bool
{
return $message instanceof ChatMessage;
}

protected function doSend(MessageInterface $message): SentMessage
{
if (!$this->supports($message)) {
throw new UnsupportedMessageTypeException(__CLASS__, ChatMessage::class, $message);
}

$options = $message->getOptions();
if (!$options instanceof GoveOptions || null === $options->getTemplate()) {
throw new MissingRequiredOptionException('template');
}

$this->sendWhatsappMessage(
token: $this->authenticate(),
recipient: $message->getRecipientId(),
template: $options->getTemplate(),
variables: $options->getVariables(),
);

$sentMessage = new SentMessage($message, (string) $this);

return $sentMessage;
}

private function authenticate(): string
{
$data = $this->doRequest('oauth/token', [
'email' => $this->email,
'password' => $this->password,
]);

return $data['token'];
}

private function sendWhatsappMessage(string $token, string $recipient, string $template, array $variables): void
{
$this->doRequest('messages/whatsapp/send', [
'slug_template' => $template,
'to' => $recipient,
'variables' => $variables,
], [
'Authorization' => sprintf('Bearer %s', $token),
]);
}

private function doRequest(string $path, array $body, array $headers = []): array
{
$endpoint = sprintf('https://%s/api/%s', $this->getEndpoint(), $path);
$response = $this->client->request('POST', $endpoint, [
'headers' => $headers,
'json' => $body,
]);

try {
$statusCode = $response->getStatusCode();
} catch (TransportExceptionInterface $e) {
throw new TransportException('Could not reach the remote Gove server.', $response, 0, $e);
}

if (200 !== $statusCode) {
$data = $response->toArray(false);
throw new TransportException('Unable to send the WhatsApp message: ' . json_encode($data), $response);
}

$data = $response->toArray(false);
if ($data['success'] !== true) {
throw new TransportException('Not successfuly response received: ' . json_encode($data), $response);
}

return $data;
}

public function __toString(): string
{
return sprintf('gove://%s', $this->getEndpoint());
}
}
36 changes: 36 additions & 0 deletions GoveTransportFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

namespace Mangati\Notifier\Gove;

use Symfony\Component\Notifier\Exception\UnsupportedSchemeException;
use Symfony\Component\Notifier\Transport\AbstractTransportFactory;
use Symfony\Component\Notifier\Transport\Dsn;
use Symfony\Component\Notifier\Transport\TransportInterface;

final class GoveTransportFactory extends AbstractTransportFactory
{
private const SCHEME = 'gove';

/** @return GoveTransport */
public function create(Dsn $dsn): TransportInterface
{
$scheme = $dsn->getScheme();

if (self::SCHEME !== $scheme) {
throw new UnsupportedSchemeException($dsn, self::SCHEME, $this->getSupportedSchemes());
}

$email = $this->getUser($dsn);
$password = $this->getPassword($dsn);
$host = 'default' === $dsn->getHost() ? null : $dsn->getHost();
$port = $dsn->getPort();

return (new GoveTransport($email, $password, $this->client, $this->dispatcher))
->setHost($host)->setPort($port);
}

protected function getSupportedSchemes(): array
{
return [self::SCHEME];
}
}
31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
Gove Notifier
===============

Provides [Gove](https://gove.digital/) integration for Symfony Notifier.

DSN example
-----------

```
GOVE_DSN=gove://TOKEN:PASSWORD@default
```

where:
- `EMAIL` is your Gove username (must be encoded, replacing `@` with `%40`)
- `PASSWORD` is your Gove username


Usage
-----------

```php
$options = new GoveOptions('phone-number', 'template_name', [
"my",
"template",
"variables",
]);

$message = new ChatMessage('Test', $options);

$notifier->send($message);
```
30 changes: 30 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"name": "mangati/gove-notifier",
"type": "symfony-notifier-bridge",
"description": "Mangati Gove Notifier Bridge",
"keywords": ["whatsapp", "gove", "notifier"],
"homepage": "https://github.com/mangati/gove-notifier",
"license": "MIT",
"authors": [
{
"name": "Rogerio Lino",
"email": "rogeriolino@gmail.com"
}
],
"require": {
"php": ">=8.1",
"symfony/http-client": "^5.3|^6.0|^7.0",
"symfony/notifier": "^5.3|^6.0|^7.0",
"symfony/event-dispatcher-contracts": "^2|^3"
},
"autoload": {
"psr-4": { "Mangati\\Notifier\\Gove\\": "" },
"exclude-from-classmap": [
"/Tests/"
]
},
"minimum-stability": "dev",
"require-dev": {
"symfony/phpunit-bridge": "6.1.x-dev"
}
}
31 changes: 31 additions & 0 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>

<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/5.2/phpunit.xsd"
backupGlobals="false"
colors="true"
bootstrap="vendor/autoload.php"
failOnRisky="true"
failOnWarning="true"
>
<php>
<ini name="error_reporting" value="-1" />
</php>

<testsuites>
<testsuite name="Mangati Gove Notifier Test Suite">
<directory>./Tests/</directory>
</testsuite>
</testsuites>

<filter>
<whitelist>
<directory>./</directory>
<exclude>
<directory>./Resources</directory>
<directory>./Tests</directory>
<directory>./vendor</directory>
</exclude>
</whitelist>
</filter>
</phpunit>

0 comments on commit 13d7a8b

Please sign in to comment.