From d9b45c10ec524cc17aae326ddeb15fb9e7d8fa8c Mon Sep 17 00:00:00 2001 From: Daniele Rapisarda Date: Tue, 25 Sep 2018 18:36:53 +0200 Subject: [PATCH 01/20] added contact driver --- .discovery/Discovery.php | 80 +++++++++++++++++ .discovery/discovery_asset_types.php | 102 ++++++++++++++++++++++ .discovery/discovery_values.php | 21 +++++ discovery.json | 1 + src/Providers/TelegramServiceProvider.php | 2 + src/TelegramDriver.php | 9 +- tests/TelegramDriverTest.php | 40 +++++++++ 7 files changed, 254 insertions(+), 1 deletion(-) create mode 100644 .discovery/Discovery.php create mode 100644 .discovery/discovery_asset_types.php create mode 100644 .discovery/discovery_values.php diff --git a/.discovery/Discovery.php b/.discovery/Discovery.php new file mode 100644 index 0000000..db92a7c --- /dev/null +++ b/.discovery/Discovery.php @@ -0,0 +1,80 @@ +values = require __DIR__.'/discovery_values.php'; + $this->assetTypesArray = require __DIR__.'/discovery_asset_types.php'; + } + + /** + * Returns the unique instance of this class (singleton). + * + * @return self + */ + public static function getInstance(): self + { + if (!self::$instance) { + self::$instance = new self(); + } + return self::$instance; + } + + /** + * Returns the asset values of the requested type. + * + * If no assets are found, an empty array is returned. + * + * @param string $assetType + * @return string[] + */ + public function get(string $assetType) : array + { + return $this->values[$assetType] ?? []; + } + + /** + * Returns an asset type object for the requested type. + * + * If no assets are found, an AssetType object containing no assets is returned. + * + * @param string $assetType + * @return AssetTypeInterface + */ + public function getAssetType(string $assetType) : AssetTypeInterface + { + if (!isset($this->assetTypes[$assetType])) { + if (isset($this->assetTypesArray[$assetType])) { + $this->assetTypes[$assetType] = ImmutableAssetType::fromArray($assetType, $this->assetTypesArray[$assetType]); + } else { + $this->assetTypes[$assetType] = ImmutableAssetType::fromArray($assetType, []); + } + } + return $this->assetTypes[$assetType]; + } +} diff --git a/.discovery/discovery_asset_types.php b/.discovery/discovery_asset_types.php new file mode 100644 index 0000000..78718c5 --- /dev/null +++ b/.discovery/discovery_asset_types.php @@ -0,0 +1,102 @@ + + array ( + 0 => + array ( + 'value' => 'stubs/telegram.php', + 'package' => 'botman/driver-telegram', + 'packageDir' => './', + 'priority' => 0.0, + 'metadata' => + array ( + ), + ), + ), + 'botman/driver' => + array ( + 0 => + array ( + 'value' => 'BotMan\\Drivers\\Telegram\\TelegramDriver', + 'package' => 'botman/driver-telegram', + 'packageDir' => './', + 'priority' => 0.0, + 'metadata' => + array ( + ), + ), + 1 => + array ( + 'value' => 'BotMan\\Drivers\\Telegram\\TelegramAudioDriver', + 'package' => 'botman/driver-telegram', + 'packageDir' => './', + 'priority' => 0.0, + 'metadata' => + array ( + ), + ), + 2 => + array ( + 'value' => 'BotMan\\Drivers\\Telegram\\TelegramFileDriver', + 'package' => 'botman/driver-telegram', + 'packageDir' => './', + 'priority' => 0.0, + 'metadata' => + array ( + ), + ), + 3 => + array ( + 'value' => 'BotMan\\Drivers\\Telegram\\TelegramLocationDriver', + 'package' => 'botman/driver-telegram', + 'packageDir' => './', + 'priority' => 0.0, + 'metadata' => + array ( + ), + ), + 4 => + array ( + 'value' => 'BotMan\\Drivers\\Telegram\\TelegramContactDriver', + 'package' => 'botman/driver-telegram', + 'packageDir' => './', + 'priority' => 0.0, + 'metadata' => + array ( + ), + ), + 5 => + array ( + 'value' => 'BotMan\\Drivers\\Telegram\\TelegramPhotoDriver', + 'package' => 'botman/driver-telegram', + 'packageDir' => './', + 'priority' => 0.0, + 'metadata' => + array ( + ), + ), + 6 => + array ( + 'value' => 'BotMan\\Drivers\\Telegram\\TelegramVideoDriver', + 'package' => 'botman/driver-telegram', + 'packageDir' => './', + 'priority' => 0.0, + 'metadata' => + array ( + ), + ), + ), + 'botman/commands' => + array ( + 0 => + array ( + 'value' => 'BotMan\\Drivers\\Telegram\\Console\\Commands\\TelegramRegisterCommand', + 'package' => 'botman/driver-telegram', + 'packageDir' => './', + 'priority' => 0.0, + 'metadata' => + array ( + ), + ), + ), +); diff --git a/.discovery/discovery_values.php b/.discovery/discovery_values.php new file mode 100644 index 0000000..d6185ba --- /dev/null +++ b/.discovery/discovery_values.php @@ -0,0 +1,21 @@ + + array ( + 0 => 'stubs/telegram.php', + ), + 'botman/driver' => + array ( + 0 => 'BotMan\\Drivers\\Telegram\\TelegramDriver', + 1 => 'BotMan\\Drivers\\Telegram\\TelegramAudioDriver', + 2 => 'BotMan\\Drivers\\Telegram\\TelegramFileDriver', + 3 => 'BotMan\\Drivers\\Telegram\\TelegramLocationDriver', + 4 => 'BotMan\\Drivers\\Telegram\\TelegramContactDriver', + 5 => 'BotMan\\Drivers\\Telegram\\TelegramPhotoDriver', + 6 => 'BotMan\\Drivers\\Telegram\\TelegramVideoDriver', + ), + 'botman/commands' => + array ( + 0 => 'BotMan\\Drivers\\Telegram\\Console\\Commands\\TelegramRegisterCommand', + ), +); diff --git a/discovery.json b/discovery.json index f824228..e371239 100644 --- a/discovery.json +++ b/discovery.json @@ -7,6 +7,7 @@ "BotMan\\Drivers\\Telegram\\TelegramAudioDriver", "BotMan\\Drivers\\Telegram\\TelegramFileDriver", "BotMan\\Drivers\\Telegram\\TelegramLocationDriver", + "BotMan\\Drivers\\Telegram\\TelegramContactDriver", "BotMan\\Drivers\\Telegram\\TelegramPhotoDriver", "BotMan\\Drivers\\Telegram\\TelegramVideoDriver" ], diff --git a/src/Providers/TelegramServiceProvider.php b/src/Providers/TelegramServiceProvider.php index bf5e532..4ae7f2f 100644 --- a/src/Providers/TelegramServiceProvider.php +++ b/src/Providers/TelegramServiceProvider.php @@ -11,6 +11,7 @@ use BotMan\Drivers\Telegram\TelegramVideoDriver; use BotMan\Studio\Providers\StudioServiceProvider; use BotMan\Drivers\Telegram\TelegramLocationDriver; +use BotMan\Drivers\Telegram\TelegramContactDriver; use BotMan\Drivers\Telegram\Console\Commands\TelegramRegisterCommand; class TelegramServiceProvider extends ServiceProvider @@ -46,6 +47,7 @@ protected function loadDrivers() DriverManager::loadDriver(TelegramAudioDriver::class); DriverManager::loadDriver(TelegramFileDriver::class); DriverManager::loadDriver(TelegramLocationDriver::class); + DriverManager::loadDriver(TelegramContactDriver::class); DriverManager::loadDriver(TelegramPhotoDriver::class); DriverManager::loadDriver(TelegramVideoDriver::class); } diff --git a/src/TelegramDriver.php b/src/TelegramDriver.php index 7306ab6..4116b55 100644 --- a/src/TelegramDriver.php +++ b/src/TelegramDriver.php @@ -91,7 +91,7 @@ public function getUser(IncomingMessage $matchingMessage) public function matchesRequest() { $noAttachments = $this->event->keys()->filter(function ($key) { - return in_array($key, ['audio', 'voice', 'video', 'photo', 'location', 'document']); + return in_array($key, ['audio', 'voice', 'video', 'photo', 'location', 'contact', 'document']); })->isEmpty(); return $noAttachments && (! is_null($this->event->get('from')) || ! is_null($this->payload->get('callback_query'))) && ! is_null($this->payload->get('update_id')); @@ -343,6 +343,13 @@ public function buildServicePayload($message, $matchingMessage, $additionalParam if (isset($parameters['title'], $parameters['address'])) { $this->endpoint = 'sendVenue'; } + } elseif ($attachment instanceof Contact) { + $this->endpoint = 'sendContact'; + $parameters['phone_number'] = $attachment->getPhoneNumber(); + $parameters['first_name'] = $attachment->getFirstName(); + $parameters['last_name'] = $attachment->getLastName(); + $parameters['user_id'] = $attachment->getUserId(); + $parameters['vcard'] = $attachment->getVcard(); } } else { $parameters['text'] = $message->getText(); diff --git a/tests/TelegramDriverTest.php b/tests/TelegramDriverTest.php index 6f530df..89b4330 100644 --- a/tests/TelegramDriverTest.php +++ b/tests/TelegramDriverTest.php @@ -17,6 +17,7 @@ use BotMan\BotMan\Drivers\Events\GenericEvent; use Symfony\Component\HttpFoundation\Response; use BotMan\BotMan\Messages\Attachments\Location; +use BotMan\BotMan\Messages\Attachments\Contact; use BotMan\BotMan\Messages\Outgoing\Actions\Button; use BotMan\Drivers\Telegram\Exceptions\TelegramException; @@ -1128,6 +1129,45 @@ public function it_can_reply_message_objects_with_location() $driver->sendPayload($driver->buildServicePayload(\BotMan\BotMan\Messages\Outgoing\OutgoingMessage::create('Test', new Location('123', '321')), $message)); } + /** @test */ + public function it_can_reply_message_objects_with_contact() + { + $responseData = [ + 'update_id' => '1234567890', + 'message' => [ + 'message_id' => '123', + 'from' => [ + 'id' => 'from_id', + ], + 'chat' => [ + 'id' => '12345', + ], + 'date' => '1480369277', + 'text' => 'Telegram Text', + ], + ]; + + $html = m::mock(Curl::class); + $html->shouldReceive('post') + ->once() + ->with('https://api.telegram.org/botTELEGRAM-BOT-TOKEN/sendContact', [], [ + 'chat_id' => '12345', + 'phone_number' => '0775269856', + 'first_name' => 'Daniele', + 'first_name' => 'Rapisarda', + 'user_id' => '123', + 'caption' => 'Test', + ]); + + $request = m::mock(\Symfony\Component\HttpFoundation\Request::class.'[getContent]'); + $request->shouldReceive('getContent')->andReturn(json_encode($responseData)); + + $driver = new TelegramDriver($request, $this->telegramConfig, $html); + + $message = $driver->getMessages()[0]; + $driver->sendPayload($driver->buildServicePayload(\BotMan\BotMan\Messages\Outgoing\OutgoingMessage::create('Test', new Contact('0775269856', 'Daniele', 'Rapisarda', '123')), $message)); + } + /** @test */ public function it_throws_exception_in_get_user() { From a1fdc566f276f560cb2f1d1ddc72c520eac72075 Mon Sep 17 00:00:00 2001 From: Daniele Rapisarda Date: Tue, 25 Sep 2018 18:43:27 +0200 Subject: [PATCH 02/20] removed self-generated files --- .discovery/Discovery.php | 80 --------------------- .discovery/discovery_asset_types.php | 102 --------------------------- .discovery/discovery_values.php | 21 ------ 3 files changed, 203 deletions(-) delete mode 100644 .discovery/Discovery.php delete mode 100644 .discovery/discovery_asset_types.php delete mode 100644 .discovery/discovery_values.php diff --git a/.discovery/Discovery.php b/.discovery/Discovery.php deleted file mode 100644 index db92a7c..0000000 --- a/.discovery/Discovery.php +++ /dev/null @@ -1,80 +0,0 @@ -values = require __DIR__.'/discovery_values.php'; - $this->assetTypesArray = require __DIR__.'/discovery_asset_types.php'; - } - - /** - * Returns the unique instance of this class (singleton). - * - * @return self - */ - public static function getInstance(): self - { - if (!self::$instance) { - self::$instance = new self(); - } - return self::$instance; - } - - /** - * Returns the asset values of the requested type. - * - * If no assets are found, an empty array is returned. - * - * @param string $assetType - * @return string[] - */ - public function get(string $assetType) : array - { - return $this->values[$assetType] ?? []; - } - - /** - * Returns an asset type object for the requested type. - * - * If no assets are found, an AssetType object containing no assets is returned. - * - * @param string $assetType - * @return AssetTypeInterface - */ - public function getAssetType(string $assetType) : AssetTypeInterface - { - if (!isset($this->assetTypes[$assetType])) { - if (isset($this->assetTypesArray[$assetType])) { - $this->assetTypes[$assetType] = ImmutableAssetType::fromArray($assetType, $this->assetTypesArray[$assetType]); - } else { - $this->assetTypes[$assetType] = ImmutableAssetType::fromArray($assetType, []); - } - } - return $this->assetTypes[$assetType]; - } -} diff --git a/.discovery/discovery_asset_types.php b/.discovery/discovery_asset_types.php deleted file mode 100644 index 78718c5..0000000 --- a/.discovery/discovery_asset_types.php +++ /dev/null @@ -1,102 +0,0 @@ - - array ( - 0 => - array ( - 'value' => 'stubs/telegram.php', - 'package' => 'botman/driver-telegram', - 'packageDir' => './', - 'priority' => 0.0, - 'metadata' => - array ( - ), - ), - ), - 'botman/driver' => - array ( - 0 => - array ( - 'value' => 'BotMan\\Drivers\\Telegram\\TelegramDriver', - 'package' => 'botman/driver-telegram', - 'packageDir' => './', - 'priority' => 0.0, - 'metadata' => - array ( - ), - ), - 1 => - array ( - 'value' => 'BotMan\\Drivers\\Telegram\\TelegramAudioDriver', - 'package' => 'botman/driver-telegram', - 'packageDir' => './', - 'priority' => 0.0, - 'metadata' => - array ( - ), - ), - 2 => - array ( - 'value' => 'BotMan\\Drivers\\Telegram\\TelegramFileDriver', - 'package' => 'botman/driver-telegram', - 'packageDir' => './', - 'priority' => 0.0, - 'metadata' => - array ( - ), - ), - 3 => - array ( - 'value' => 'BotMan\\Drivers\\Telegram\\TelegramLocationDriver', - 'package' => 'botman/driver-telegram', - 'packageDir' => './', - 'priority' => 0.0, - 'metadata' => - array ( - ), - ), - 4 => - array ( - 'value' => 'BotMan\\Drivers\\Telegram\\TelegramContactDriver', - 'package' => 'botman/driver-telegram', - 'packageDir' => './', - 'priority' => 0.0, - 'metadata' => - array ( - ), - ), - 5 => - array ( - 'value' => 'BotMan\\Drivers\\Telegram\\TelegramPhotoDriver', - 'package' => 'botman/driver-telegram', - 'packageDir' => './', - 'priority' => 0.0, - 'metadata' => - array ( - ), - ), - 6 => - array ( - 'value' => 'BotMan\\Drivers\\Telegram\\TelegramVideoDriver', - 'package' => 'botman/driver-telegram', - 'packageDir' => './', - 'priority' => 0.0, - 'metadata' => - array ( - ), - ), - ), - 'botman/commands' => - array ( - 0 => - array ( - 'value' => 'BotMan\\Drivers\\Telegram\\Console\\Commands\\TelegramRegisterCommand', - 'package' => 'botman/driver-telegram', - 'packageDir' => './', - 'priority' => 0.0, - 'metadata' => - array ( - ), - ), - ), -); diff --git a/.discovery/discovery_values.php b/.discovery/discovery_values.php deleted file mode 100644 index d6185ba..0000000 --- a/.discovery/discovery_values.php +++ /dev/null @@ -1,21 +0,0 @@ - - array ( - 0 => 'stubs/telegram.php', - ), - 'botman/driver' => - array ( - 0 => 'BotMan\\Drivers\\Telegram\\TelegramDriver', - 1 => 'BotMan\\Drivers\\Telegram\\TelegramAudioDriver', - 2 => 'BotMan\\Drivers\\Telegram\\TelegramFileDriver', - 3 => 'BotMan\\Drivers\\Telegram\\TelegramLocationDriver', - 4 => 'BotMan\\Drivers\\Telegram\\TelegramContactDriver', - 5 => 'BotMan\\Drivers\\Telegram\\TelegramPhotoDriver', - 6 => 'BotMan\\Drivers\\Telegram\\TelegramVideoDriver', - ), - 'botman/commands' => - array ( - 0 => 'BotMan\\Drivers\\Telegram\\Console\\Commands\\TelegramRegisterCommand', - ), -); From 5ced1597ed9c7eae842ca9c09ef61478821172db Mon Sep 17 00:00:00 2001 From: Hanc Date: Fri, 28 Sep 2018 14:39:22 +0200 Subject: [PATCH 03/20] added contact driver (#52) * added contact driver * removed self-generated files --- discovery.json | 1 + src/Providers/TelegramServiceProvider.php | 2 ++ src/TelegramDriver.php | 9 ++++- tests/TelegramDriverTest.php | 40 +++++++++++++++++++++++ 4 files changed, 51 insertions(+), 1 deletion(-) diff --git a/discovery.json b/discovery.json index f824228..e371239 100644 --- a/discovery.json +++ b/discovery.json @@ -7,6 +7,7 @@ "BotMan\\Drivers\\Telegram\\TelegramAudioDriver", "BotMan\\Drivers\\Telegram\\TelegramFileDriver", "BotMan\\Drivers\\Telegram\\TelegramLocationDriver", + "BotMan\\Drivers\\Telegram\\TelegramContactDriver", "BotMan\\Drivers\\Telegram\\TelegramPhotoDriver", "BotMan\\Drivers\\Telegram\\TelegramVideoDriver" ], diff --git a/src/Providers/TelegramServiceProvider.php b/src/Providers/TelegramServiceProvider.php index bf5e532..4ae7f2f 100644 --- a/src/Providers/TelegramServiceProvider.php +++ b/src/Providers/TelegramServiceProvider.php @@ -11,6 +11,7 @@ use BotMan\Drivers\Telegram\TelegramVideoDriver; use BotMan\Studio\Providers\StudioServiceProvider; use BotMan\Drivers\Telegram\TelegramLocationDriver; +use BotMan\Drivers\Telegram\TelegramContactDriver; use BotMan\Drivers\Telegram\Console\Commands\TelegramRegisterCommand; class TelegramServiceProvider extends ServiceProvider @@ -46,6 +47,7 @@ protected function loadDrivers() DriverManager::loadDriver(TelegramAudioDriver::class); DriverManager::loadDriver(TelegramFileDriver::class); DriverManager::loadDriver(TelegramLocationDriver::class); + DriverManager::loadDriver(TelegramContactDriver::class); DriverManager::loadDriver(TelegramPhotoDriver::class); DriverManager::loadDriver(TelegramVideoDriver::class); } diff --git a/src/TelegramDriver.php b/src/TelegramDriver.php index 7306ab6..4116b55 100644 --- a/src/TelegramDriver.php +++ b/src/TelegramDriver.php @@ -91,7 +91,7 @@ public function getUser(IncomingMessage $matchingMessage) public function matchesRequest() { $noAttachments = $this->event->keys()->filter(function ($key) { - return in_array($key, ['audio', 'voice', 'video', 'photo', 'location', 'document']); + return in_array($key, ['audio', 'voice', 'video', 'photo', 'location', 'contact', 'document']); })->isEmpty(); return $noAttachments && (! is_null($this->event->get('from')) || ! is_null($this->payload->get('callback_query'))) && ! is_null($this->payload->get('update_id')); @@ -343,6 +343,13 @@ public function buildServicePayload($message, $matchingMessage, $additionalParam if (isset($parameters['title'], $parameters['address'])) { $this->endpoint = 'sendVenue'; } + } elseif ($attachment instanceof Contact) { + $this->endpoint = 'sendContact'; + $parameters['phone_number'] = $attachment->getPhoneNumber(); + $parameters['first_name'] = $attachment->getFirstName(); + $parameters['last_name'] = $attachment->getLastName(); + $parameters['user_id'] = $attachment->getUserId(); + $parameters['vcard'] = $attachment->getVcard(); } } else { $parameters['text'] = $message->getText(); diff --git a/tests/TelegramDriverTest.php b/tests/TelegramDriverTest.php index 6f530df..89b4330 100644 --- a/tests/TelegramDriverTest.php +++ b/tests/TelegramDriverTest.php @@ -17,6 +17,7 @@ use BotMan\BotMan\Drivers\Events\GenericEvent; use Symfony\Component\HttpFoundation\Response; use BotMan\BotMan\Messages\Attachments\Location; +use BotMan\BotMan\Messages\Attachments\Contact; use BotMan\BotMan\Messages\Outgoing\Actions\Button; use BotMan\Drivers\Telegram\Exceptions\TelegramException; @@ -1128,6 +1129,45 @@ public function it_can_reply_message_objects_with_location() $driver->sendPayload($driver->buildServicePayload(\BotMan\BotMan\Messages\Outgoing\OutgoingMessage::create('Test', new Location('123', '321')), $message)); } + /** @test */ + public function it_can_reply_message_objects_with_contact() + { + $responseData = [ + 'update_id' => '1234567890', + 'message' => [ + 'message_id' => '123', + 'from' => [ + 'id' => 'from_id', + ], + 'chat' => [ + 'id' => '12345', + ], + 'date' => '1480369277', + 'text' => 'Telegram Text', + ], + ]; + + $html = m::mock(Curl::class); + $html->shouldReceive('post') + ->once() + ->with('https://api.telegram.org/botTELEGRAM-BOT-TOKEN/sendContact', [], [ + 'chat_id' => '12345', + 'phone_number' => '0775269856', + 'first_name' => 'Daniele', + 'first_name' => 'Rapisarda', + 'user_id' => '123', + 'caption' => 'Test', + ]); + + $request = m::mock(\Symfony\Component\HttpFoundation\Request::class.'[getContent]'); + $request->shouldReceive('getContent')->andReturn(json_encode($responseData)); + + $driver = new TelegramDriver($request, $this->telegramConfig, $html); + + $message = $driver->getMessages()[0]; + $driver->sendPayload($driver->buildServicePayload(\BotMan\BotMan\Messages\Outgoing\OutgoingMessage::create('Test', new Contact('0775269856', 'Daniele', 'Rapisarda', '123')), $message)); + } + /** @test */ public function it_throws_exception_in_get_user() { From de13e6bacf16ecab3305ae7c88afd51ffcd48ab6 Mon Sep 17 00:00:00 2001 From: Daniele Rapisarda Date: Sun, 7 Oct 2018 14:23:55 +0200 Subject: [PATCH 04/20] extended TelegramDriver with Contact --- src/TelegramContactDriver.php | 68 +++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 src/TelegramContactDriver.php diff --git a/src/TelegramContactDriver.php b/src/TelegramContactDriver.php new file mode 100644 index 0000000..947c9b8 --- /dev/null +++ b/src/TelegramContactDriver.php @@ -0,0 +1,68 @@ +event->get('from')) && ! is_null($this->event->get('contact')); + } + + /** + * @return bool + */ + public function hasMatchingEvent() + { + return false; + } + + /** + * Retrieve the chat message. + * + * @return array + */ + public function getMessages() + { + if (empty($this->messages)) { + $this->loadMessages(); + } + + return $this->messages; + } + + /** + * Load Telegram messages. + */ + public function loadMessages() + { + $message = new IncomingMessage(Contact::PATTERN, $this->event->get('from')['id'], $this->event->get('chat')['id'], $this->event); + $message->setContact(new Contact( + $this->event->get('contact')['phone_number'], + $this->event->get('contact')['first_name'], + $this->event->get('contact')['last_name'], + $this->event->get('contact')['user_id'], + $this->event->get('contact')['vcard'] + )); + + $this->messages = [$message]; + } + + /** + * @return bool + */ + public function isConfigured() + { + return false; + } +} From 812fb55001c810581e2e12f22bb101d870ec4e8c Mon Sep 17 00:00:00 2001 From: Daniele Rapisarda Date: Sun, 7 Oct 2018 14:40:49 +0200 Subject: [PATCH 05/20] added Contact namespace --- src/TelegramDriver.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/TelegramDriver.php b/src/TelegramDriver.php index 4116b55..4b2ace3 100644 --- a/src/TelegramDriver.php +++ b/src/TelegramDriver.php @@ -15,6 +15,7 @@ use BotMan\BotMan\Drivers\Events\GenericEvent; use Symfony\Component\HttpFoundation\Response; use BotMan\BotMan\Messages\Attachments\Location; +use BotMan\BotMan\Messages\Attachments\Contact; use Symfony\Component\HttpFoundation\ParameterBag; use BotMan\BotMan\Messages\Incoming\IncomingMessage; use BotMan\BotMan\Messages\Outgoing\OutgoingMessage; From a9d9f5a53ee5af2304b6c3ce494b23ec69a86ccb Mon Sep 17 00:00:00 2001 From: Steve Hall Date: Thu, 19 Mar 2020 11:15:15 +0000 Subject: [PATCH 06/20] Add configuration option for throw_http_exceptions to ensure that there are no BC breaks. When set to true this will perform various checks on the response from an http call and throw a TelegramConnectionException if there are any problems. The message content will contain debugging information. Have added new tests for this. Also fixed it_throws_exception_in_get_user test as it would not have correctly picked up situation where no exception was thrown. --- .../TelegramConnectionException.php | 9 +++ src/TelegramDriver.php | 51 +++++++++++- tests/TelegramDriverTest.php | 79 ++++++++++++++++++- 3 files changed, 136 insertions(+), 3 deletions(-) create mode 100644 src/Exceptions/TelegramConnectionException.php diff --git a/src/Exceptions/TelegramConnectionException.php b/src/Exceptions/TelegramConnectionException.php new file mode 100644 index 0000000..2c1e3a3 --- /dev/null +++ b/src/Exceptions/TelegramConnectionException.php @@ -0,0 +1,9 @@ + $matchingMessage->getSender(), ]; - $response = $this->http->post($this->buildApiUrl('getChatMember'), [], $parameters); + if ($this->config->get('throw_http_exceptions')) { + $response = $this->postWithExceptionHandling($this->buildApiUrl('getChatMember'), [], $parameters); + } else { + $response = $this->http->post($this->buildApiUrl('getChatMember'), [], $parameters); + } $responseData = json_decode($response->getContent(), true); @@ -245,6 +250,9 @@ public function types(IncomingMessage $matchingMessage) 'action' => 'typing', ]; + if ($this->config->get('throw_http_exceptions')) { + return $this->postWithExceptionHandling($this->buildApiUrl('sendChatAction'), [], $parameters); + } return $this->http->post($this->buildApiUrl('sendChatAction'), [], $parameters); } @@ -283,7 +291,9 @@ private function removeInlineKeyboard($chatId, $messageId) 'message_id' => $messageId, 'inline_keyboard' => [], ]; - + if ($this->config->get('throw_http_exceptions')) { + return $this->postWithExceptionHandling($this->buildApiUrl('editMessageReplyMarkup'), [], $parameters); + } return $this->http->post($this->buildApiUrl('editMessageReplyMarkup'), [], $parameters); } @@ -302,6 +312,7 @@ public function buildServicePayload($message, $matchingMessage, $additionalParam 'chat_id' => $recipient, ], $additionalParameters); + /* * If we send a Question with buttons, ignore * the text and append the question. @@ -367,6 +378,9 @@ public function buildServicePayload($message, $matchingMessage, $additionalParam */ public function sendPayload($payload) { + if ($this->config->get('throw_http_exceptions')) { + return $this->postWithExceptionHandling($this->buildApiUrl($this->endpoint), [], $payload); + } return $this->http->post($this->buildApiUrl($this->endpoint), [], $payload); } @@ -392,6 +406,9 @@ public function sendRequest($endpoint, array $parameters, IncomingMessage $match 'chat_id' => $matchingMessage->getRecipient(), ], $parameters); + if ($this->config->get('throw_http_exceptions')) { + return $this->postWithExceptionHandling($this->buildApiUrl($endpoint), [], $parameters); + } return $this->http->post($this->buildApiUrl($endpoint), [], $parameters); } @@ -416,4 +433,34 @@ protected function buildFileApiUrl($endpoint) { return self::FILE_API_URL.$this->config->get('token').'/'.$endpoint; } + + private function postWithExceptionHandling( + $url, + array $urlParameters = [], + array $postParameters = [], + array $headers = [], + $asJSON = false + ) { + $response = $this->http->post($url, $urlParameters, $postParameters, $headers, $asJSON); + $responseData = json_decode($response->getContent(), true); + if ($response->isOk() && isset($responseData['ok']) && true === $responseData['ok']) { + return $response; + } + $responseData['description'] = $responseData['description'] ?? 'No description from Telegram'; + $responseData['error_code'] = $responseData['error_code'] ?? 'No error code from Telegram'; + $responseData['parameters'] = $responseData['parameters'] ?? 'No parameters from Telegram'; + + + $message = "Status Code: {$response->getStatusCode()}\n". + "Description: ".print_r($responseData['description'], true)."\n". + "Error Code: ".print_r($responseData['error_code'], true)."\n". + "Parameters: ".print_r($responseData['parameters'], true)."\n". + "URL: $url\n". + "URL Parameters: ".print_r($urlParameters,true)."\n". + "Post Parameters: ".print_r($postParameters, true)."\n". + "Headers: ". print_r($headers,true)."\n"; + + throw new TelegramConnectionException($message ); + + } } diff --git a/tests/TelegramDriverTest.php b/tests/TelegramDriverTest.php index 89b4330..c42098b 100644 --- a/tests/TelegramDriverTest.php +++ b/tests/TelegramDriverTest.php @@ -2,6 +2,7 @@ namespace Tests; +use BotMan\Drivers\Telegram\Exceptions\TelegramConnectionException; use Mockery as m; use BotMan\BotMan\Http\Curl; use BotMan\BotMan\Users\User; @@ -911,6 +912,78 @@ public function it_can_reply_message_objects() $driver->sendPayload($driver->buildServicePayload(\BotMan\BotMan\Messages\Outgoing\OutgoingMessage::create('Test'), $message)); } + /** @test */ + public function it_does_not_throw_an_exception_when_a_message_cant_be_sent_and_not_configured() + { + $response = new Response('{ + "ok": false, + "error_code": 400, + "description": "Bad Request: can\'t parse entities: Can\'t find end of Italic entity at byte offset 10" +}', 400); + + + $htmlInterface = m::mock(Curl::class); + $htmlInterface->shouldReceive('post')->with('https://api.telegram.org/botTELEGRAM-BOT-TOKEN/sendMessage', [], [ + 'chat_id' => null, + 'parse_mode' => 'MarkdownV2', + 'text' => 'unparsable_string' + ])->once()->andReturn($response); + + $request = m::mock(\Symfony\Component\HttpFoundation\Request::class.'[getContent]'); + $request->shouldReceive('getContent')->andReturn($response); + + $driver = new TelegramDriver($request, $this->telegramConfig, $htmlInterface); + + $message = $driver->getMessages()[0]; + $throwable = null; + try { + $driver->sendPayload($driver->buildServicePayload(\BotMan\BotMan\Messages\Outgoing\OutgoingMessage::create('unparsable_string'), + $message, ['parse_mode' => 'MarkdownV2'])); + } catch (\Throwable $t) { + $throwable = $t; + throw $t; + } + $this->assertNull($throwable); + + } + + /** @test */ + public function it_throws_an_exception_when_a_message_cant_be_sent_and_configured() + { + $response = new Response('{ + "ok": false, + "error_code": 400, + "description": "Bad Request: can\'t parse entities: Can\'t find end of Italic entity at byte offset 10" +}', 400); + + + $htmlInterface = m::mock(Curl::class); + $htmlInterface->shouldReceive('post')->with('https://api.telegram.org/botTELEGRAM-BOT-TOKEN/sendMessage', [], [ + 'chat_id' => null, + 'parse_mode' => 'MarkdownV2', + 'text' => 'unparsable_string' + ], [], false)->once()->andReturn($response); + + $request = m::mock(\Symfony\Component\HttpFoundation\Request::class.'[getContent]'); + $request->shouldReceive('getContent')->andReturn($response); + + $configurationWithHttpExceptions = $this->telegramConfig; + $configurationWithHttpExceptions['telegram']['throw_http_exceptions'] = true; + + $driver = new TelegramDriver($request, $configurationWithHttpExceptions, $htmlInterface); + + $message = $driver->getMessages()[0]; + $throwable = null; + try { + $driver->sendPayload($driver->buildServicePayload(\BotMan\BotMan\Messages\Outgoing\OutgoingMessage::create('unparsable_string'), + $message, ['parse_mode' => 'MarkdownV2'])); + } catch (\Throwable $t) { + $throwable = $t; + } + $this->assertNotNull($throwable); + $this->assertSame(TelegramConnectionException::class, get_class($throwable)); + } + /** @test */ public function it_can_reply_message_objects_with_image() { @@ -1194,11 +1267,15 @@ public function it_throws_exception_in_get_user() ], ], $htmlInterface); + $throwable = null; try { $driver->getUser($driver->getMessages()[0]); } catch (\Throwable $t) { - $this->assertSame(TelegramException::class, get_class($t)); + $throwable = $t; } + $this->assertNotNull($throwable); + $this->assertSame(TelegramException::class, get_class($throwable)); + } /** @test */ From 0980c4879d73f8f4fec85d690850078825ae0393 Mon Sep 17 00:00:00 2001 From: Steve Hall Date: Thu, 19 Mar 2020 12:00:42 +0000 Subject: [PATCH 07/20] Filter telegram token from exception message. Prevent a credential leak! --- src/TelegramDriver.php | 3 ++- tests/TelegramDriverTest.php | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/TelegramDriver.php b/src/TelegramDriver.php index 937cc9d..bf84a03 100644 --- a/src/TelegramDriver.php +++ b/src/TelegramDriver.php @@ -460,7 +460,8 @@ private function postWithExceptionHandling( "Post Parameters: ".print_r($postParameters, true)."\n". "Headers: ". print_r($headers,true)."\n"; - throw new TelegramConnectionException($message ); + $message = str_replace($this->config->get('token'), 'TELEGRAM-TOKEN-HIDDEN', $message); + throw new TelegramConnectionException($message); } } diff --git a/tests/TelegramDriverTest.php b/tests/TelegramDriverTest.php index c42098b..914e9f8 100644 --- a/tests/TelegramDriverTest.php +++ b/tests/TelegramDriverTest.php @@ -982,6 +982,8 @@ public function it_throws_an_exception_when_a_message_cant_be_sent_and_configure } $this->assertNotNull($throwable); $this->assertSame(TelegramConnectionException::class, get_class($throwable)); + $this->assertNotContains($configurationWithHttpExceptions['telegram']['token'], $throwable->getMessage()); + $this->assertContains('TELEGRAM-TOKEN-HIDDEN', $throwable->getMessage()); } /** @test */ From 5a8d1ca62f45768376256c9dbd33e21a697e3663 Mon Sep 17 00:00:00 2001 From: Steve Hall Date: Sun, 15 Mar 2020 13:17:15 +0000 Subject: [PATCH 08/20] Fix bug in sending Contact and update tests to test both with and without a vcard. --- src/TelegramDriver.php | 5 ++- tests/TelegramDriverTest.php | 62 ++++++++++++++++++++++++++++++++++-- 2 files changed, 63 insertions(+), 4 deletions(-) diff --git a/src/TelegramDriver.php b/src/TelegramDriver.php index bf84a03..b687049 100644 --- a/src/TelegramDriver.php +++ b/src/TelegramDriver.php @@ -11,6 +11,7 @@ use BotMan\BotMan\Messages\Attachments\Audio; use BotMan\BotMan\Messages\Attachments\Image; use BotMan\BotMan\Messages\Attachments\Video; +use BotMan\BotMan\Messages\Attachments\Contact; use BotMan\BotMan\Messages\Outgoing\Question; use Symfony\Component\HttpFoundation\Request; use BotMan\BotMan\Drivers\Events\GenericEvent; @@ -360,7 +361,9 @@ public function buildServicePayload($message, $matchingMessage, $additionalParam $parameters['first_name'] = $attachment->getFirstName(); $parameters['last_name'] = $attachment->getLastName(); $parameters['user_id'] = $attachment->getUserId(); - $parameters['vcard'] = $attachment->getVcard(); + if (null !== $attachment->getVcard()) { + $parameters['vcard'] = $attachment->getVcard(); + } } } else { $parameters['text'] = $message->getText(); diff --git a/tests/TelegramDriverTest.php b/tests/TelegramDriverTest.php index 914e9f8..32a1587 100644 --- a/tests/TelegramDriverTest.php +++ b/tests/TelegramDriverTest.php @@ -1204,7 +1204,7 @@ public function it_can_reply_message_objects_with_location() $driver->sendPayload($driver->buildServicePayload(\BotMan\BotMan\Messages\Outgoing\OutgoingMessage::create('Test', new Location('123', '321')), $message)); } - /** @test */ + /** @test */ public function it_can_reply_message_objects_with_contact() { $responseData = [ @@ -1229,9 +1229,65 @@ public function it_can_reply_message_objects_with_contact() 'chat_id' => '12345', 'phone_number' => '0775269856', 'first_name' => 'Daniele', - 'first_name' => 'Rapisarda', + 'last_name' => 'Rapisarda', + 'user_id' => '123', + 'caption' => 'Test' + ]); + + $request = m::mock(\Symfony\Component\HttpFoundation\Request::class.'[getContent]'); + $request->shouldReceive('getContent')->andReturn(json_encode($responseData)); + + $driver = new TelegramDriver($request, $this->telegramConfig, $html); + + $message = $driver->getMessages()[0]; + $driver->sendPayload($driver->buildServicePayload(\BotMan\BotMan\Messages\Outgoing\OutgoingMessage::create('Test', new Contact('0775269856', 'Daniele', 'Rapisarda', '123')), $message)); + } + + /** @test */ + public function it_can_reply_message_objects_with_contact_with_vcard() + { + $responseData = [ + 'update_id' => '1234567890', + 'message' => [ + 'message_id' => '123', + 'from' => [ + 'id' => 'from_id', + ], + 'chat' => [ + 'id' => '12345', + ], + 'date' => '1480369277', + 'text' => 'Telegram Text', + ], + ]; + + $vcard = 'BEGIN:VCARD +VERSION:4.0 +N:Gump;Forrest;;Mr.; +FN:Forrest Gump +ORG:Bubba Gump Shrimp Co. +TITLE:Shrimp Man +PHOTO;MEDIATYPE=image/gif:http://www.example.com/dir_photos/my_photo.gif +TEL;TYPE=work,voice;VALUE=uri:tel:+1-111-555-1212 +TEL;TYPE=home,voice;VALUE=uri:tel:+1-404-555-1212 +ADR;TYPE=WORK;PREF=1;LABEL="100 Waters Edge\nBaytown\, LA 30314\nUnited States of America":;;100 Waters Edge;Baytown;LA;30314;United States of America +ADR;TYPE=HOME;LABEL="42 Plantation St.\nBaytown\, LA 30314\nUnited States of America":;;42 Plantation St.;Baytown;LA;30314;United States of America +EMAIL:forrestgump@example.com +REV:20080424T195243Z +x-qq:21588891 +END:VCARD'; + + $html = m::mock(Curl::class); + $html->shouldReceive('post') + ->once() + ->with('https://api.telegram.org/botTELEGRAM-BOT-TOKEN/sendContact', [], [ + 'chat_id' => '12345', + 'phone_number' => '0775269856', + 'first_name' => 'Daniele', + 'last_name' => 'Rapisarda', 'user_id' => '123', 'caption' => 'Test', + 'vcard' => $vcard, ]); $request = m::mock(\Symfony\Component\HttpFoundation\Request::class.'[getContent]'); @@ -1240,7 +1296,7 @@ public function it_can_reply_message_objects_with_contact() $driver = new TelegramDriver($request, $this->telegramConfig, $html); $message = $driver->getMessages()[0]; - $driver->sendPayload($driver->buildServicePayload(\BotMan\BotMan\Messages\Outgoing\OutgoingMessage::create('Test', new Contact('0775269856', 'Daniele', 'Rapisarda', '123')), $message)); + $driver->sendPayload($driver->buildServicePayload(\BotMan\BotMan\Messages\Outgoing\OutgoingMessage::create('Test', new Contact('0775269856', 'Daniele', 'Rapisarda', '123', $vcard)), $message)); } /** @test */ From 272e4d8930a910e63129ac9789e7e10282007760 Mon Sep 17 00:00:00 2001 From: Steve Hall Date: Sun, 15 Mar 2020 13:23:21 +0000 Subject: [PATCH 09/20] Cs Fixer --- tests/TelegramDriverTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/TelegramDriverTest.php b/tests/TelegramDriverTest.php index 32a1587..7770e4b 100644 --- a/tests/TelegramDriverTest.php +++ b/tests/TelegramDriverTest.php @@ -1243,7 +1243,7 @@ public function it_can_reply_message_objects_with_contact() $driver->sendPayload($driver->buildServicePayload(\BotMan\BotMan\Messages\Outgoing\OutgoingMessage::create('Test', new Contact('0775269856', 'Daniele', 'Rapisarda', '123')), $message)); } - /** @test */ + /** @test */ public function it_can_reply_message_objects_with_contact_with_vcard() { $responseData = [ @@ -1285,8 +1285,8 @@ public function it_can_reply_message_objects_with_contact_with_vcard() 'phone_number' => '0775269856', 'first_name' => 'Daniele', 'last_name' => 'Rapisarda', - 'user_id' => '123', - 'caption' => 'Test', + 'user_id' => '123', + 'caption' => 'Test', 'vcard' => $vcard, ]); From 1abc3bdd6f51ab82726c95140aafa069704104c1 Mon Sep 17 00:00:00 2001 From: Steve Hall Date: Sun, 15 Mar 2020 13:34:47 +0000 Subject: [PATCH 10/20] The documentation for the free styleCI config is here: https://docs.styleci.io/standalone-php linter is not mentioned. Removed to fix ``` Config Issue The provided config option 'linting' was not valid. ``` --- .styleci.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.styleci.yml b/.styleci.yml index a2f2088..930de3b 100644 --- a/.styleci.yml +++ b/.styleci.yml @@ -1,6 +1,4 @@ preset: laravel enabled: - - unalign_double_arrow - -linting: true \ No newline at end of file + - unalign_double_arrow \ No newline at end of file From 48baadd7ace982fa460962a0dc13cd45cb0a4bc5 Mon Sep 17 00:00:00 2001 From: Steve Hall Date: Sat, 4 Apr 2020 14:28:04 +0100 Subject: [PATCH 11/20] Add option to retry for server failures and 429 rate warnings. --- src/TelegramDriver.php | 23 ++++++++++- tests/TelegramDriverTest.php | 77 ++++++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+), 1 deletion(-) diff --git a/src/TelegramDriver.php b/src/TelegramDriver.php index b687049..c95f39a 100644 --- a/src/TelegramDriver.php +++ b/src/TelegramDriver.php @@ -63,6 +63,7 @@ public function buildPayload(Request $request) * @param IncomingMessage $matchingMessage * @return User * @throws TelegramException + * @throws TelegramConnectionException */ public function getUser(IncomingMessage $matchingMessage) { @@ -437,17 +438,37 @@ protected function buildFileApiUrl($endpoint) return self::FILE_API_URL.$this->config->get('token').'/'.$endpoint; } + /** + * @param $url + * @param array $urlParameters + * @param array $postParameters + * @param array $headers + * @param bool $asJSON + * @param int $retryCount + * @return Response + * @throws TelegramConnectionException + */ private function postWithExceptionHandling( $url, array $urlParameters = [], array $postParameters = [], array $headers = [], - $asJSON = false + $asJSON = false, + int $retryCount = 0 ) { $response = $this->http->post($url, $urlParameters, $postParameters, $headers, $asJSON); $responseData = json_decode($response->getContent(), true); if ($response->isOk() && isset($responseData['ok']) && true === $responseData['ok']) { return $response; + } elseif ($this->config->get('retry_http_exceptions') && $retryCount <= $this->config->get('retry_http_exceptions') ) { + $retryCount++; + if ($response->getStatusCode() == 429 && isset($responseData['retry_after']) && is_numeric($responseData['retry_after'])) { + usleep($responseData['retry_after'] * 1000000); + } else { + $multiplier = $this->config->get('retry_http_exceptions_multiplier')??2; + usleep($retryCount*$multiplier* 1000000); + } + return $this->postWithExceptionHandling($url,$urlParameters, $postParameters, $headers , $asJSON, $retryCount); } $responseData['description'] = $responseData['description'] ?? 'No description from Telegram'; $responseData['error_code'] = $responseData['error_code'] ?? 'No error code from Telegram'; diff --git a/tests/TelegramDriverTest.php b/tests/TelegramDriverTest.php index 7770e4b..a28763d 100644 --- a/tests/TelegramDriverTest.php +++ b/tests/TelegramDriverTest.php @@ -986,6 +986,83 @@ public function it_throws_an_exception_when_a_message_cant_be_sent_and_configure $this->assertContains('TELEGRAM-TOKEN-HIDDEN', $throwable->getMessage()); } + /** @test */ + public function it_retries_after_throwing_an_exception_when_a_message_cant_be_sent_and_configured() + { + $responseFailed = new Response('', 500); + $responseSucceeds = new Response('{"ok": true}', 200); + + $htmlInterface = m::mock(Curl::class); + $htmlInterface->shouldReceive('post')->with('https://api.telegram.org/botTELEGRAM-BOT-TOKEN/sendMessage', [], [ + 'chat_id' => null, + 'parse_mode' => 'MarkdownV2', + 'text' => 'a message' + ], [], false)->times(3)->andReturn(clone $responseFailed, clone $responseFailed, $responseSucceeds ); + + $request = m::mock(\Symfony\Component\HttpFoundation\Request::class.'[getContent]'); + $request->shouldReceive('getContent')->andReturn($responseFailed); + + $configurationWithHttpExceptions = $this->telegramConfig; + $configurationWithHttpExceptions['telegram']['throw_http_exceptions'] = true; + $configurationWithHttpExceptions['telegram']['retry_http_exceptions'] = 5; + $configurationWithHttpExceptions['telegram']['retry_http_exceptions_multiplier'] = 0.1; // to keep the tests going at a reasonable speed. + + $driver = new TelegramDriver($request, $configurationWithHttpExceptions, $htmlInterface); + + + $message = $driver->getMessages()[0]; + $throwable = null; + $duration = 0.0; + try { + $start = microtime(true); + $driver->sendPayload($driver->buildServicePayload(\BotMan\BotMan\Messages\Outgoing\OutgoingMessage::create('a message'), + $message, ['parse_mode' => 'MarkdownV2'])); + $duration = microtime(true) - $start; + } catch (\Throwable $t) { + $throwable = $t; + } + $this->assertNull($throwable); + $this->assertGreaterThanOrEqual(0.1+0.2, $duration); + } + + /** @test */ + public function it_respects_the_back_off_in_a_429_response() + { + $response429 = new Response('{"retry_after": 1.5}', 429); + $responseSucceeds = new Response('{"ok": true}', 200); + + $htmlInterface = m::mock(Curl::class); + $htmlInterface->shouldReceive('post')->with('https://api.telegram.org/botTELEGRAM-BOT-TOKEN/sendMessage', [], [ + 'chat_id' => null, + 'parse_mode' => 'MarkdownV2', + 'text' => 'a message' + ], [], false)->times(2)->andReturn($response429, $responseSucceeds ); + + $request = m::mock(\Symfony\Component\HttpFoundation\Request::class.'[getContent]'); + $request->shouldReceive('getContent')->andReturn($response429); + + $configurationWithHttpExceptions = $this->telegramConfig; + $configurationWithHttpExceptions['telegram']['throw_http_exceptions'] = true; + $configurationWithHttpExceptions['telegram']['retry_http_exceptions'] = 5; + + $driver = new TelegramDriver($request, $configurationWithHttpExceptions, $htmlInterface); + + + $message = $driver->getMessages()[0]; + $throwable = null; + $duration = 0.0; + try { + $start = microtime(true); + $driver->sendPayload($driver->buildServicePayload(\BotMan\BotMan\Messages\Outgoing\OutgoingMessage::create('a message'), + $message, ['parse_mode' => 'MarkdownV2'])); + $duration = microtime(true) - $start; + } catch (\Throwable $t) { + $throwable = $t; + } + $this->assertNull($throwable); + $this->assertGreaterThanOrEqual(1.5, $duration); + } + /** @test */ public function it_can_reply_message_objects_with_image() { From 72971b59bbfa658b3c56fe168c456ebb7dbc2636 Mon Sep 17 00:00:00 2001 From: Steve Hall Date: Sat, 4 Apr 2020 14:43:22 +0100 Subject: [PATCH 12/20] Change .styleci to match PSR2 as stated in contributing guidelines, and update files to PSR2 rules. --- .styleci.yml | 5 +--- src/TelegramDriver.php | 40 ++++++++++++++++++++---------- tests/TelegramDriverTest.php | 48 ++++++++++++++++++++++-------------- 3 files changed, 58 insertions(+), 35 deletions(-) diff --git a/.styleci.yml b/.styleci.yml index 930de3b..247a09c 100644 --- a/.styleci.yml +++ b/.styleci.yml @@ -1,4 +1 @@ -preset: laravel - -enabled: - - unalign_double_arrow \ No newline at end of file +preset: psr2 diff --git a/src/TelegramDriver.php b/src/TelegramDriver.php index c95f39a..810928d 100644 --- a/src/TelegramDriver.php +++ b/src/TelegramDriver.php @@ -86,8 +86,13 @@ public function getUser(IncomingMessage $matchingMessage) $userData = Collection::make($responseData['result']['user']); - return new User($userData->get('id'), $userData->get('first_name'), $userData->get('last_name'), - $userData->get('username'), $responseData['result']); + return new User( + $userData->get('id'), + $userData->get('first_name'), + $userData->get('last_name'), + $userData->get('username'), + $responseData['result'] + ); } /** @@ -170,8 +175,10 @@ public function messagesHandled() if ($callback !== null) { $callback['message']['chat']['id']; - $this->removeInlineKeyboard($callback['message']['chat']['id'], - $callback['message']['message_id']); + $this->removeInlineKeyboard( + $callback['message']['chat']['id'], + $callback['message']['message_id'] + ); } } @@ -216,8 +223,12 @@ public function loadMessages() $callback = Collection::make($this->payload->get('callback_query')); $messages = [ - new IncomingMessage($callback->get('data'), $callback->get('from')['id'], - $callback->get('message')['chat']['id'], $callback->get('message')), + new IncomingMessage( + $callback->get('data'), + $callback->get('from')['id'], + $callback->get('message')['chat']['id'], + $callback->get('message') + ), ]; } elseif ($this->isValidLoginRequest()) { $messages = [ @@ -225,8 +236,12 @@ public function loadMessages() ]; } else { $messages = [ - new IncomingMessage($this->event->get('text'), $this->event->get('from')['id'], $this->event->get('chat')['id'], - $this->event), + new IncomingMessage( + $this->event->get('text'), + $this->event->get('from')['id'], + $this->event->get('chat')['id'], + $this->event + ), ]; } @@ -460,7 +475,7 @@ private function postWithExceptionHandling( $responseData = json_decode($response->getContent(), true); if ($response->isOk() && isset($responseData['ok']) && true === $responseData['ok']) { return $response; - } elseif ($this->config->get('retry_http_exceptions') && $retryCount <= $this->config->get('retry_http_exceptions') ) { + } elseif ($this->config->get('retry_http_exceptions') && $retryCount <= $this->config->get('retry_http_exceptions')) { $retryCount++; if ($response->getStatusCode() == 429 && isset($responseData['retry_after']) && is_numeric($responseData['retry_after'])) { usleep($responseData['retry_after'] * 1000000); @@ -468,7 +483,7 @@ private function postWithExceptionHandling( $multiplier = $this->config->get('retry_http_exceptions_multiplier')??2; usleep($retryCount*$multiplier* 1000000); } - return $this->postWithExceptionHandling($url,$urlParameters, $postParameters, $headers , $asJSON, $retryCount); + return $this->postWithExceptionHandling($url, $urlParameters, $postParameters, $headers, $asJSON, $retryCount); } $responseData['description'] = $responseData['description'] ?? 'No description from Telegram'; $responseData['error_code'] = $responseData['error_code'] ?? 'No error code from Telegram'; @@ -480,12 +495,11 @@ private function postWithExceptionHandling( "Error Code: ".print_r($responseData['error_code'], true)."\n". "Parameters: ".print_r($responseData['parameters'], true)."\n". "URL: $url\n". - "URL Parameters: ".print_r($urlParameters,true)."\n". + "URL Parameters: ".print_r($urlParameters, true)."\n". "Post Parameters: ".print_r($postParameters, true)."\n". - "Headers: ". print_r($headers,true)."\n"; + "Headers: ". print_r($headers, true)."\n"; $message = str_replace($this->config->get('token'), 'TELEGRAM-TOKEN-HIDDEN', $message); throw new TelegramConnectionException($message); - } } diff --git a/tests/TelegramDriverTest.php b/tests/TelegramDriverTest.php index a28763d..5d4a210 100644 --- a/tests/TelegramDriverTest.php +++ b/tests/TelegramDriverTest.php @@ -937,14 +937,16 @@ public function it_does_not_throw_an_exception_when_a_message_cant_be_sent_and_n $message = $driver->getMessages()[0]; $throwable = null; try { - $driver->sendPayload($driver->buildServicePayload(\BotMan\BotMan\Messages\Outgoing\OutgoingMessage::create('unparsable_string'), - $message, ['parse_mode' => 'MarkdownV2'])); - } catch (\Throwable $t) { + $driver->sendPayload($driver->buildServicePayload( + \BotMan\BotMan\Messages\Outgoing\OutgoingMessage::create('unparsable_string'), + $message, + ['parse_mode' => 'MarkdownV2'] + )); + } catch (\Throwable $t) { $throwable = $t; throw $t; } $this->assertNull($throwable); - } /** @test */ @@ -975,9 +977,12 @@ public function it_throws_an_exception_when_a_message_cant_be_sent_and_configure $message = $driver->getMessages()[0]; $throwable = null; try { - $driver->sendPayload($driver->buildServicePayload(\BotMan\BotMan\Messages\Outgoing\OutgoingMessage::create('unparsable_string'), - $message, ['parse_mode' => 'MarkdownV2'])); - } catch (\Throwable $t) { + $driver->sendPayload($driver->buildServicePayload( + \BotMan\BotMan\Messages\Outgoing\OutgoingMessage::create('unparsable_string'), + $message, + ['parse_mode' => 'MarkdownV2'] + )); + } catch (\Throwable $t) { $throwable = $t; } $this->assertNotNull($throwable); @@ -997,7 +1002,7 @@ public function it_retries_after_throwing_an_exception_when_a_message_cant_be_se 'chat_id' => null, 'parse_mode' => 'MarkdownV2', 'text' => 'a message' - ], [], false)->times(3)->andReturn(clone $responseFailed, clone $responseFailed, $responseSucceeds ); + ], [], false)->times(3)->andReturn(clone $responseFailed, clone $responseFailed, $responseSucceeds); $request = m::mock(\Symfony\Component\HttpFoundation\Request::class.'[getContent]'); $request->shouldReceive('getContent')->andReturn($responseFailed); @@ -1015,10 +1020,13 @@ public function it_retries_after_throwing_an_exception_when_a_message_cant_be_se $duration = 0.0; try { $start = microtime(true); - $driver->sendPayload($driver->buildServicePayload(\BotMan\BotMan\Messages\Outgoing\OutgoingMessage::create('a message'), - $message, ['parse_mode' => 'MarkdownV2'])); + $driver->sendPayload($driver->buildServicePayload( + \BotMan\BotMan\Messages\Outgoing\OutgoingMessage::create('a message'), + $message, + ['parse_mode' => 'MarkdownV2'] + )); $duration = microtime(true) - $start; - } catch (\Throwable $t) { + } catch (\Throwable $t) { $throwable = $t; } $this->assertNull($throwable); @@ -1036,7 +1044,7 @@ public function it_respects_the_back_off_in_a_429_response() 'chat_id' => null, 'parse_mode' => 'MarkdownV2', 'text' => 'a message' - ], [], false)->times(2)->andReturn($response429, $responseSucceeds ); + ], [], false)->times(2)->andReturn($response429, $responseSucceeds); $request = m::mock(\Symfony\Component\HttpFoundation\Request::class.'[getContent]'); $request->shouldReceive('getContent')->andReturn($response429); @@ -1053,10 +1061,13 @@ public function it_respects_the_back_off_in_a_429_response() $duration = 0.0; try { $start = microtime(true); - $driver->sendPayload($driver->buildServicePayload(\BotMan\BotMan\Messages\Outgoing\OutgoingMessage::create('a message'), - $message, ['parse_mode' => 'MarkdownV2'])); + $driver->sendPayload($driver->buildServicePayload( + \BotMan\BotMan\Messages\Outgoing\OutgoingMessage::create('a message'), + $message, + ['parse_mode' => 'MarkdownV2'] + )); $duration = microtime(true) - $start; - } catch (\Throwable $t) { + } catch (\Throwable $t) { $throwable = $t; } $this->assertNull($throwable); @@ -1204,8 +1215,10 @@ public function it_can_reply_message_objects_with_audio() $driver = new TelegramDriver($request, $this->telegramConfig, $html); $message = $driver->getMessages()[0]; - $driver->sendPayload($driver->buildServicePayload(\BotMan\BotMan\Messages\Outgoing\OutgoingMessage::create('Test', - Audio::url('http://image.url/foo.mp3')), $message)); + $driver->sendPayload($driver->buildServicePayload(\BotMan\BotMan\Messages\Outgoing\OutgoingMessage::create( + 'Test', + Audio::url('http://image.url/foo.mp3') + ), $message)); } /** @test */ @@ -1410,7 +1423,6 @@ public function it_throws_exception_in_get_user() } $this->assertNotNull($throwable); $this->assertSame(TelegramException::class, get_class($throwable)); - } /** @test */ From 21b424e00d364a44b52aab2162551a401475d95f Mon Sep 17 00:00:00 2001 From: Steve Hall Date: Fri, 22 May 2020 21:50:54 +0100 Subject: [PATCH 13/20] Fix bug and failing tests in sendContact (#71) * Fix bug in sending Contact and update tests to test both with and without a vcard. * Cs Fixer Co-authored-by: Steve Hall From 7e6499aebb81f90ab6bdf8d6c288a9ce23d17dbf Mon Sep 17 00:00:00 2001 From: Steve Hall Date: Fri, 22 May 2020 22:01:50 +0100 Subject: [PATCH 14/20] Add configuration option to enable setting of default additional options to be sent with every request. Specifically this is to enable defaulting all messages to Markdown format by having the following config: (#70) ``` 'telegram' => [ 'token' => 'YOUR-TELEGRAM-TOKEN-HERE', 'default_additional_parameters' => [ 'parse_mode' => 'Markdown', ], ] ``` Co-authored-by: Steve Hall Co-authored-by: FeralHeart --- src/TelegramDriver.php | 6 +- tests/TelegramDriverTest.php | 124 +++++++++++++++++++++++++++++++++++ 2 files changed, 127 insertions(+), 3 deletions(-) diff --git a/src/TelegramDriver.php b/src/TelegramDriver.php index 810928d..43bfcdd 100644 --- a/src/TelegramDriver.php +++ b/src/TelegramDriver.php @@ -325,11 +325,11 @@ public function buildServicePayload($message, $matchingMessage, $additionalParam $this->endpoint = 'sendMessage'; $recipient = $matchingMessage->getRecipient() === '' ? $matchingMessage->getSender() : $matchingMessage->getRecipient(); + $defaultAdditionalParameters = $this->config->get('default_additional_parameters', []); $parameters = array_merge_recursive([ 'chat_id' => $recipient, - ], $additionalParameters); - - + ], $additionalParameters + $defaultAdditionalParameters); + /* * If we send a Question with buttons, ignore * the text and append the question. diff --git a/tests/TelegramDriverTest.php b/tests/TelegramDriverTest.php index 5d4a210..065f31b 100644 --- a/tests/TelegramDriverTest.php +++ b/tests/TelegramDriverTest.php @@ -30,6 +30,15 @@ class TelegramDriverTest extends PHPUnit_Framework_TestCase ], ]; + protected $telegramConfigWithDefaultAdditionalParameters = [ + 'telegram' => [ + 'token' => 'TELEGRAM-BOT-TOKEN', + 'default_additional_parameters' => [ + 'foo' => 'bar' + ] + ], + ]; + public function tearDown() { m::close(); @@ -853,6 +862,121 @@ public function it_can_reply_with_additional_parameters() ])); } + /** @test */ + public function it_can_reply_with_default_additional_parameters() + { + $responseData = [ + 'update_id' => '1234567890', + 'message' => [ + 'message_id' => '123', + 'from' => [ + 'id' => 'from_id', + ], + 'chat' => [ + 'id' => '12345', + ], + 'date' => '1480369277', + 'text' => 'Telegram Text', + ], + ]; + + $html = m::mock(Curl::class); + $html->shouldReceive('post') + ->once() + ->with('https://api.telegram.org/botTELEGRAM-BOT-TOKEN/sendMessage', [], [ + 'chat_id' => '12345', + 'text' => 'Test', + 'foo' => 'bar', + ]); + + $request = m::mock(\Symfony\Component\HttpFoundation\Request::class.'[getContent]'); + $request->shouldReceive('getContent')->andReturn(json_encode($responseData)); + + $driver = new TelegramDriver($request, $this->telegramConfigWithDefaultAdditionalParameters, $html); + + $message = $driver->getMessages()[0]; + $driver->sendPayload($driver->buildServicePayload('Test', $message)); + } + + /** @test */ + public function it_can_reply_with_default_additional_parameters_and_runtime_additional_parameters() + { + $responseData = [ + 'update_id' => '1234567890', + 'message' => [ + 'message_id' => '123', + 'from' => [ + 'id' => 'from_id', + ], + 'chat' => [ + 'id' => '12345', + ], + 'date' => '1480369277', + 'text' => 'Telegram Text', + ], + ]; + + $html = m::mock(Curl::class); + $html->shouldReceive('post') + ->once() + ->with('https://api.telegram.org/botTELEGRAM-BOT-TOKEN/sendMessage', [], [ + 'chat_id' => '12345', + 'text' => 'Test', + 'foo' => 'bar', + 'baz' => 'foo', + ]); + + $request = m::mock(\Symfony\Component\HttpFoundation\Request::class.'[getContent]'); + $request->shouldReceive('getContent')->andReturn(json_encode($responseData)); + + $driver = new TelegramDriver($request, $this->telegramConfigWithDefaultAdditionalParameters, $html); + + $message = $driver->getMessages()[0]; + $driver->sendPayload($driver->buildServicePayload('Test', $message, [ + 'baz' => 'foo', + ])); + } + + /** @test */ + public function it_can_reply_with_default_additional_parameters_overriden_by_runtime_additional_parameters() + { + $responseData = [ + 'update_id' => '1234567890', + 'message' => [ + 'message_id' => '123', + 'from' => [ + 'id' => 'from_id', + ], + 'chat' => [ + 'id' => '12345', + ], + 'date' => '1480369277', + 'text' => 'Telegram Text', + ], + ]; + + $html = m::mock(Curl::class); + $html->shouldReceive('post') + ->once() + ->with('https://api.telegram.org/botTELEGRAM-BOT-TOKEN/sendMessage', [], [ + 'chat_id' => '12345', + 'text' => 'Test', + 'foo' => 'baz', + 'baz' => 'foo', + ]); + + $request = m::mock(\Symfony\Component\HttpFoundation\Request::class.'[getContent]'); + $request->shouldReceive('getContent')->andReturn(json_encode($responseData)); + + $driver = new TelegramDriver($request, $this->telegramConfigWithDefaultAdditionalParameters, $html); + + $message = $driver->getMessages()[0]; + $driver->sendPayload($driver->buildServicePayload('Test', $message, [ + 'foo' => 'baz', + 'baz' => 'foo', + ])); + } + /** @test */ public function it_is_configured() { From 8917d9ccac2055d948f2666cebfd076d906dff3c Mon Sep 17 00:00:00 2001 From: Arthur Date: Sat, 23 May 2020 03:02:44 +0600 Subject: [PATCH 15/20] Create ISSUE_TEMPLATE.md (#59) Co-authored-by: antimech --- ISSUE_TEMPLATE.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 ISSUE_TEMPLATE.md diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md new file mode 100644 index 0000000..8f13de0 --- /dev/null +++ b/ISSUE_TEMPLATE.md @@ -0,0 +1,20 @@ + + + + +### Required Information + +- BotMan version: +- BotMan Telegram Driver version: + +### Expected behaviour + + +### Actual behaviour + + +### Steps to reproduce + + +### Extra details + From 22cae51280b6f3debdf7024ae918097c51c60f9c Mon Sep 17 00:00:00 2001 From: Arthur Date: Sat, 23 May 2020 03:03:22 +0600 Subject: [PATCH 16/20] Fix repo link in CONTRIBUTING.md (#57) Co-authored-by: antimech --- CONTRIBUTING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 66f7971..d5536a4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,6 @@ # Contributing -Contributions are **welcome** and will be fully **credited**. We accept contributions via Pull Requests on [Github](https://github.com/botman/driver-facebook). +Contributions are **welcome** and will be fully **credited**. We accept contributions via Pull Requests on [Github](https://github.com/botman/driver-telegram). ## Pull Requests @@ -19,4 +19,4 @@ $ phpunit ``` -*Happy coding!* \ No newline at end of file +*Happy coding!* From a7e8c52a73d8d144935c0577965f2dac2f5cf698 Mon Sep 17 00:00:00 2001 From: FeralHeart Date: Fri, 22 May 2020 23:04:22 +0200 Subject: [PATCH 17/20] Add channel_post to buildPayload (#69) --- src/TelegramDriver.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/TelegramDriver.php b/src/TelegramDriver.php index 43bfcdd..b23baf0 100644 --- a/src/TelegramDriver.php +++ b/src/TelegramDriver.php @@ -54,6 +54,10 @@ public function buildPayload(Request $request) if (empty($message)) { $message = $this->payload->get('edited_message'); } + if (empty($message)) { + $message = $this->payload->get('channel_post'); + $message['from'] = ['id' => 0]; + } $this->event = Collection::make($message); $this->config = Collection::make($this->config->get('telegram')); $this->queryParameters = Collection::make($request->query); From 86cf9515a933433fa9fde351052863269d55e9f9 Mon Sep 17 00:00:00 2001 From: Arthur Date: Sat, 23 May 2020 03:06:32 +0600 Subject: [PATCH 18/20] Update book link (#58) Use https instead of http. Co-authored-by: antimech --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d5536a4..7353ce3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -10,7 +10,7 @@ Contributions are **welcome** and will be fully **credited**. We accept contribu - **Consider our release cycle.** We try to follow [SemVer v2.0.0](http://semver.org/). Randomly breaking public APIs is not an option. - **Create feature branches.** Don't ask us to pull from your master branch. - **One pull request per feature.** If you want to do more than one thing, send multiple pull requests. -- **Send coherent history.** Make sure each individual commit in your pull request is meaningful. If you had to make multiple intermediate commits while developing, please [squash them](http://www.git-scm.com/book/en/v2/Git-Tools-Rewriting-History#Changing-Multiple-Commit-Messages) before submitting. +- **Send coherent history.** Make sure each individual commit in your pull request is meaningful. If you had to make multiple intermediate commits while developing, please [squash them](https://www.git-scm.com/book/en/v2/Git-Tools-Rewriting-History#Changing-Multiple-Commit-Messages) before submitting. ## Running Tests From da9a9cb044d8f4dc3bb77200f3da80ce527c861a Mon Sep 17 00:00:00 2001 From: kubk Date: Sat, 23 May 2020 00:21:37 +0300 Subject: [PATCH 19/20] Don't remove inline keyboard automatically (Closes #44, closes #29, also closes botman/botman#709) (#48) --- src/TelegramDriver.php | 3 ++- tests/TelegramDriverTest.php | 45 +++++++++++++++++++++++++++++++++++- 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/src/TelegramDriver.php b/src/TelegramDriver.php index b23baf0..a907b2a 100644 --- a/src/TelegramDriver.php +++ b/src/TelegramDriver.php @@ -176,8 +176,9 @@ protected function isValidLoginRequest() public function messagesHandled() { $callback = $this->payload->get('callback_query'); + $hideInlineKeyboard = $this->config->get('hideInlineKeyboard', true); - if ($callback !== null) { + if ($callback !== null && $hideInlineKeyboard) { $callback['message']['chat']['id']; $this->removeInlineKeyboard( $callback['message']['chat']['id'], diff --git a/tests/TelegramDriverTest.php b/tests/TelegramDriverTest.php index 065f31b..3fd7f8f 100644 --- a/tests/TelegramDriverTest.php +++ b/tests/TelegramDriverTest.php @@ -637,7 +637,7 @@ public function it_returns_answer_from_interactive_messages() } /** @test */ - public function it_hides_keyboard() + public function it_hides_keyboard_by_default() { $responseData = [ 'update_id' => '1234567890', @@ -678,6 +678,49 @@ public function it_hides_keyboard() $driver->messagesHandled(); } + /** @test */ + public function it_does_not_hide_keyboard_if_configured() + { + $responseData = [ + 'update_id' => '1234567890', + 'callback_query' => [ + 'id' => '11717237123', + 'from' => [ + 'id' => 'from_id', + ], + 'message' => [ + 'message_id' => '123', + 'from' => [ + 'id' => 'from_id', + ], + 'chat' => [ + 'id' => '1234', + ], + 'date' => '1480369277', + 'text' => 'Telegram Text', + ], + 'data' => 'FooBar', + ], + ]; + + $html = m::mock(Curl::class); + $html->shouldNotReceive('post'); + + $request = m::mock(Request::class.'[getContent]'); + $request->shouldReceive('getContent')->andReturn(json_encode($responseData)); + + $telegramConfig = [ + 'telegram' => [ + 'token' => 'TELEGRAM-BOT-TOKEN', + 'hideInlineKeyboard' => false + ], + ]; + $driver = new TelegramDriver($request, $telegramConfig, $html); + + $driver->messagesHandled(); + } + + /** @test */ public function it_can_reply_string_messages() { From 8a6d1636d6117e1c567604fbe332427c31fa0ec7 Mon Sep 17 00:00:00 2001 From: FeralHeart Date: Sat, 23 May 2020 00:03:45 +0200 Subject: [PATCH 20/20] Added Hall-of-fame to README (#78) --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 64e44f9..51e4222 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,8 @@ BotMan driver to connect Telegram with [BotMan](https://github.com/botman/botman Please see [CONTRIBUTING](CONTRIBUTING.md) for details. +[![](https://sourcerer.io/fame/feralheart/botman/driver-telegram/images/0)](https://sourcerer.io/fame/feralheart/botman/driver-telegram/links/0)[![](https://sourcerer.io/fame/feralheart/botman/driver-telegram/images/1)](https://sourcerer.io/fame/feralheart/botman/driver-telegram/links/1)[![](https://sourcerer.io/fame/feralheart/botman/driver-telegram/images/2)](https://sourcerer.io/fame/feralheart/botman/driver-telegram/links/2)[![](https://sourcerer.io/fame/feralheart/botman/driver-telegram/images/3)](https://sourcerer.io/fame/feralheart/botman/driver-telegram/links/3)[![](https://sourcerer.io/fame/feralheart/botman/driver-telegram/images/4)](https://sourcerer.io/fame/feralheart/botman/driver-telegram/links/4)[![](https://sourcerer.io/fame/feralheart/botman/driver-telegram/images/5)](https://sourcerer.io/fame/feralheart/botman/driver-telegram/links/5)[![](https://sourcerer.io/fame/feralheart/botman/driver-telegram/images/6)](https://sourcerer.io/fame/feralheart/botman/driver-telegram/links/6)[![](https://sourcerer.io/fame/feralheart/botman/driver-telegram/images/7)](https://sourcerer.io/fame/feralheart/botman/driver-telegram/links/7) + ## Security Vulnerabilities If you discover a security vulnerability within BotMan, please send an e-mail to Marcel Pociot at m.pociot@gmail.com. All security vulnerabilities will be promptly addressed.