diff --git a/README.md b/README.md index c8f4b0f..1c40126 100644 --- a/README.md +++ b/README.md @@ -222,6 +222,36 @@ $whatsapp_cloud_api->sendList( ); ``` +### Send a button reply message + +```php + 'your-configured-from-phone-number-id', + 'access_token' => 'your-facebook-whatsapp-application-token' +]); + +$rows = [ + new Button('button-1', 'Yes'), + new Button('button-2', 'No'), + new Button('button-3', 'Not Now'), +]; +$action = new ButtonAction($rows); + +$whatsapp_cloud_api->sendButton( + '', + 'Would you like to rate us on Trustpilot?', + $action, + 'RATE US', // Optional: Specify a header (type "text") + 'Please choose an option' // Optional: Specify a footer +); +``` + ## Media messages ### Upload media resources Media messages accept as identifiers an Internet URL pointing to a public resource (image, video, audio, etc.). When you try to send a media message from a URL you must instantiate the `LinkID` object. @@ -345,6 +375,7 @@ Fields list: https://developers.facebook.com/docs/whatsapp/cloud-api/reference/b - Send Locations - Send Contacts - Send Lists +- Send Buttons - Upload media resources to WhatsApp servers - Download media resources from WhatsApp servers - Mark messages as read diff --git a/src/Message/ButtonReply/Button.php b/src/Message/ButtonReply/Button.php new file mode 100644 index 0000000..f701e64 --- /dev/null +++ b/src/Message/ButtonReply/Button.php @@ -0,0 +1,25 @@ +id = $id; + $this->title = $title; + } + + public function id(): string + { + return $this->id; + } + + public function title(): string + { + return $this->title; + } +} diff --git a/src/Message/ButtonReply/ButtonAction.php b/src/Message/ButtonReply/ButtonAction.php new file mode 100644 index 0000000..09c5d9c --- /dev/null +++ b/src/Message/ButtonReply/ButtonAction.php @@ -0,0 +1,30 @@ +buttons = $buttons; + } + + public function buttons(): array + { + $buttonActions = []; + + foreach ($this->buttons as $button) { + $buttonActions[] = [ + "type" => "reply", + "reply" => [ + "id" => $button->id(), + "title" => $button->title(), + ], + ]; + } + + return $buttonActions; + } +} diff --git a/src/Message/ButtonReplyMessage.php b/src/Message/ButtonReplyMessage.php new file mode 100644 index 0000000..0053456 --- /dev/null +++ b/src/Message/ButtonReplyMessage.php @@ -0,0 +1,48 @@ +body = $body; + $this->action = $action; + $this->header = $header; + $this->footer = $footer; + + parent::__construct($to, $reply_to); + } + + public function header(): ?string + { + return $this->header; + } + + public function body(): string + { + return $this->body; + } + + public function action(): ButtonAction + { + return $this->action; + } + + public function footer(): ?string + { + return $this->footer; + } +} diff --git a/src/Request/MessageRequest/RequestButtonReplyMessage.php b/src/Request/MessageRequest/RequestButtonReplyMessage.php new file mode 100644 index 0000000..ab3dc6b --- /dev/null +++ b/src/Request/MessageRequest/RequestButtonReplyMessage.php @@ -0,0 +1,42 @@ + $this->message->messagingProduct(), + 'recipient_type' => $this->message->recipientType(), + 'to' => $this->message->to(), + 'type' => 'interactive', + 'interactive' => [ + 'type' => 'button', + 'body' => ['text' => $this->message->body()], + 'action' => ['buttons' => $this->message->action()->buttons()], + ], + ]; + + if ($this->message->header()) { + $body['interactive']['header'] = [ + 'type' => 'text', + 'text' => $this->message->header(), + ]; + } + + if ($this->message->footer()) { + $body['interactive']['footer'] = [ + 'text' => $this->message->footer(), + ]; + } + + if ($this->message->replyTo()) { + $body['context']['message_id'] = $this->message->replyTo(); + } + + return $body; + } +} diff --git a/src/WhatsAppCloudApi.php b/src/WhatsAppCloudApi.php index ad15cb5..bd46734 100644 --- a/src/WhatsAppCloudApi.php +++ b/src/WhatsAppCloudApi.php @@ -2,6 +2,7 @@ namespace Netflie\WhatsAppCloudApi; +use Netflie\WhatsAppCloudApi\Message\ButtonReply\ButtonAction; use Netflie\WhatsAppCloudApi\Message\Contact\ContactName; use Netflie\WhatsAppCloudApi\Message\Contact\Phone; use Netflie\WhatsAppCloudApi\Message\Media\MediaID; @@ -288,6 +289,27 @@ public function sendList(string $to, string $header, string $body, string $foote return $this->client->sendMessage($request); } + public function sendButton(string $to, string $body, ButtonAction $action, ?string $header = null, ?string $footer = null): Response + { + $message = new Message\ButtonReplyMessage( + $to, + $body, + $action, + $header, + $footer, + $this->reply_to + ); + + $request = new Request\MessageRequest\RequestButtonReplyMessage( + $message, + $this->app->accessToken(), + $this->app->fromPhoneNumberId(), + $this->timeout + ); + + return $this->client->sendMessage($request); + } + /** * Upload a media file (image, audio, video...) to Facebook servers. * diff --git a/tests/Integration/WhatsAppCloudApiTest.php b/tests/Integration/WhatsAppCloudApiTest.php index a6a1044..51ff656 100644 --- a/tests/Integration/WhatsAppCloudApiTest.php +++ b/tests/Integration/WhatsAppCloudApiTest.php @@ -2,6 +2,8 @@ namespace Netflie\WhatsAppCloudApi\Tests\Integration; +use Netflie\WhatsAppCloudApi\Message\ButtonReply\Button; +use Netflie\WhatsAppCloudApi\Message\ButtonReply\ButtonAction; use Netflie\WhatsAppCloudApi\Message\Contact\ContactName; use Netflie\WhatsAppCloudApi\Message\Contact\Phone; use Netflie\WhatsAppCloudApi\Message\Contact\PhoneType; @@ -258,6 +260,29 @@ public function test_send_list() $this->assertEquals(false, $response->isError()); } + public function test_send_reply_buttons() + { + $buttonRows = [ + new Button('button-1', 'Yes'), + new Button('button-2', 'No'), + new Button('button-3', 'Not Now'), + ]; + $buttonAction = new ButtonAction($buttonRows); + $header = 'RATE US'; + $footer = 'Please choose an option'; + + $response = $this->whatsapp_app_cloud_api->sendButton( + WhatsAppCloudApiTestConfiguration::$to_phone_number_id, + 'Would you like to rate us?', + $buttonAction, + $header, + $footer + ); + + $this->assertEquals(200, $response->httpStatusCode()); + $this->assertEquals(false, $response->isError()); + } + public function test_upload_media() { $response = $this->whatsapp_app_cloud_api->uploadMedia('tests/Support/netflie.png'); diff --git a/tests/Unit/WhatsAppCloudApiTest.php b/tests/Unit/WhatsAppCloudApiTest.php index c859184..c04e2f6 100644 --- a/tests/Unit/WhatsAppCloudApiTest.php +++ b/tests/Unit/WhatsAppCloudApiTest.php @@ -6,6 +6,8 @@ use Netflie\WhatsAppCloudApi\Client; use Netflie\WhatsAppCloudApi\Http\ClientHandler; use Netflie\WhatsAppCloudApi\Http\RawResponse; +use Netflie\WhatsAppCloudApi\Message\ButtonReply\Button; +use Netflie\WhatsAppCloudApi\Message\ButtonReply\ButtonAction; use Netflie\WhatsAppCloudApi\Message\Contact\ContactName; use Netflie\WhatsAppCloudApi\Message\Contact\Phone; use Netflie\WhatsAppCloudApi\Message\Contact\PhoneType; @@ -897,6 +899,77 @@ public function test_send_list() $this->assertEquals(false, $response->isError()); } + public function test_send_reply_buttons() + { + $to = $this->faker->phoneNumber; + $url = $this->buildMessageRequestUri(); + $reply_to = $this->faker->uuid; + + $buttonRows = [ + ['id' => $this->faker->uuid, 'title' => $this->faker->text(10)], + ['id' => $this->faker->uuid, 'title' => $this->faker->text(10)], + ['id' => $this->faker->uuid, 'title' => $this->faker->text(10)], + ]; + $buttonAction = ['buttons' => []]; + + foreach ($buttonRows as $button) { + $buttonAction['buttons'][] = [ + 'type' => 'reply', + 'reply' => $button, + ]; + } + + $message = $this->faker->text(50); + $header = $this->faker->text(50); + $footer = $this->faker->text(50); + + $body = [ + 'messaging_product' => 'whatsapp', + 'recipient_type' => 'individual', + 'to' => $to, + 'type' => 'interactive', + 'interactive' => [ + 'type' => 'button', + 'body' => ['text' => $message], + 'action' => $buttonAction, + 'header' => ['type' => 'text', 'text' => $header], + 'footer' => ['text' => $footer], + ], + 'context' => [ + 'message_id' => $reply_to, + ], + ]; + $headers = [ + 'Authorization' => 'Bearer ' . $this->access_token, + ]; + + $this->client_handler + ->postJsonData($url, $body, $headers, Argument::type('int')) + ->shouldBeCalled() + ->willReturn(new RawResponse($headers, $this->successfulMessageNodeResponse(), 200)); + + $actionButtons = []; + + foreach ($buttonRows as $button) { + $actionButtons[] = new Button($button['id'], $button['title']); + } + + $response = $this->whatsapp_app_cloud_api + ->replyTo($reply_to) + ->sendButton( + $to, + $message, + new ButtonAction($actionButtons), + $header, + $footer + ); + + $this->assertEquals(200, $response->httpStatusCode()); + $this->assertEquals(json_decode($this->successfulMessageNodeResponse(), true), $response->decodedBody()); + $this->assertEquals($this->successfulMessageNodeResponse(), $response->body()); + $this->assertEquals(false, $response->isError()); + } + public function test_upload_media() { $url = $this->buildMediaRequestUri();