├── .editorconfig ├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── composer.json ├── docs ├── cookbook.md ├── first-steps.md ├── i-authToken.jpg ├── i-create_button.png ├── i-get-access.jpg └── i-public_account_button.png ├── examples ├── bot-plus.php ├── bot.php ├── config.php.dist └── setup.php ├── phpunit.xml.dist ├── src ├── Api │ ├── Entity.php │ ├── Event.php │ ├── Event │ │ ├── Conversation.php │ │ ├── Delivered.php │ │ ├── Factory.php │ │ ├── Failed.php │ │ ├── Message.php │ │ ├── Seen.php │ │ ├── Subscribed.php │ │ ├── Type.php │ │ ├── Unsubscribed.php │ │ └── Webhook.php │ ├── Exception │ │ └── ApiException.php │ ├── Keyboard.php │ ├── Keyboard │ │ └── Button.php │ ├── Message.php │ ├── Message │ │ ├── CarouselContent.php │ │ ├── Contact.php │ │ ├── Factory.php │ │ ├── File.php │ │ ├── Location.php │ │ ├── Picture.php │ │ ├── Sticker.php │ │ ├── Text.php │ │ ├── Type.php │ │ ├── Url.php │ │ └── Video.php │ ├── Response.php │ ├── Sender.php │ ├── Signature.php │ ├── User.php │ └── User │ │ └── State.php ├── Bot.php ├── Bot │ └── Manager.php └── Client.php └── test ├── Viber └── Tests │ ├── Api │ ├── EntityTest.php │ ├── Event │ │ ├── FactoryTest.php │ │ └── MessageTest.php │ ├── Keyboard │ │ └── ButtonTest.php │ ├── Message │ │ └── FactoryTest.php │ ├── MessageTest.php │ ├── ResponseTest.php │ ├── SenderTest.php │ └── SignatureTest.php │ ├── ApiMock.php │ ├── Bot │ └── ManagerTest.php │ ├── BotTest.php │ ├── ClientTest.php │ ├── Functions.php │ └── TestCase.php └── bootstrap.php /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Unix-style newlines with a newline ending for every file 7 | # Indent with 4 spaces 8 | [php] 9 | end_of_line = lf 10 | indent_style = space 11 | indent_size = 4 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor/ 2 | phpunit.xml 3 | composer.phar 4 | composer.lock 5 | .idea 6 | .DS_STORE 7 | /examples/config.php 8 | .php_cs.cache 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | sudo: false 4 | 5 | php: 6 | - 7.2 7 | - 7.3 8 | - 7.4 9 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | ## 0.0.12 4 | + improve code style 5 | + add onPicture method 6 | 7 | ## 0.0.5 8 | 9 | + signature inside QS 10 | 11 | ## 0.0.4 12 | 13 | + fix #13, fix #12, fix unit test 14 | 15 | ## 0.0.3 16 | 17 | + drop HHVM support 18 | + fix typos 19 | + sync with set_webhook api call 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) Novikov Bogdan 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PHP sdk for Viber api 2 | 3 | [![Build 4 | Status](https://secure.travis-ci.org/Bogdaan/viber-bot-php.png)](http://travis-ci.org/Bogdaan/viber-bot-php) 5 | 6 | Library to develop a bot for the Viber platform. [Create you first Viber bot step by step](docs/first-steps.md), see demo at `viber://pa?chatURI=viber-bot-php&context=github.com` 7 | 8 | ## Installation 9 | 10 | ``` 11 | composer require bogdaan/viber-bot-php 12 | ``` 13 | 14 | ## Example 15 | 16 | ```php 17 | '; 25 | 26 | // reply name 27 | $botSender = new Sender([ 28 | 'name' => 'Whois bot', 29 | 'avatar' => 'https://developers.viber.com/img/favicon.ico', 30 | ]); 31 | 32 | try { 33 | $bot = new Bot(['token' => $apiKey]); 34 | $bot 35 | ->onConversation(function ($event) use ($bot, $botSender) { 36 | // this event fires if user open chat, you can return "welcome message" 37 | // to user, but you can't send more messages! 38 | return (new \Viber\Api\Message\Text()) 39 | ->setSender($botSender) 40 | ->setText("Can i help you?"); 41 | }) 42 | ->onText('|whois .*|si', function ($event) use ($bot, $botSender) { 43 | // match by template, for example "whois Bogdaan" 44 | $bot->getClient()->sendMessage( 45 | (new \Viber\Api\Message\Text()) 46 | ->setSender($botSender) 47 | ->setReceiver($event->getSender()->getId()) 48 | ->setText("I do not know )") 49 | ); 50 | }) 51 | ->run(); 52 | } catch (Exception $e) { 53 | // todo - log exceptions 54 | } 55 | ``` 56 | 57 | See more in **examples** directory. 58 | 59 | ## Library structure 60 | 61 | ``` 62 | . 63 | ├── Api 64 | │   ├── Entity.php 65 | │   ├── Event # all remote events ("callbacks") 66 | │   │   ├── Conversation.php # fires when user open 1v1 chat 67 | │   │   ├── Delivered.php # fires when message delivered (for each device) 68 | │   │   ├── Factory.php # Event factory 69 | │   │   ├── Failed.php # fires when delivery failed (for each device) 70 | │   │   ├── Message.php # fires when user send message 71 | │   │   ├── Seen.php # fires when user read message (for each device) 72 | │   │   ├── Subscribed.php # fires when user subscribe to PA 73 | │   │   ├── Type.php # available types 74 | │   │   └── Unsubscribed.php # fires when user unsubscribed 75 | │   ├── Event.php # base class for all events 76 | │   ├── Exception # 77 | │   │   └── ApiException.php # remote or logic error 78 | │   ├── Keyboard # 79 | │   │   └── Button.php # all types of buttons here 80 | │   ├── Keyboard.php # button container 81 | │   ├── Message # 82 | │   │   ├── CarouselContent.php # 83 | │   │   ├── Contact.php # 84 | │   │   ├── Factory.php # 85 | │   │   ├── File.php # 86 | │   │   ├── Location.php # 87 | │   │   ├── Picture.php # 88 | │   │   ├── Sticker.php # 89 | │   │   ├── Text.php # 90 | │   │   ├── Type.php # available message types 91 | │   │   ├── Url.php # 92 | │   │   └── Video.php # 93 | │   ├── Message.php # base class for all messages 94 | │   ├── Response.php # wrap api response 95 | │   ├── Sender.php # represent bot-sender 96 | │   ├── Signature.php # signature helper (verify or create sign) 97 | │   ├── User # 98 | │   │   └── State.php # user state (online/offline etc) 99 | │   └── User.php # viber user 100 | ├── Bot # 101 | │   └── Manager.php # manage bot closures 102 | ├── Bot.php # bot class 103 | └── Client.php # api client 104 | ``` 105 | 106 | 107 | ## Read more 108 | 109 | - [Create you first Viber bot](docs/first-steps.md) 110 | - [Cookbook](docs/cookbook.md) 111 | - [REST api documentation](https://developers.viber.com/api/rest-bot-api/index.html) 112 | - [SDK for node](https://github.com/Viber/viber-bot-node) 113 | - [SDK for python](https://github.com/Viber/viber-bot-python) 114 | - [Project page](http://viber.hcbogdan.com/) 115 | 116 | ## Features 117 | 118 | - [x] all api entities 119 | - [x] validate request and response signs 120 | - [x] provide webhook interface 121 | - [x] provide event interface 122 | - [ ] wrap all api response to entities 123 | - [ ] validate api entities before submit? 124 | - [ ] implement log levels with monolog? 125 | - [ ] post on public page 126 | 127 | ## Contributing 128 | 129 | Pull requests are welcome. 130 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bogdaan/viber-bot-php", 3 | "description": "Php bot interface to work with Viber API", 4 | "keywords": ["viber", "im", "bot"], 5 | "type": "library", 6 | "license": "MIT", 7 | "authors": [ 8 | { 9 | "name": "Novikov Bogdan", 10 | "email": "hcbogdan@gmail.com" 11 | } 12 | ], 13 | "require": { 14 | "php": ">=5.5.0", 15 | "guzzlehttp/guzzle": "^6.2 | ^7.0" 16 | }, 17 | "autoload": { 18 | "psr-4": { 19 | "Viber\\": "src/" 20 | } 21 | }, 22 | "require-dev": { 23 | "phpunit/phpunit": "^4.8", 24 | "monolog/monolog": "^1.22" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /docs/cookbook.md: -------------------------------------------------------------------------------- 1 | # Cookbook 2 | 3 | ## How to secure you webhook url? 4 | 5 | You can: 6 | 7 | 1. create long file name like `6320c1115d5bc2b6ca615b96be050884.php` and 8 | register it as webhook url. 9 | 2. Check Viber backend IP address range (you can find it in dev docs). 10 | 11 | ## How to react on picture-message, url-message etc. ? 12 | 13 | You need to create own event checker. For example: 14 | 15 | ```php 16 | // ... 17 | // $bot - \Viber\Bot instance 18 | $bot->on(function ($event) { 19 | return isThisIsCatPicture(event); 20 | }, function ($event) { 21 | // process cat pictures here 22 | }); 23 | ``` 24 | 25 | ## How to process user conversation 26 | 27 | ## How to track message delivery? 28 | 29 | Inside viber ecosystem each device/client send delivery status. So, if you want to track delivery process you can: 30 | 31 | First, subscribe to delivery events: 32 | 33 | ```php 34 | use Viber\Api\Event\Type; 35 | 36 | // ... 37 | // register to all events 38 | $result = $client->setWebhook($webhookUrl, [ 39 | Type::DELIVERED, // if message delivered to device 40 | Type::SEEN, // if message is seen device 41 | Type::FAILED, // if message not delivered 42 | Type::SUBSCRIBED, 43 | Type::UNSUBSCRIBED, 44 | Type::CONVERSATION, 45 | Type::MESSAGE 46 | ]); 47 | ``` 48 | 49 | Then setup event handler callback inside bot-manager: 50 | 51 | ```php 52 | // ... 53 | $bot 54 | ->on(function (Event $event) { 55 | return ($event instanceof \Viber\Api\Event\DELIVERED); 56 | }, function($event) { 57 | // process delivered 58 | }) 59 | ->on(function (Event $event) { 60 | return ($event instanceof \Viber\Api\Event\SEEN); 61 | }, function($event) { 62 | // process seen 63 | }) 64 | ->on(function (Event $event) { 65 | return ($event instanceof \Viber\Api\Event\FAILED); 66 | }, function($event) { 67 | // process failed 68 | }); 69 | ``` 70 | 71 | ## How to request user phone? 72 | 73 | You need to setup minimal API version to 3: 74 | 75 | ```php 76 | $bot->getClient()->sendMessage( 77 | (new \Viber\Api\Message\Text()) 78 | ->setSender($botSender) 79 | ->setReceiver($event->getSender()->getId()) 80 | ->setMinApiVersion(3) 81 | ->setText("We need your phone number") 82 | ->setKeyboard( 83 | (new \Viber\Api\Keyboard()) 84 | ->setButtons([ 85 | (new \Viber\Api\Keyboard\Button()) 86 | ->setActionType('share-phone') 87 | ->setActionBody('reply') 88 | ->setText('Send phone number') 89 | ]) 90 | ) 91 | ); 92 | ``` 93 | 94 | ## Integration (api.ai, botan.io and others) 95 | -------------------------------------------------------------------------------- /docs/first-steps.md: -------------------------------------------------------------------------------- 1 | # How to create you first Viber bot 2 | 3 | 4 | ## Early access to public account 5 | 6 | If you already can create special account (you have early access) - skip this step. In order to get early access you need: 7 | 8 | 1. Visit [https://www.viber.com/en/public-accounts](https://www.viber.com/en/public-accounts) 9 | 10 | 2. Click **Apply for a Public Account** button 11 | 12 | 3. Fill form and send it 13 | 14 | Soon you will receive a message like this: 15 | ![Early access to PA](i-get-access.jpg) 16 | 17 | After this - restart Viber app, and go to next step. 18 | 19 | 20 | ## Create public account (page) 21 | 22 | Now you can create **Viber Public Account** (PA) from you Viber app: 23 | 24 | 1. Open Public Accounts through the Public Accounts icon ![button view](i-public_account_button.png) at the top right of your screen 25 | 26 | 2. Once you're on the Public Accounts home page, tap on the create button ![button view](i-create_button.png) at the bottom of the screen 27 | 28 | 3. Tap on Join now to start creating your Public Account 29 | 30 | 4. Fill you application info (name, category, background photo etc.) 31 | 32 | 5. On finish step you can copy api-token, or you can get token on "Edit details" page 33 | 34 | ![authToken](i-authToken.jpg) 35 | 36 | That's all. 37 | 38 | ## Install this package with composer 39 | 40 | You can download it from github or install with composer (recommended): 41 | ``` 42 | composer require bogdaan/viber-bot-php 43 | ``` 44 | 45 | ## Setup webhook 46 | 47 | First you need trusted CA certificate webhook url (you can get [letsencryptp](https://letsencrypt.org) cert for dev server). 48 | 49 | Let's create setup.php and subscrive for viber events: 50 | ``` 51 | '; // from "Edit Details" page 57 | $webhookUrl = ''; // for exmaple https://my.com/bot.php 58 | 59 | try { 60 | $client = new Client([ 'token' => $apiKey ]); 61 | $result = $client->setWebhook($webhookUrl); 62 | echo "Success!\n"; 63 | } catch (Exception $e) { 64 | echo "Error: ". $e->getError() ."\n"; 65 | } 66 | ``` 67 | 68 | ## Create bot 69 | 70 | We already subscribed for events. Now we can can accept messages, pictures and other events. Let's create simple bot.php: 71 | 72 | ``` 73 | '; 81 | 82 | $botSender = new Sender([ 83 | 'name' => 'Reply bot', 84 | 'avatar' => 'https://developers.viber.com/img/favicon.ico', 85 | ]); 86 | 87 | try { 88 | $bot = new Bot([ 'token' => $apiKey ]); 89 | $bot 90 | ->onText('|.*|s', function ($event) use ($bot) { 91 | // .* - match any symbols (see PCRE) 92 | $bot->getClient()->sendMessage( 93 | (new \Viber\Api\Message\Text()) 94 | ->setSender($botSender) 95 | ->setReceiver($event->getSender()->getId()) 96 | ->setText("Hi!") 97 | ); 98 | }) 99 | ->run(); 100 | } catch (Exception $e) { 101 | // todo - log errors 102 | } 103 | 104 | ``` 105 | 106 | You can see more in **examples** directory. 107 | 108 | ## Resources 109 | 110 | - [Official documentation](https://developers.viber.com/docs/api/rest-bot-api/) 111 | -------------------------------------------------------------------------------- /docs/i-authToken.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bogdaan/viber-bot-php/176dc938613d88e42ca04c7e7598f842f8f1cafc/docs/i-authToken.jpg -------------------------------------------------------------------------------- /docs/i-create_button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bogdaan/viber-bot-php/176dc938613d88e42ca04c7e7598f842f8f1cafc/docs/i-create_button.png -------------------------------------------------------------------------------- /docs/i-get-access.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bogdaan/viber-bot-php/176dc938613d88e42ca04c7e7598f842f8f1cafc/docs/i-get-access.jpg -------------------------------------------------------------------------------- /docs/i-public_account_button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bogdaan/viber-bot-php/176dc938613d88e42ca04c7e7598f842f8f1cafc/docs/i-public_account_button.png -------------------------------------------------------------------------------- /examples/bot-plus.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | 11 | require_once("../vendor/autoload.php"); 12 | 13 | use Viber\Bot; 14 | use Viber\Api\Sender; 15 | use Monolog\Logger; 16 | use Monolog\Handler\StreamHandler; 17 | 18 | $config = require('./config.php'); 19 | $apiKey = $config['apiKey']; 20 | 21 | // reply name 22 | $botSender = new Sender([ 23 | 'name' => 'Demo bot', 24 | 'avatar' => 'https://developers.viber.com/images/favicon.ico', 25 | ]); 26 | 27 | // log bot interaction 28 | $log = new Logger('bot'); 29 | $log->pushHandler(new StreamHandler('/tmp/bot.log')); 30 | 31 | try { 32 | // create bot instance 33 | $bot = new Bot(['token' => $apiKey]); 34 | $bot 35 | // first interaction with bot - return "welcome message" 36 | ->onConversation(function ($event) use ($bot, $botSender, $log) { 37 | $log->info('onConversation handler'); 38 | $buttons = []; 39 | for ($i = 0; $i <= 8; $i++) { 40 | $buttons[] = 41 | (new \Viber\Api\Keyboard\Button()) 42 | ->setColumns(1) 43 | ->setActionType('reply') 44 | ->setActionBody('k' . $i) 45 | ->setText('k' . $i); 46 | } 47 | return (new \Viber\Api\Message\Text()) 48 | ->setSender($botSender) 49 | ->setText("Hi, you can see some demo: send 'k1' or 'k2' etc.") 50 | ->setKeyboard( 51 | (new \Viber\Api\Keyboard()) 52 | ->setButtons($buttons) 53 | ); 54 | }) 55 | // when user subscribe to PA 56 | ->onSubscribe(function ($event) use ($bot, $botSender, $log) { 57 | $log->info('onSubscribe handler'); 58 | $this->getClient()->sendMessage( 59 | (new \Viber\Api\Message\Text()) 60 | ->setSender($botSender) 61 | ->setText('Thanks for subscription!') 62 | ); 63 | }) 64 | ->onText('|btn-click|s', function ($event) use ($bot, $botSender, $log) { 65 | $log->info('click on button'); 66 | $receiverId = $event->getSender()->getId(); 67 | $bot->getClient()->sendMessage( 68 | (new \Viber\Api\Message\Text()) 69 | ->setSender($botSender) 70 | ->setReceiver($receiverId) 71 | ->setText('you press the button') 72 | ); 73 | }) 74 | ->onText('|k\d+|is', function ($event) use ($bot, $botSender, $log) { 75 | $caseNumber = (int)preg_replace('|[^0-9]|s', '', $event->getMessage()->getText()); 76 | $log->info('onText demo handler #' . $caseNumber); 77 | $client = $bot->getClient(); 78 | $receiverId = $event->getSender()->getId(); 79 | switch ($caseNumber) { 80 | case 0: 81 | $client->sendMessage( 82 | (new \Viber\Api\Message\Text()) 83 | ->setSender($botSender) 84 | ->setReceiver($receiverId) 85 | ->setText('Basic keyboard layout') 86 | ->setKeyboard( 87 | (new \Viber\Api\Keyboard()) 88 | ->setButtons([ 89 | (new \Viber\Api\Keyboard\Button()) 90 | ->setActionType('reply') 91 | ->setActionBody('btn-click') 92 | ->setText('Tap this button') 93 | ]) 94 | ) 95 | ); 96 | break; 97 | // 98 | case 1: 99 | $client->sendMessage( 100 | (new \Viber\Api\Message\Text()) 101 | ->setSender($botSender) 102 | ->setReceiver($receiverId) 103 | ->setText('More buttons and styles') 104 | ->setKeyboard( 105 | (new \Viber\Api\Keyboard()) 106 | ->setButtons([ 107 | (new \Viber\Api\Keyboard\Button()) 108 | ->setBgColor('#8074d6') 109 | ->setTextSize('small') 110 | ->setTextHAlign('right') 111 | ->setActionType('reply') 112 | ->setActionBody('btn-click') 113 | ->setText('Button 1'), 114 | 115 | (new \Viber\Api\Keyboard\Button()) 116 | ->setBgColor('#2fa4e7') 117 | ->setTextHAlign('center') 118 | ->setActionType('reply') 119 | ->setActionBody('btn-click') 120 | ->setText('Button 2'), 121 | 122 | (new \Viber\Api\Keyboard\Button()) 123 | ->setBgColor('#555555') 124 | ->setTextSize('large') 125 | ->setTextHAlign('left') 126 | ->setActionType('reply') 127 | ->setActionBody('btn-click') 128 | ->setText('Button 3'), 129 | ]) 130 | ) 131 | ); 132 | break; 133 | // 134 | case 2: 135 | $client->sendMessage( 136 | (new \Viber\Api\Message\Contact()) 137 | ->setSender($botSender) 138 | ->setReceiver($receiverId) 139 | ->setName('Novikov Bogdan') 140 | ->setPhoneNumber('+380000000000') 141 | ); 142 | break; 143 | // 144 | case 3: 145 | $client->sendMessage( 146 | (new \Viber\Api\Message\Location()) 147 | ->setSender($botSender) 148 | ->setReceiver($receiverId) 149 | ->setLat(48.486504) 150 | ->setLng(35.038910) 151 | ); 152 | break; 153 | // 154 | case 4: 155 | $client->sendMessage( 156 | (new \Viber\Api\Message\Sticker()) 157 | ->setSender($botSender) 158 | ->setReceiver($receiverId) 159 | ->setStickerId(114408) 160 | ); 161 | break; 162 | // 163 | case 5: 164 | $client->sendMessage( 165 | (new \Viber\Api\Message\Url()) 166 | ->setSender($botSender) 167 | ->setReceiver($receiverId) 168 | ->setMedia('https://hcbogdan.com') 169 | ); 170 | break; 171 | // 172 | case 6: 173 | $client->sendMessage( 174 | (new \Viber\Api\Message\Picture()) 175 | ->setSender($botSender) 176 | ->setReceiver($receiverId) 177 | ->setText('some media data') 178 | ->setMedia('https://developers.viber.com/img/devlogo.png') 179 | ); 180 | break; 181 | // 182 | case 7: 183 | $client->sendMessage( 184 | (new \Viber\Api\Message\Video()) 185 | ->setSender($botSender) 186 | ->setReceiver($receiverId) 187 | ->setSize(2 * 1024 * 1024) 188 | ->setMedia('http://techslides.com/demos/sample-videos/small.mp4') 189 | ); 190 | break; 191 | // 192 | case 8: 193 | $client->sendMessage( 194 | (new \Viber\Api\Message\CarouselContent()) 195 | ->setSender($botSender) 196 | ->setReceiver($receiverId) 197 | ->setButtonsGroupColumns(6) 198 | ->setButtonsGroupRows(6) 199 | ->setBgColor('#FFFFFF') 200 | ->setButtons([ 201 | (new \Viber\Api\Keyboard\Button()) 202 | ->setColumns(6) 203 | ->setRows(3) 204 | ->setActionType('open-url') 205 | ->setActionBody('https://www.google.com') 206 | ->setImage('https://i.vimeocdn.com/portrait/58832_300x300'), 207 | 208 | (new \Viber\Api\Keyboard\Button()) 209 | ->setColumns(6) 210 | ->setRows(3) 211 | ->setActionType('reply') 212 | ->setActionBody('https://www.google.com') 213 | ->setText('Buy') 214 | ->setTextSize("large") 215 | ->setTextVAlign("middle") 216 | ->setTextHAlign("middle") 217 | ->setImage('https://s14.postimg.org/4mmt4rw1t/Button.png') 218 | ]) 219 | ); 220 | break; 221 | } 222 | }) 223 | ->run(); 224 | } catch (Exception $e) { 225 | $log->warning('Exception: ' . $e->getMessage()); 226 | if ($bot) { 227 | $log->warning('Actual sign: ' . $bot->getSignHeaderValue()); 228 | $log->warning('Actual body: ' . $bot->getInputBody()); 229 | } 230 | } 231 | -------------------------------------------------------------------------------- /examples/bot.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | 11 | require_once '../vendor/autoload.php'; 12 | 13 | use Viber\Bot; 14 | use Viber\Api\Sender; 15 | use Monolog\Logger; 16 | use Monolog\Handler\StreamHandler; 17 | 18 | $config = require('./config.php'); 19 | $apiKey = $config['apiKey']; 20 | 21 | // reply name 22 | $botSender = new Sender([ 23 | 'name' => 'Whois bot', 24 | 'avatar' => 'https://developers.viber.com/img/favicon.ico', 25 | ]); 26 | 27 | // log bot interaction 28 | $log = new Logger('bot'); 29 | $log->pushHandler(new StreamHandler('/tmp/bot.log')); 30 | 31 | try { 32 | // create bot instance 33 | $bot = new Bot(['token' => $apiKey]); 34 | $bot 35 | ->onConversation(function ($event) use ($bot, $botSender, $log) { 36 | $log->info('onConversation ' . var_export($event, true)); 37 | // this event fires if user open chat, you can return "welcome message" 38 | // to user, but you can't send more messages! 39 | return (new \Viber\Api\Message\Text()) 40 | ->setSender($botSender) 41 | ->setText('Can i help you?'); 42 | }) 43 | ->onText('|whois .*|si', function ($event) use ($bot, $botSender, $log) { 44 | $log->info('onText whois ' . var_export($event, true)); 45 | // match by template, for example "whois Bogdaan" 46 | $bot->getClient()->sendMessage( 47 | (new \Viber\Api\Message\Text()) 48 | ->setSender($botSender) 49 | ->setReceiver($event->getSender()->getId()) 50 | ->setText('I do not know )') 51 | ); 52 | }) 53 | ->onText('|.*|s', function ($event) use ($bot, $botSender, $log) { 54 | $log->info('onText ' . var_export($event, true)); 55 | // .* - match any symbols 56 | $bot->getClient()->sendMessage( 57 | (new \Viber\Api\Message\Text()) 58 | ->setSender($botSender) 59 | ->setReceiver($event->getSender()->getId()) 60 | ->setText('HI!') 61 | ); 62 | }) 63 | ->onPicture(function ($event) use ($bot, $botSender, $log) { 64 | $log->info('onPicture ' . var_export($event, true)); 65 | $bot->getClient()->sendMessage( 66 | (new \Viber\Api\Message\Text()) 67 | ->setSender($botSender) 68 | ->setReceiver($event->getSender()->getId()) 69 | ->setText('Nice picture ;-)') 70 | ); 71 | }) 72 | ->on(function ($event) { 73 | return true; // match all 74 | }, function ($event) use ($log) { 75 | $log->info('Other event: ' . var_export($event, true)); 76 | }) 77 | ->run(); 78 | } catch (Exception $e) { 79 | $log->warning('Exception: ', $e->getMessage()); 80 | if ($bot) { 81 | $log->warning('Actual sign: ' . $bot->getSignHeaderValue()); 82 | $log->warning('Actual body: ' . $bot->getInputBody()); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /examples/config.php.dist: -------------------------------------------------------------------------------- 1 | '', 8 | 'webhookUrl' => '', 9 | ]; 10 | -------------------------------------------------------------------------------- /examples/setup.php: -------------------------------------------------------------------------------- 1 | 8 | */ 9 | 10 | require_once '../vendor/autoload.php'; 11 | 12 | use Viber\Client; 13 | 14 | $config = require('./config.php'); 15 | 16 | $apiKey = $config['apiKey']; // from PA "Edit Details" page 17 | $webhookUrl = $config['webhookUrl']; // for exmaple https://my.com/bot.php 18 | 19 | try { 20 | $client = new Client(['token' => $apiKey]); 21 | $result = $client->setWebhook($webhookUrl); 22 | echo "Success!\n"; // print_r($result); 23 | } catch (Exception $e) { 24 | echo 'Error: ' . $e->getMessage() . "\n"; 25 | } 26 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | ./test/ 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /src/Api/Entity.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class Entity 13 | { 14 | /** 15 | * Map api-response keys to class setters 16 | * 17 | * @var array 18 | */ 19 | protected $propertiesMap = []; 20 | 21 | /** 22 | * Make new instance from api response array 23 | * 24 | * @param mixed $properties list of properties 25 | * @throws \Viber\Api\Exception\ApiException 26 | */ 27 | public function __construct($properties = null) 28 | { 29 | if (null === $properties) { 30 | return; 31 | } 32 | if (!is_array($properties) && !$properties instanceof \ArrayAccess) { 33 | throw new ApiException('Properties must be an array or implement ArrayAccess'); 34 | } 35 | if (empty($this->propertiesMap)) { // no property map 36 | foreach ($properties as $propName => $propValue) { 37 | if (property_exists(get_class($this), $propName)) { 38 | $this->$propName = $propValue; 39 | } 40 | } 41 | } else { // call setters 42 | foreach ($properties as $propName => $propValue) { 43 | if (isset($this->propertiesMap[$propName])) { 44 | $setterName = $this->propertiesMap[$propName]; 45 | $this->$setterName($propValue); 46 | } 47 | } 48 | } 49 | } 50 | 51 | /** 52 | * Build array single-level array 53 | * 54 | * @return array 55 | */ 56 | public function toArray() 57 | { 58 | return []; 59 | } 60 | 61 | /** 62 | * Build multi-level array for api call`s, filter or upgrade properties 63 | * 64 | * @return array 65 | */ 66 | public function toApiArray() 67 | { 68 | $entity = $this->toArray(); 69 | foreach ($entity as $name => &$value) { 70 | if (null === $value) { 71 | unset($entity[$name]); 72 | } elseif ($value instanceof Entity) { 73 | $value = $value->toArray(); 74 | } 75 | } 76 | return $entity; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/Api/Event.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class Event 13 | { 14 | /** 15 | * Event type 16 | * 17 | * @var string 18 | */ 19 | protected $event; 20 | 21 | /** 22 | * Time of the event that triggered the callback 23 | * 24 | * @var integer 25 | */ 26 | protected $timestamp; 27 | 28 | /** 29 | * Unique ID of the message 30 | * 31 | * @var string 32 | */ 33 | protected $message_token; 34 | 35 | /** 36 | * Init event from api array 37 | * 38 | * @param array $properties 39 | * @throws \Viber\Api\Exception\ApiException 40 | */ 41 | public function __construct(array $properties) 42 | { 43 | foreach ($properties as $propName => $propValue) { 44 | if (property_exists(get_class($this), $propName)) { 45 | if ('sender' === $propName) { 46 | $this->sender = new Sender($propValue); 47 | } elseif ('message' === $propName) { 48 | $this->message = MessageFactory::makeFromApi($propValue); 49 | } elseif ('user' === $propName) { 50 | $this->user = new User($propValue); 51 | } else { 52 | $this->$propName = $propValue; 53 | } 54 | } 55 | } 56 | } 57 | 58 | /** 59 | * Get the value of Event type 60 | * 61 | * @return string 62 | */ 63 | public function getEvent() 64 | { 65 | return $this->event; 66 | } 67 | 68 | /** 69 | * Alias for getEvent 70 | * 71 | * @return string 72 | */ 73 | public function getEventType() 74 | { 75 | return $this->getEvent(); 76 | } 77 | 78 | /** 79 | * Get the value of Time of the event that triggered the callback 80 | * 81 | * @return integer 82 | */ 83 | public function getTimestamp() 84 | { 85 | return $this->timestamp; 86 | } 87 | 88 | /** 89 | * Get the value of Unique ID of the message 90 | * 91 | * @return string 92 | */ 93 | public function getMessageToken() 94 | { 95 | return $this->message_token; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/Api/Event/Conversation.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class Conversation extends Event 16 | { 17 | /** 18 | * Context information 19 | * 20 | * @var string 21 | */ 22 | protected $context; 23 | 24 | /** 25 | * Viber user 26 | * 27 | * @var \Viber\Api\User 28 | */ 29 | protected $user; 30 | 31 | /** 32 | * Conversation action 33 | * 34 | * @var string 35 | */ 36 | protected $type; 37 | 38 | /** 39 | * Get the value of Context information 40 | * 41 | * @return string 42 | */ 43 | public function getContext() 44 | { 45 | return $this->context; 46 | } 47 | 48 | /** 49 | * Get the value of Viber user 50 | * 51 | * @return \Viber\Api\User 52 | */ 53 | public function getUser() 54 | { 55 | return $this->user; 56 | } 57 | 58 | /** 59 | * Get conversation type 60 | * 61 | * @return string 62 | */ 63 | public function getType() 64 | { 65 | return $this->type; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/Api/Event/Delivered.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class Delivered extends Event 13 | { 14 | /** 15 | * Viber user id 16 | * 17 | * @var string 18 | */ 19 | protected $user_id; 20 | 21 | /** 22 | * Get the value of Viber user id 23 | * 24 | * @return string 25 | */ 26 | public function getUserId() 27 | { 28 | return $this->user_id; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Api/Event/Factory.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class Factory 13 | { 14 | /** 15 | * Make some event from api-request array 16 | * 17 | * @param array $data api request data 18 | * @return \Viber\Api\Event 19 | */ 20 | public static function makeFromApi(array $data) 21 | { 22 | if (isset($data['event'])) { 23 | switch ($data['event']) { 24 | case Type::MESSAGE: 25 | return new Message($data); 26 | case Type::SUBSCRIBED: 27 | return new Subscribed($data); 28 | case Type::CONVERSATION: 29 | return new Conversation($data); 30 | case Type::UNSUBSCRIBED: 31 | return new Unsubscribed($data); 32 | case Type::DELIVERED: 33 | return new Delivered($data); 34 | case Type::SEEN: 35 | return new Seen($data); 36 | case Type::FAILED: 37 | return new Failed($data); 38 | case Type::WEBHOOK: 39 | return new Webhook($data); 40 | } 41 | } 42 | throw new ApiException('Unknow event data'); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Api/Event/Failed.php: -------------------------------------------------------------------------------- 1 | 12 | */ 13 | class Failed extends Event 14 | { 15 | /** 16 | * Viber user id 17 | * 18 | * @var string 19 | */ 20 | protected $user_id; 21 | 22 | /** 23 | * A string describing the failure 24 | * 25 | * @var string 26 | */ 27 | protected $dsc; 28 | 29 | /** 30 | * Get the value of Viber user id 31 | * 32 | * @return string 33 | */ 34 | public function getUserId() 35 | { 36 | return $this->user_id; 37 | } 38 | 39 | /** 40 | * Get the value of A string describing the failure 41 | * 42 | * @return string 43 | */ 44 | public function getDsc() 45 | { 46 | return $this->dsc; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Api/Event/Message.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class Message extends Event 13 | { 14 | /** 15 | * Who send message 16 | * 17 | * @var \Viber\Api\Sender 18 | */ 19 | protected $sender; 20 | 21 | /** 22 | * Message data 23 | * 24 | * @var \Viber\Api\Message 25 | */ 26 | protected $message; 27 | 28 | /** 29 | * Get the value of Who send message 30 | * 31 | * @return \Viber\Api\Sender 32 | */ 33 | public function getSender() 34 | { 35 | return $this->sender; 36 | } 37 | 38 | /** 39 | * Get the value of Message data 40 | * 41 | * @return \Viber\Api\Message|\Viber\Api\Message\Text 42 | */ 43 | public function getMessage() 44 | { 45 | return $this->message; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Api/Event/Seen.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class Seen extends Event 13 | { 14 | /** 15 | * Viber user id 16 | * @var string 17 | */ 18 | protected $user_id; 19 | 20 | /** 21 | * Get the value of Viber user id 22 | * 23 | * @return string 24 | */ 25 | public function getUserId() 26 | { 27 | return $this->user_id; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Api/Event/Subscribed.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class Subscribed extends Event 13 | { 14 | /** 15 | * Viber user 16 | * @var \Viber\Api\User 17 | */ 18 | protected $user; 19 | 20 | /** 21 | * Get the value of Viber user 22 | * 23 | * @return \Viber\Api\User 24 | */ 25 | public function getUser() 26 | { 27 | return $this->user; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Api/Event/Type.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | interface Type 11 | { 12 | const DELIVERED = 'delivered'; 13 | const SEEN = 'seen'; 14 | const FAILED = 'failed'; 15 | const SUBSCRIBED = 'subscribed'; 16 | const UNSUBSCRIBED = 'unsubscribed'; 17 | const CONVERSATION = 'conversation_started'; 18 | const MESSAGE = 'message'; 19 | const WEBHOOK = 'webhook'; 20 | } 21 | -------------------------------------------------------------------------------- /src/Api/Event/Unsubscribed.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class Unsubscribed extends Event 13 | { 14 | /** 15 | * Viber user id 16 | * 17 | * @var string 18 | */ 19 | protected $user_id; 20 | 21 | /** 22 | * Get the value of Viber user id 23 | * 24 | * @return string 25 | */ 26 | public function getUserId() 27 | { 28 | return $this->user_id; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Api/Event/Webhook.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class Webhook extends Event 13 | { 14 | } 15 | -------------------------------------------------------------------------------- /src/Api/Exception/ApiException.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | class ApiException extends \RuntimeException 11 | { 12 | } 13 | -------------------------------------------------------------------------------- /src/Api/Keyboard.php: -------------------------------------------------------------------------------- 1 | 16 | */ 17 | class Keyboard extends Entity 18 | { 19 | /** 20 | * Array containing all keyboard buttons by order 21 | * 22 | * @var array 23 | */ 24 | protected $Buttons; 25 | 26 | /** 27 | * Background color of the keyboard (HEX) 28 | * 29 | * @var string 30 | */ 31 | protected $BgColor; 32 | 33 | /** 34 | * When true - the keyboard will always be displayed with the same height 35 | * as the native keyboard.When false - short keyboards will be displayed 36 | * with the minimal possible height. Maximal height will be native 37 | * keyboard height 38 | * 39 | * @var boolean 40 | */ 41 | protected $DefaultHeight; 42 | 43 | /** 44 | * optional (api level 4). Customize the keyboard input field. 45 | * regular-display regular size input field. 46 | * minimized - display input field minimized by default. 47 | * hidden - hide the input field 48 | * 49 | * @var string 50 | */ 51 | protected $InputFieldState; 52 | 53 | /** 54 | * {@inheritDoc} 55 | */ 56 | public function toArray() 57 | { 58 | return [ 59 | 'Type' => 'keyboard', 60 | 'Buttons' => $this->getButtonsApiArray(), 61 | 'BgColor' => $this->getBgColor(), 62 | 'DefaultHeight' => $this->getDefaultHeight(), 63 | ]; 64 | } 65 | 66 | /** 67 | * Build buttons api array 68 | * 69 | * @return array 70 | */ 71 | protected function getButtonsApiArray() 72 | { 73 | $buttons = []; 74 | foreach ($this->getButtons() as $i) { 75 | $buttons[] = $i->toApiArray(); 76 | } 77 | return $buttons; 78 | } 79 | 80 | /** 81 | * Get the value of Array containing all keyboard buttons by order 82 | * 83 | * @return array 84 | */ 85 | public function getButtons() 86 | { 87 | return $this->Buttons; 88 | } 89 | 90 | /** 91 | * Set the value of Array containing all keyboard buttons by order 92 | * 93 | * @param array Buttons 94 | * 95 | * @return self 96 | */ 97 | public function setButtons(array $Buttons) 98 | { 99 | $this->Buttons = $Buttons; 100 | 101 | return $this; 102 | } 103 | 104 | /** 105 | * Get the value of Background color of the keyboard (HEX) 106 | * 107 | * @return string 108 | */ 109 | public function getBgColor() 110 | { 111 | return $this->BgColor; 112 | } 113 | 114 | /** 115 | * Set the value of Background color of the keyboard (HEX) 116 | * 117 | * @param string BgColor 118 | * 119 | * @return self 120 | */ 121 | public function setBgColor($BgColor) 122 | { 123 | $this->BgColor = $BgColor; 124 | 125 | return $this; 126 | } 127 | 128 | /** 129 | * Get the value of When true - the keyboard will always be displayed with the same height 130 | * 131 | * @return boolean 132 | */ 133 | public function getDefaultHeight() 134 | { 135 | return $this->DefaultHeight; 136 | } 137 | 138 | /** 139 | * Set the value of When true - the keyboard will always be displayed with the same height 140 | * 141 | * @param boolean DefaultHeight 142 | * 143 | * @return self 144 | */ 145 | public function setDefaultHeight($DefaultHeight) 146 | { 147 | $this->DefaultHeight = $DefaultHeight; 148 | 149 | return $this; 150 | } 151 | 152 | /** 153 | * @return string 154 | */ 155 | public function getInputFieldState() 156 | { 157 | return $this->InputFieldState; 158 | } 159 | 160 | /** 161 | * @param boolean DefaultHeight 162 | * 163 | * @return self 164 | */ 165 | public function setInputFieldState($fieldState) 166 | { 167 | $this->InputFieldState = $fieldState; 168 | 169 | return $this; 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /src/Api/Keyboard/Button.php: -------------------------------------------------------------------------------- 1 | 17 | */ 18 | class Button extends Entity 19 | { 20 | /** 21 | * Button width in columns (1-6) 22 | * 23 | * @var integer 24 | */ 25 | protected $Columns = 6; 26 | 27 | /** 28 | * Button height in rows (1-2) 29 | * 30 | * @var integer 31 | */ 32 | protected $Rows = 1; 33 | 34 | /** 35 | * Background color of button 36 | * 37 | * @var string 38 | */ 39 | protected $BgColor; 40 | 41 | /** 42 | * Type of the background media ("picture" or "gif") 43 | * For picture - JPEG and PNG files are supported. 44 | * Max size: 500 kb 45 | * 46 | * @var string 47 | */ 48 | protected $BgMediaType; 49 | 50 | /** 51 | * URL for background media content. 52 | * Will be placed with aspect to fill logic. 53 | * 54 | * @var string 55 | */ 56 | protected $BgMedia; 57 | 58 | /** 59 | * When true - animated background media (gif) will loop continuously. 60 | * When false - animated background media will play once and stop. 61 | * 62 | * @var boolean 63 | */ 64 | protected $BgLoop; 65 | 66 | /** 67 | * Type of action pressing the button will perform. 68 | * "reply" - will send a reply to the PA. 69 | * "open-url" - will open the specified URL and send the URL as reply to the PA. 70 | * 71 | * @see https://developers.viber.com/tools/keyboards/index.html#replyLogic 72 | * 73 | * @var string 74 | */ 75 | protected $ActionType; 76 | 77 | /** 78 | * Text for reply ActionType OR URL for "open-url". 79 | * For ActionType reply - text 80 | * For ActionType open-url - Valid URL. 81 | * Max length: Android - 250 characters; iOS - 100 characters 82 | * 83 | * @var string 84 | */ 85 | protected $ActionBody; 86 | 87 | /** 88 | * URL of image to place on top of background (if any). Can be a partially 89 | * transparent image that will allow showing some of the background. 90 | * Will be placed with aspect to fill logic. 91 | * 92 | * Valid URL. JPEG and PNG files are supported. Max size: 500 kb 93 | * 94 | * @var string 95 | */ 96 | protected $Image; 97 | 98 | /** 99 | * Text to be displayed on the button. Can contain some HTML tags. 100 | * 101 | * Free text. Valid and allowed HTML tags Max 250 characters. If the text 102 | * is too long to display on the button it will be cropped and ended 103 | * with "..." 104 | * 105 | * @var string 106 | */ 107 | protected $Text; 108 | 109 | /** 110 | * Vertical alignment of the text 111 | * 112 | * Avail: top, middle, bottom 113 | * 114 | * @var string 115 | */ 116 | protected $TextVAlign; 117 | 118 | /** 119 | * Horizontal align of the text 120 | * 121 | * Avail: left, center, right 122 | * 123 | * @var string 124 | */ 125 | protected $TextHAlign; 126 | 127 | /** 128 | * Text opacity. Range: 0-100 129 | * 130 | * @var integer 131 | */ 132 | protected $TextOpacity; 133 | 134 | /** 135 | * Text size out of 3 available options: small, regular, large 136 | * 137 | * @var string 138 | */ 139 | protected $TextSize; 140 | 141 | /** 142 | * Option for enable/disable display text on open-url action 143 | * 144 | * @var bool 145 | */ 146 | protected $Silent; 147 | 148 | /** 149 | * {@inheritDoc} 150 | */ 151 | public function toArray() 152 | { 153 | return [ 154 | 'Columns' => $this->getColumns(), 155 | 'Rows' => $this->getRows(), 156 | 'BgColor' => $this->getBgColor(), 157 | 'BgMediaType' => $this->getBgMediaType(), 158 | 'BgMedia' => $this->getBgMedia(), 159 | 'BgLoop' => $this->getBgLoop(), 160 | 'ActionType' => $this->getActionType(), 161 | 'ActionBody' => $this->getActionBody(), 162 | 'Image' => $this->getImage(), 163 | 'Text' => $this->getText(), 164 | 'TextVAlign' => $this->getTextVAlign(), 165 | 'TextHAlign' => $this->getTextHAlign(), 166 | 'TextOpacity' => $this->getTextOpacity(), 167 | 'TextSize' => $this->getTextSize(), 168 | 'Silent' => $this->isSilent(), 169 | ]; 170 | } 171 | 172 | /** 173 | * Get the value of Button width in columns (1-6) 174 | * 175 | * @return integer 176 | */ 177 | public function getColumns() 178 | { 179 | return $this->Columns; 180 | } 181 | 182 | /** 183 | * Set the value of Button width in columns (1-6) 184 | * 185 | * @param integer Columns 186 | * 187 | * @return self 188 | */ 189 | public function setColumns($Columns) 190 | { 191 | $this->Columns = $Columns; 192 | 193 | return $this; 194 | } 195 | 196 | /** 197 | * Get the value of Button height in rows (1-2) 198 | * 199 | * @return integer 200 | */ 201 | public function getRows() 202 | { 203 | return $this->Rows; 204 | } 205 | 206 | /** 207 | * Set the value of Button height in rows (1-2) 208 | * 209 | * @param integer Rows 210 | * 211 | * @return self 212 | */ 213 | public function setRows($Rows) 214 | { 215 | $this->Rows = $Rows; 216 | 217 | return $this; 218 | } 219 | 220 | /** 221 | * Get the value of Background color of button 222 | * 223 | * @return string 224 | */ 225 | public function getBgColor() 226 | { 227 | return $this->BgColor; 228 | } 229 | 230 | /** 231 | * Set the value of Background color of button 232 | * 233 | * @param string BgColor 234 | * 235 | * @return self 236 | */ 237 | public function setBgColor($BgColor) 238 | { 239 | $this->BgColor = $BgColor; 240 | 241 | return $this; 242 | } 243 | 244 | /** 245 | * Get the value of Type of the background media ("picture" or "gif") 246 | * 247 | * @return string 248 | */ 249 | public function getBgMediaType() 250 | { 251 | return $this->BgMediaType; 252 | } 253 | 254 | /** 255 | * Set the value of Type of the background media ("picture" or "gif") 256 | * 257 | * @param string BgMediaType 258 | * 259 | * @return self 260 | */ 261 | public function setBgMediaType($BgMediaType) 262 | { 263 | $this->BgMediaType = $BgMediaType; 264 | 265 | return $this; 266 | } 267 | 268 | /** 269 | * Get the value of URL for background media content. 270 | * 271 | * @return string 272 | */ 273 | public function getBgMedia() 274 | { 275 | return $this->BgMedia; 276 | } 277 | 278 | /** 279 | * Set the value of URL for background media content. 280 | * 281 | * @param string BgMedia 282 | * 283 | * @return self 284 | */ 285 | public function setBgMedia($BgMedia) 286 | { 287 | $this->BgMedia = $BgMedia; 288 | 289 | return $this; 290 | } 291 | 292 | /** 293 | * Get the value of When true - animated background media (gif) will loop continuously. 294 | * 295 | * @return boolean 296 | */ 297 | public function getBgLoop() 298 | { 299 | return $this->BgLoop; 300 | } 301 | 302 | /** 303 | * Set the value of When true - animated background media (gif) will loop continuously. 304 | * 305 | * @param boolean BgLoop 306 | * 307 | * @return self 308 | */ 309 | public function setBgLoop($BgLoop) 310 | { 311 | $this->BgLoop = $BgLoop; 312 | 313 | return $this; 314 | } 315 | 316 | /** 317 | * Get the value of Type of action pressing the button will perform. 318 | * 319 | * @return string 320 | */ 321 | public function getActionType() 322 | { 323 | return $this->ActionType; 324 | } 325 | 326 | /** 327 | * Set the value of Type of action pressing the button will perform. 328 | * 329 | * @param string ActionType 330 | * 331 | * @return self 332 | */ 333 | public function setActionType($ActionType) 334 | { 335 | $this->ActionType = $ActionType; 336 | 337 | return $this; 338 | } 339 | 340 | /** 341 | * Get the value of Text for reply ActionType OR URL for "open-url". 342 | * 343 | * @return string 344 | */ 345 | public function getActionBody() 346 | { 347 | return $this->ActionBody; 348 | } 349 | 350 | /** 351 | * Set the value of Text for reply ActionType OR URL for "open-url". 352 | * 353 | * @param string ActionBody 354 | * 355 | * @return self 356 | */ 357 | public function setActionBody($ActionBody) 358 | { 359 | $this->ActionBody = $ActionBody; 360 | 361 | return $this; 362 | } 363 | 364 | /** 365 | * Get the value of URL of image to place on top of background (if any). Can be a partially 366 | * 367 | * @return string 368 | */ 369 | public function getImage() 370 | { 371 | return $this->Image; 372 | } 373 | 374 | /** 375 | * Set the value of URL of image to place on top of background (if any). Can be a partially 376 | * 377 | * @param string Image 378 | * 379 | * @return self 380 | */ 381 | public function setImage($Image) 382 | { 383 | $this->Image = $Image; 384 | 385 | return $this; 386 | } 387 | 388 | /** 389 | * Get the value of Text to be displayed on the button. Can contain some HTML tags. 390 | * 391 | * @return string 392 | */ 393 | public function getText() 394 | { 395 | return $this->Text; 396 | } 397 | 398 | /** 399 | * Set the value of Text to be displayed on the button. Can contain some HTML tags. 400 | * 401 | * @param string Text 402 | * 403 | * @return self 404 | */ 405 | public function setText($Text) 406 | { 407 | $this->Text = $Text; 408 | 409 | return $this; 410 | } 411 | 412 | /** 413 | * Get the value of Vertical alignment of the text 414 | * 415 | * @return string 416 | */ 417 | public function getTextVAlign() 418 | { 419 | return $this->TextVAlign; 420 | } 421 | 422 | /** 423 | * Set the value of Vertical alignment of the text 424 | * 425 | * @param string TextVAlign 426 | * 427 | * @return self 428 | */ 429 | public function setTextVAlign($TextVAlign) 430 | { 431 | $this->TextVAlign = $TextVAlign; 432 | 433 | return $this; 434 | } 435 | 436 | /** 437 | * Get the value of Horizontal align of the text 438 | * 439 | * @return string 440 | */ 441 | public function getTextHAlign() 442 | { 443 | return $this->TextHAlign; 444 | } 445 | 446 | /** 447 | * Set the value of Horizontal align of the text 448 | * 449 | * @param string TextHAlign 450 | * 451 | * @return self 452 | */ 453 | public function setTextHAlign($TextHAlign) 454 | { 455 | $this->TextHAlign = $TextHAlign; 456 | 457 | return $this; 458 | } 459 | 460 | /** 461 | * Get the value of Text opacity. Range: 0-100 462 | * 463 | * @return integer 464 | */ 465 | public function getTextOpacity() 466 | { 467 | return $this->TextOpacity; 468 | } 469 | 470 | /** 471 | * Set the value of Text opacity. Range: 0-100 472 | * 473 | * @param integer TextOpacity 474 | * 475 | * @return self 476 | */ 477 | public function setTextOpacity($TextOpacity) 478 | { 479 | $this->TextOpacity = $TextOpacity; 480 | 481 | return $this; 482 | } 483 | 484 | /** 485 | * Get the value of Text size out of 3 available options: small, regular, large 486 | * 487 | * @return string 488 | */ 489 | public function getTextSize() 490 | { 491 | return $this->TextSize; 492 | } 493 | 494 | /** 495 | * Set the value of Text size out of 3 available options: small, regular, large 496 | * 497 | * @param string TextSize 498 | * 499 | * @return self 500 | */ 501 | public function setTextSize($TextSize) 502 | { 503 | $this->TextSize = $TextSize; 504 | 505 | return $this; 506 | } 507 | 508 | /** 509 | * @return bool 510 | */ 511 | public function isSilent() 512 | { 513 | return (bool) $this->Silent; 514 | } 515 | 516 | /** 517 | * @param bool $Silent 518 | * 519 | * @return self 520 | */ 521 | public function setSilent($Silent) 522 | { 523 | $this->Silent = $Silent; 524 | 525 | return $this; 526 | } 527 | } 528 | -------------------------------------------------------------------------------- /src/Api/Message.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | class Message extends Entity 11 | { 12 | /** 13 | * Viber user id array 14 | * 15 | * @var array 16 | */ 17 | protected $broadcast_list; 18 | 19 | /** 20 | * Viber user id 21 | * 22 | * @var integer 23 | */ 24 | protected $receiver; 25 | 26 | /** 27 | * Message type 28 | * 29 | * @var string 30 | */ 31 | protected $type; 32 | 33 | /** 34 | * Sender information 35 | * 36 | * @var \Viber\Api\Sender 37 | */ 38 | protected $sender; 39 | 40 | /** 41 | * Track messages and user’s replies. Passed back with user’s reply 42 | * 43 | * @var string 44 | */ 45 | protected $tracking_data; 46 | 47 | /** 48 | * API version required by clients 49 | * 50 | * @var integer 51 | */ 52 | protected $min_api_version = 1; 53 | 54 | /** 55 | * Custom keyboard for message 56 | * 57 | * @var \Viber\Api\Keyboard 58 | */ 59 | protected $keyboard; 60 | 61 | /** 62 | * {@inheritDoc} 63 | */ 64 | public function toArray() 65 | { 66 | return [ 67 | 'receiver' => $this->getReceiver(), 68 | 'type' => $this->getType(), 69 | 'sender' => $this->getSender(), 70 | 'tracking_data' => $this->getTrackingData(), 71 | 'min_api_version' => $this->getMinApiVersion(), 72 | 'keyboard' => $this->getKeyboard(), 73 | 'broadcast_list' => $this->getBroadcastList() 74 | ]; 75 | } 76 | 77 | /** 78 | * Get the value of Viber user's IDs 79 | * 80 | * @return array 81 | */ 82 | public function getBroadcastList() 83 | { 84 | return $this->broadcast_list; 85 | } 86 | 87 | /** 88 | * Set the value of Viber user's IDs 89 | * 90 | * @param array IDs 91 | * 92 | * @return static 93 | */ 94 | public function setBroadcastList($broadcast_list) 95 | { 96 | $this->broadcast_list = $broadcast_list; 97 | return $this; 98 | } 99 | 100 | 101 | /** 102 | * Get the value of Viber user 103 | * 104 | * @return string 105 | */ 106 | public function getReceiver() 107 | { 108 | return $this->receiver; 109 | } 110 | 111 | /** 112 | * Set the value of Viber user 113 | * 114 | * @param string receiver 115 | * 116 | * @return static 117 | */ 118 | public function setReceiver($receiver) 119 | { 120 | $this->receiver = $receiver; 121 | 122 | return $this; 123 | } 124 | 125 | /** 126 | * Get the value of Message type 127 | * 128 | * @return string 129 | */ 130 | public function getType() 131 | { 132 | return $this->type; 133 | } 134 | 135 | /** 136 | * Get the value of Sender information 137 | * 138 | * @return \Viber\Api\Sender 139 | */ 140 | public function getSender() 141 | { 142 | return $this->sender; 143 | } 144 | 145 | /** 146 | * Set the value of Sender information 147 | * 148 | * @param \Viber\Api\Sender sender 149 | * 150 | * @return static 151 | */ 152 | public function setSender(\Viber\Api\Sender $sender) 153 | { 154 | $this->sender = $sender; 155 | 156 | return $this; 157 | } 158 | 159 | /** 160 | * Get the value of Track messages and user’s replies. Passed back with user’s reply 161 | * 162 | * @return string 163 | */ 164 | public function getTrackingData() 165 | { 166 | return $this->tracking_data; 167 | } 168 | 169 | /** 170 | * Set the value of Track messages and user’s replies. Passed back with user’s reply 171 | * 172 | * @param string tracking_data 173 | * 174 | * @return static 175 | */ 176 | public function setTrackingData($tracking_data) 177 | { 178 | $this->tracking_data = $tracking_data; 179 | 180 | return $this; 181 | } 182 | 183 | /** 184 | * Get the value of API version required by clients 185 | * 186 | * @return integer 187 | */ 188 | public function getMinApiVersion() 189 | { 190 | return $this->min_api_version; 191 | } 192 | 193 | /** 194 | * Set the value of API version required by clients 195 | * 196 | * @param integer min_api_version 197 | * 198 | * @return static 199 | */ 200 | public function setMinApiVersion($min_api_version) 201 | { 202 | $this->min_api_version = $min_api_version; 203 | 204 | return $this; 205 | } 206 | 207 | /** 208 | * Get the value of Custom keyboard for message 209 | * 210 | * @return \Viber\Api\Keyboard 211 | */ 212 | public function getKeyboard() 213 | { 214 | return $this->keyboard; 215 | } 216 | 217 | /** 218 | * Set the value of Custom keyboard for message 219 | * 220 | * @param \Viber\Api\Keyboard keyboard 221 | * 222 | * @return static 223 | */ 224 | public function setKeyboard(\Viber\Api\Keyboard $keyboard) 225 | { 226 | $this->keyboard = $keyboard; 227 | 228 | return $this; 229 | } 230 | } 231 | -------------------------------------------------------------------------------- /src/Api/Message/CarouselContent.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class CarouselContent extends Message 13 | { 14 | /** 15 | * API version required by clients 16 | * 17 | * @var integer 18 | */ 19 | protected $min_api_version = 2; 20 | 21 | /** 22 | * Number of columns per carousel content block. Default 6 columns 23 | * 24 | * @var integer 25 | */ 26 | protected $ButtonsGroupColumns; 27 | 28 | /** 29 | * Number of rows per carousel content block. Default 7 columns 30 | * 31 | * @var integer 32 | */ 33 | protected $ButtonsGroupRows; 34 | 35 | /** 36 | * Background color of carousel content message 37 | * 38 | * @var string 39 | */ 40 | protected $BgColor; 41 | 42 | /** 43 | * Array of buttons 44 | * 45 | * @var array 46 | */ 47 | protected $Buttons; 48 | 49 | /** 50 | * {@inheritdoc} 51 | */ 52 | public function getType() 53 | { 54 | return Type::RICH_MEDIA; 55 | } 56 | 57 | /** 58 | * {@inheritdoc} 59 | */ 60 | public function toArray() 61 | { 62 | return array_merge(parent::toArray(), [ 63 | 'rich_media' => [ 64 | 'Type' => $this->getType(), 65 | 'ButtonsGroupColumns' => $this->getButtonsGroupColumns(), 66 | 'ButtonsGroupRows' => $this->getButtonsGroupRows(), 67 | 'BgColor' => $this->getBgColor(), 68 | 'Buttons' => $this->getButtonsApiArray(), 69 | ] 70 | ]); 71 | } 72 | 73 | /** 74 | * Get the value of number of columns per carousel content block. 75 | * 76 | * @return string 77 | */ 78 | public function getButtonsGroupColumns() 79 | { 80 | return $this->ButtonsGroupColumns; 81 | } 82 | 83 | /** 84 | * Set the value of number of columns per carousel content block. 85 | * 86 | * @param integer $value 87 | * 88 | * @return self 89 | */ 90 | public function setButtonsGroupColumns($value) 91 | { 92 | $this->ButtonsGroupColumns = $value; 93 | 94 | return $this; 95 | } 96 | 97 | /** 98 | * Get the value of number of rows per carousel content block 99 | * 100 | * @return string 101 | */ 102 | public function getButtonsGroupRows() 103 | { 104 | return $this->ButtonsGroupRows; 105 | } 106 | 107 | /** 108 | * Set the value of number of rows per carousel content block 109 | * 110 | * @param integer $value 111 | * 112 | * @return self 113 | */ 114 | public function setButtonsGroupRows($value) 115 | { 116 | $this->ButtonsGroupRows = $value; 117 | 118 | return $this; 119 | } 120 | 121 | /** 122 | * Get the value of background color of carousel content message 123 | * 124 | * @return string 125 | */ 126 | public function getBgColor() 127 | { 128 | return $this->BgColor; 129 | } 130 | 131 | /** 132 | * Set the value of background color of carousel content message 133 | * 134 | * @param string BgColor 135 | * 136 | * @return self 137 | */ 138 | public function setBgColor($BgColor) 139 | { 140 | $this->BgColor = $BgColor; 141 | 142 | return $this; 143 | } 144 | 145 | /** 146 | * Get the rich media buttons 147 | * 148 | * @return array 149 | */ 150 | public function getButtons() 151 | { 152 | return $this->Buttons; 153 | } 154 | 155 | /** 156 | * Set the rich media buttons 157 | * 158 | * @param array $Buttons 159 | * 160 | * @return self 161 | */ 162 | public function setButtons($Buttons) 163 | { 164 | $this->Buttons = $Buttons; 165 | 166 | return $this; 167 | } 168 | 169 | /** 170 | * Build buttons api array 171 | * 172 | * @return array 173 | */ 174 | protected function getButtonsApiArray() 175 | { 176 | $buttons = []; 177 | foreach ($this->getButtons() as $i) { 178 | $buttons[] = $i->toApiArray(); 179 | } 180 | 181 | return $buttons; 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /src/Api/Message/Contact.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class Contact extends Message 13 | { 14 | /** 15 | * Name of the contact. Max 28 characters. 16 | * 17 | * @var string 18 | */ 19 | protected $name; 20 | 21 | /** 22 | * Phone number of the contact. Max 18 characters 23 | * 24 | * @var string 25 | */ 26 | protected $phone_number; 27 | 28 | /** 29 | * {@inheritdoc} 30 | */ 31 | protected $propertiesMap = [ 32 | 'contact' => 'setConcat' 33 | ]; 34 | 35 | /** 36 | * {@inheritdoc} 37 | */ 38 | public function getType() 39 | { 40 | return Type::CONTACT; 41 | } 42 | 43 | /** 44 | * {@inheritdoc} 45 | */ 46 | public function toArray() 47 | { 48 | return array_merge(parent::toArray(), [ 49 | 'contact' => [ 50 | 'name' => $this->getName(), 51 | 'phone_number' => $this->getPhoneNumber(), 52 | ] 53 | ]); 54 | } 55 | 56 | /** 57 | * Get the value of Name of the contact. Max 28 characters. 58 | * 59 | * @return string 60 | */ 61 | public function getName() 62 | { 63 | return $this->name; 64 | } 65 | 66 | /** 67 | * Set the value of Name of the contact. Max 28 characters. 68 | * 69 | * @param string name 70 | * 71 | * @return self 72 | */ 73 | public function setName($name) 74 | { 75 | $this->name = $name; 76 | 77 | return $this; 78 | } 79 | 80 | /** 81 | * Get the value of Phone number of the contact. Max 18 characters 82 | * 83 | * @return string 84 | */ 85 | public function getPhoneNumber() 86 | { 87 | return $this->phone_number; 88 | } 89 | 90 | /** 91 | * Set the value of Phone number of the contact. Max 18 characters 92 | * 93 | * @param string phone_number 94 | * 95 | * @return self 96 | */ 97 | public function setPhoneNumber($phone_number) 98 | { 99 | $this->phone_number = $phone_number; 100 | 101 | return $this; 102 | } 103 | 104 | /** 105 | * Set the value of Phone number of the contact from contact array. 106 | * 107 | * @param array contact 108 | * 109 | * @return self 110 | */ 111 | public function setConcat($contact) 112 | { 113 | $this->phone_number = $contact['phone_number']; 114 | 115 | return $this; 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/Api/Message/Factory.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class Factory 13 | { 14 | /** 15 | * Make certain message from api-request array 16 | * 17 | * @param array $data api request data 18 | * @return \Viber\Api\Message 19 | */ 20 | public static function makeFromApi(array $data) 21 | { 22 | if (isset($data['type'])) { 23 | switch ($data['type']) { 24 | case Type::TEXT: 25 | return new Text($data); 26 | case Type::URL: 27 | return new Url($data); 28 | case Type::PICTURE: 29 | return new Picture($data); 30 | case Type::CONTACT: 31 | return new Contact($data); 32 | case Type::VIDEO: 33 | return new Video($data); 34 | case Type::FILE: 35 | return new File($data); 36 | case Type::STICKER: 37 | return new Sticker($data); 38 | case Type::LOCATION: 39 | return new Location($data); 40 | } 41 | } 42 | throw new ApiException('Unknow message data'); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Api/Message/File.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class File extends Message 13 | { 14 | /** 15 | * URL of the file 16 | * 17 | * @var string 18 | */ 19 | protected $media; 20 | 21 | /** 22 | * Size of the file in bytes 23 | * 24 | * @var integer 25 | */ 26 | protected $size; 27 | 28 | /** 29 | * Name of the file. 30 | * File name should include extension. 31 | * Max 256 characters (including file extension) 32 | * 33 | * @var string 34 | */ 35 | protected $file_name; 36 | 37 | /** 38 | * message type 39 | * @return [type] [description] 40 | */ 41 | public function getType() 42 | { 43 | return Type::FILE; 44 | } 45 | 46 | /** 47 | * {@inheritdoc} 48 | */ 49 | public function toArray() 50 | { 51 | return array_merge(parent::toArray(), [ 52 | 'media' => $this->getMedia(), 53 | 'size' => $this->getSize(), 54 | 'file_name' => $this->getFileName(), 55 | ]); 56 | } 57 | 58 | /** 59 | * Get the value of URL of the file 60 | * 61 | * @return string 62 | */ 63 | public function getMedia() 64 | { 65 | return $this->media; 66 | } 67 | 68 | /** 69 | * Set the value of URL of the file 70 | * 71 | * @param string media 72 | * 73 | * @return static 74 | */ 75 | public function setMedia($media) 76 | { 77 | $this->media = $media; 78 | 79 | return $this; 80 | } 81 | 82 | /** 83 | * Get the value of Size of the file in bytes 84 | * 85 | * @return integer 86 | */ 87 | public function getSize() 88 | { 89 | return $this->size; 90 | } 91 | 92 | /** 93 | * Set the value of Size of the file in bytes 94 | * 95 | * @param integer size 96 | * 97 | * @return static 98 | */ 99 | public function setSize($size) 100 | { 101 | $this->size = $size; 102 | 103 | return $this; 104 | } 105 | 106 | /** 107 | * Get the value of Name of the file. 108 | * 109 | * @return string 110 | */ 111 | public function getFileName() 112 | { 113 | return $this->file_name; 114 | } 115 | 116 | /** 117 | * Set the value of Name of the file. 118 | * 119 | * @param string file_name 120 | * 121 | * @return static 122 | */ 123 | public function setFileName($file_name) 124 | { 125 | $this->file_name = $file_name; 126 | 127 | return $this; 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /src/Api/Message/Location.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class Location extends Message 13 | { 14 | /** 15 | * Location coordinates. With "lat" and "lon" keys 16 | * 17 | * @var array 18 | */ 19 | protected $location = ['lat' => 0, 'lon' => 0]; 20 | 21 | /** 22 | * {@inheritdoc} 23 | */ 24 | public function getType() 25 | { 26 | return Type::LOCATION; 27 | } 28 | 29 | /** 30 | * {@inheritdoc} 31 | */ 32 | public function toArray() 33 | { 34 | return array_merge(parent::toArray(), [ 35 | 'location' => $this->getLocation() 36 | ]); 37 | } 38 | 39 | /** 40 | * Get the value of Location coordinates. 41 | * 42 | * @return array 43 | */ 44 | public function getLocation() 45 | { 46 | return $this->location; 47 | } 48 | 49 | /** 50 | * Set the value of Location coordinates. 51 | * 52 | * @param array location [lat => 0, lon => 0] 53 | * 54 | * @return static 55 | */ 56 | public function setLocation(array $location) 57 | { 58 | $this->location = $location; 59 | 60 | return $this; 61 | } 62 | 63 | /** 64 | * Set latitude coordinate part 65 | * 66 | * @param float $lat 67 | * 68 | * @return static 69 | */ 70 | public function setLat($lat) 71 | { 72 | $this->location['lat'] = $lat; 73 | 74 | return $this; 75 | } 76 | 77 | /** 78 | * Set longitude coordinate part 79 | * 80 | * @param float $lon 81 | * 82 | * @return static 83 | */ 84 | public function setLng($lon) 85 | { 86 | $this->location['lon'] = $lon; 87 | 88 | return $this; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/Api/Message/Picture.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class Picture extends Message 13 | { 14 | /** 15 | * Description of image 16 | * @var string 17 | */ 18 | protected $text; 19 | 20 | /** 21 | * URL of the image (JPEG) 22 | * @var string 23 | */ 24 | protected $media; 25 | 26 | /** 27 | * URL of a reduced size image (JPEG) 28 | * @var string 29 | */ 30 | protected $thumbnail; 31 | 32 | /** 33 | * Size of the file in bytes 34 | * 35 | * @var integer 36 | */ 37 | protected $size; 38 | 39 | /** 40 | * Name of the file. 41 | * File name should include extension. 42 | * Max 256 characters (including file extension) 43 | * 44 | * @var string 45 | */ 46 | protected $file_name; 47 | 48 | /** 49 | * {@inheritdoc} 50 | */ 51 | public function getType() 52 | { 53 | return Type::PICTURE; 54 | } 55 | 56 | /** 57 | * {@inheritdoc} 58 | */ 59 | public function toArray() 60 | { 61 | return array_merge(parent::toArray(), [ 62 | 'text' => $this->getText(), 63 | 'media' => $this->getMedia(), 64 | 'thumbnail' => $this->getThumbnail(), 65 | 'size' => $this->getSize(), 66 | 'file_name' => $this->getFileName(), 67 | ]); 68 | } 69 | 70 | /** 71 | * Get the value of Description of image 72 | * 73 | * @return string 74 | */ 75 | public function getText() 76 | { 77 | return $this->text; 78 | } 79 | 80 | /** 81 | * Set the value of Description of image 82 | * 83 | * @param string text 84 | * 85 | * @return self 86 | */ 87 | public function setText($text) 88 | { 89 | $this->text = $text; 90 | 91 | return $this; 92 | } 93 | 94 | /** 95 | * Get the value of URL of the image (JPEG) 96 | * 97 | * @return string 98 | */ 99 | public function getMedia() 100 | { 101 | return $this->media; 102 | } 103 | 104 | /** 105 | * Set the value of URL of the image (JPEG) 106 | * 107 | * @param string media 108 | * 109 | * @return self 110 | */ 111 | public function setMedia($media) 112 | { 113 | $this->media = $media; 114 | 115 | return $this; 116 | } 117 | 118 | /** 119 | * Get the value of URL of a reduced size image (JPEG) 120 | * 121 | * @return string 122 | */ 123 | public function getThumbnail() 124 | { 125 | return $this->thumbnail; 126 | } 127 | 128 | /** 129 | * Set the value of URL of a reduced size image (JPEG) 130 | * 131 | * @param string thumbnail 132 | * 133 | * @return self 134 | */ 135 | public function setThumbnail($thumbnail) 136 | { 137 | $this->thumbnail = $thumbnail; 138 | 139 | return $this; 140 | } 141 | 142 | /** 143 | * Get the value of Size of the file in bytes 144 | * 145 | * @return integer 146 | */ 147 | public function getSize() 148 | { 149 | return $this->size; 150 | } 151 | 152 | /** 153 | * Set the value of Size of the file in bytes 154 | * 155 | * @param integer size 156 | * 157 | * @return static 158 | */ 159 | public function setSize($size) 160 | { 161 | $this->size = $size; 162 | 163 | return $this; 164 | } 165 | 166 | /** 167 | * Get the value of Name of the file. 168 | * 169 | * @return string 170 | */ 171 | public function getFileName() 172 | { 173 | return $this->file_name; 174 | } 175 | 176 | /** 177 | * Set the value of Name of the file. 178 | * 179 | * @param string file_name 180 | * 181 | * @return static 182 | */ 183 | public function setFileName($file_name) 184 | { 185 | $this->file_name = $file_name; 186 | 187 | return $this; 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /src/Api/Message/Sticker.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | class Sticker extends Message 15 | { 16 | /** 17 | * Unique Viber sticker ID. 18 | * 19 | * @var integer 20 | */ 21 | protected $sticker_id; 22 | 23 | /** 24 | * {@inheritdoc} 25 | */ 26 | public function getType() 27 | { 28 | return Type::STICKER; 29 | } 30 | 31 | /** 32 | * {@inheritdoc} 33 | */ 34 | public function toArray() 35 | { 36 | return array_merge(parent::toArray(), [ 37 | 'sticker_id' => $this->getStickerId(), 38 | ]); 39 | } 40 | 41 | /** 42 | * Get the value of Unique Viber sticker ID. 43 | * 44 | * @return integer 45 | */ 46 | public function getStickerId() 47 | { 48 | return $this->sticker_id; 49 | } 50 | 51 | /** 52 | * Set the value of Unique Viber sticker ID. 53 | * 54 | * @param integer sticker_id 55 | * 56 | * @return static 57 | */ 58 | public function setStickerId($sticker_id) 59 | { 60 | $this->sticker_id = $sticker_id; 61 | 62 | return $this; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/Api/Message/Text.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class Text extends Message 13 | { 14 | /** 15 | * The text of the message 16 | * 17 | * @var string 18 | */ 19 | protected $text; 20 | 21 | /** 22 | * {@inheritdoc} 23 | */ 24 | public function getType() 25 | { 26 | return Type::TEXT; 27 | } 28 | 29 | /** 30 | * {@inheritdoc} 31 | */ 32 | public function toArray() 33 | { 34 | return array_merge(parent::toArray(), [ 35 | 'text' => $this->getText() 36 | ]); 37 | } 38 | 39 | /** 40 | * Get the value of The text of the message 41 | * 42 | * @return string 43 | */ 44 | public function getText() 45 | { 46 | return $this->text; 47 | } 48 | 49 | /** 50 | * Set the value of The text of the message 51 | * 52 | * @param string text 53 | * 54 | * @return static 55 | */ 56 | public function setText($text) 57 | { 58 | $this->text = $text; 59 | 60 | return $this; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/Api/Message/Type.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | interface Type 11 | { 12 | const TEXT = 'text'; 13 | const PICTURE = 'picture'; 14 | const VIDEO = 'video'; 15 | const FILE = 'file'; 16 | const STICKER = 'sticker'; 17 | const CONTACT = 'contact'; 18 | const URL = 'url'; 19 | const LOCATION = 'location'; 20 | const RICH_MEDIA = 'rich_media'; 21 | } 22 | -------------------------------------------------------------------------------- /src/Api/Message/Url.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class Url extends Message 13 | { 14 | /** 15 | * URL. Max 2,000 characters 16 | * 17 | * @var string 18 | */ 19 | protected $media; 20 | 21 | /** 22 | * {@inheritdoc} 23 | */ 24 | public function getType() 25 | { 26 | return Type::URL; 27 | } 28 | 29 | /** 30 | * {@inheritdoc} 31 | */ 32 | public function toArray() 33 | { 34 | return array_merge(parent::toArray(), [ 35 | 'media' => $this->getMedia() 36 | ]); 37 | } 38 | 39 | /** 40 | * Get the value of URL. Max 2,000 characters 41 | * 42 | * @return string 43 | */ 44 | public function getMedia() 45 | { 46 | return $this->media; 47 | } 48 | 49 | /** 50 | * Set the value of URL. Max 2,000 characters 51 | * 52 | * @param string media 53 | * 54 | * @return static 55 | */ 56 | public function setMedia($media) 57 | { 58 | $this->media = $media; 59 | 60 | return $this; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/Api/Message/Video.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class Video extends Message 13 | { 14 | /** 15 | * URL of the video (MP4, H264) 16 | * 17 | * @var string 18 | */ 19 | protected $media; 20 | 21 | /** 22 | * Size of the video in bytes 23 | * 24 | * @var integer 25 | */ 26 | protected $size; 27 | 28 | /** 29 | * Video duration in seconds 30 | * 31 | * @var integer 32 | */ 33 | protected $duration; 34 | 35 | /** 36 | * URL of a reduced size image (JPEG) 37 | * 38 | * @var string 39 | */ 40 | protected $thumbnail; 41 | 42 | /** 43 | * {@inheritdoc} 44 | */ 45 | public function getType() 46 | { 47 | return Type::VIDEO; 48 | } 49 | 50 | /** 51 | * {@inheritdoc} 52 | */ 53 | public function toArray() 54 | { 55 | return array_merge(parent::toArray(), [ 56 | 'media' => $this->getMedia(), 57 | 'size' => $this->getSize(), 58 | 'duration' => $this->getDuration(), 59 | 'thumbnail' => $this->getThumbnail(), 60 | ]); 61 | } 62 | 63 | /** 64 | * Get the value of URL of the video (MP4, H264) 65 | * 66 | * @return string 67 | */ 68 | public function getMedia() 69 | { 70 | return $this->media; 71 | } 72 | 73 | /** 74 | * Set the value of URL of the video (MP4, H264) 75 | * 76 | * @param string media 77 | * 78 | * @return static 79 | */ 80 | public function setMedia($media) 81 | { 82 | $this->media = $media; 83 | 84 | return $this; 85 | } 86 | 87 | /** 88 | * Get the value of Size of the video in bytes 89 | * 90 | * @return integer 91 | */ 92 | public function getSize() 93 | { 94 | return $this->size; 95 | } 96 | 97 | /** 98 | * Set the value of Size of the video in bytes 99 | * 100 | * @param integer size 101 | * 102 | * @return static 103 | */ 104 | public function setSize($size) 105 | { 106 | $this->size = $size; 107 | 108 | return $this; 109 | } 110 | 111 | /** 112 | * Get the value of Video duration in seconds 113 | * 114 | * @return integer 115 | */ 116 | public function getDuration() 117 | { 118 | return $this->duration; 119 | } 120 | 121 | /** 122 | * Set the value of Video duration in seconds 123 | * 124 | * @param integer duration 125 | * 126 | * @return static 127 | */ 128 | public function setDuration($duration) 129 | { 130 | $this->duration = $duration; 131 | 132 | return $this; 133 | } 134 | 135 | /** 136 | * Get the value of URL of a reduced size image (JPEG) 137 | * 138 | * @return string 139 | */ 140 | public function getThumbnail() 141 | { 142 | return $this->thumbnail; 143 | } 144 | 145 | /** 146 | * Set the value of URL of a reduced size image (JPEG) 147 | * 148 | * @param string thumbnail 149 | * 150 | * @return static 151 | */ 152 | public function setThumbnail($thumbnail) 153 | { 154 | $this->thumbnail = $thumbnail; 155 | 156 | return $this; 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /src/Api/Response.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class Response 13 | { 14 | /** 15 | * Raw response data 16 | * 17 | * @var array 18 | */ 19 | protected $data; 20 | 21 | /** 22 | * Create api response from http-response 23 | * 24 | * @param \GuzzleHttp\Psr7\Response $response network response 25 | * @return \Viber\Api\Response 26 | * @throws \Viber\Api\Exception\ApiException 27 | */ 28 | public static function create(\GuzzleHttp\Psr7\Response $response) 29 | { 30 | // - validate body 31 | $data = json_decode($response->getBody(), true, 512, JSON_BIGINT_AS_STRING); 32 | if (empty($data)) { 33 | throw new ApiException('Invalid response body'); 34 | } 35 | // - validate internal data 36 | if (isset($data['status'])) { 37 | if ($data['status'] != 0) { 38 | throw new ApiException('Remote error: ' . 39 | (isset($data['status_message']) ? $data['status_message'] : '-'), 40 | $data['status']); 41 | } 42 | $item = new self(); 43 | $item->data = $data; 44 | return $item; 45 | } 46 | throw new ApiException('Invalid response json'); 47 | } 48 | 49 | /** 50 | * Get the value of Raw response data 51 | * 52 | * @return array 53 | */ 54 | public function getData() 55 | { 56 | return $this->data; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Api/Sender.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | class Sender extends Entity 11 | { 12 | /** 13 | * Viber User id 14 | * 15 | * @var string 16 | */ 17 | protected $id; 18 | 19 | /** 20 | * Viber name 21 | * 22 | * @var string 23 | */ 24 | protected $name; 25 | 26 | /** 27 | * URL of the user's avatar 28 | * 29 | * @var string 30 | */ 31 | protected $avatar; 32 | 33 | /** 34 | * {@inheritDoc} 35 | */ 36 | protected $propertiesMap = [ 37 | 'id' => 'setId', 38 | 'name' => 'setName', 39 | 'avatar' => 'setAvatar', 40 | ]; 41 | 42 | /** 43 | * {@inheritDoc} 44 | */ 45 | public function toArray() 46 | { 47 | return [ 48 | 'id' => $this->getId(), 49 | 'name' => $this->getName(), 50 | 'avatar' => $this->getAvatar(), 51 | ]; 52 | } 53 | 54 | /** 55 | * Get the value of Viber User id 56 | * 57 | * @return [type] 58 | */ 59 | public function getId() 60 | { 61 | return $this->id; 62 | } 63 | 64 | /** 65 | * Set the value of Viber User id 66 | * 67 | * @param string id 68 | * 69 | * @return self 70 | */ 71 | public function setId($id) 72 | { 73 | $this->id = $id; 74 | 75 | return $this; 76 | } 77 | 78 | /** 79 | * Get the value of Viber name 80 | * 81 | * @return string 82 | */ 83 | public function getName() 84 | { 85 | return $this->name; 86 | } 87 | 88 | /** 89 | * Set the value of Viber name 90 | * 91 | * @param string name 92 | * 93 | * @return self 94 | */ 95 | public function setName($name) 96 | { 97 | $this->name = $name; 98 | 99 | return $this; 100 | } 101 | 102 | /** 103 | * Get the value of URL of the user's avatar 104 | * 105 | * @return string 106 | */ 107 | public function getAvatar() 108 | { 109 | return $this->avatar; 110 | } 111 | 112 | /** 113 | * Set the value of URL of the user's avatar 114 | * 115 | * @param string avatar 116 | * 117 | * @return self 118 | */ 119 | public function setAvatar($avatar) 120 | { 121 | $this->avatar = $avatar; 122 | 123 | return $this; 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/Api/Signature.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | class Signature 11 | { 12 | /** 13 | * Make signature value 14 | * 15 | * @param string $messageBody request body 16 | * @param string $token bot token 17 | * @return string signature 18 | */ 19 | public static function make($messageBody, $token) 20 | { 21 | return hash_hmac('sha256', $messageBody, $token); 22 | } 23 | 24 | /** 25 | * Is message signatore valid? 26 | * 27 | * @param string $sign from request headers 28 | * @param string $messageBody from request body 29 | * @param string $token bot access token 30 | * @return boolean valid or not 31 | */ 32 | public static function isValid($sign, $messageBody, $token) 33 | { 34 | return hash_equals($sign, self::make($messageBody, $token)); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Api/User.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class User extends Entity 13 | { 14 | /** 15 | * Unique Viber user id 16 | * 17 | * @var string 18 | */ 19 | protected $id; 20 | 21 | /** 22 | * User name 23 | * 24 | * @var string 25 | */ 26 | protected $name; 27 | 28 | /** 29 | * URL of the user's avatar 30 | * 31 | * @var string 32 | */ 33 | protected $avatar; 34 | 35 | /** 36 | * User's country code 37 | * 38 | * @var string 39 | */ 40 | protected $country; 41 | 42 | /** 43 | * User’s phone language. Will be returned according to the device language 44 | * 45 | * @see ISO 639-1 46 | * 47 | * @var string 48 | */ 49 | protected $language; 50 | 51 | /** 52 | * The operating system type and version of the user's primary device. 53 | * 54 | * @var string 55 | */ 56 | protected $primary_device_os; 57 | 58 | /** 59 | * Max API version, matching the most updated user's device 60 | * 61 | * @var integer 62 | */ 63 | protected $api_version; 64 | 65 | /** 66 | * The Viber version installed on the user's primary device 67 | * 68 | * @var string 69 | */ 70 | protected $viber_version; 71 | 72 | /** 73 | * Mobile country code 74 | * 75 | * @var string 76 | */ 77 | protected $mcc; 78 | 79 | /** 80 | * Mobile network code 81 | * 82 | * @var string 83 | */ 84 | protected $mnc; 85 | 86 | /** 87 | * Create user instance from api response array 88 | * 89 | * @throws \Viber\Api\Exception\ApiException 90 | * @param array $properties list of properties 91 | */ 92 | public function __construct($properties) 93 | { 94 | if (!is_array($properties) && !$properties instanceof \ArrayAccess) { 95 | throw new ApiException('Properties must be an array or implement ArrayAccess'); 96 | } 97 | foreach ($properties as $propertyName => $propertyValue) { 98 | if (property_exists($this, $propertyName)) { 99 | $this->$propertyName = $propertyValue; 100 | } 101 | } 102 | } 103 | 104 | /** 105 | * {@inheritDoc} 106 | */ 107 | public function toArray() 108 | { 109 | return [ 110 | 'id' => $this->getId(), 111 | 'name' => $this->getName(), 112 | 'avatar' => $this->getAvatar(), 113 | 'country' => $this->getCountry(), 114 | 'language' => $this->getLanguage(), 115 | 'primary_device_os' => $this->getPrimaryDeviceOs(), 116 | 'api_version' => $this->getApiVersion(), 117 | 'viber_version' => $this->getViberVersion(), 118 | 'mcc' => $this->getMcc(), 119 | 'mnc' => $this->getMnc(), 120 | ]; 121 | } 122 | 123 | /** 124 | * Get the value of Unique Viber user id 125 | * 126 | * @return string 127 | */ 128 | public function getId() 129 | { 130 | return $this->id; 131 | } 132 | 133 | /** 134 | * Get the value of User's Viber name 135 | * 136 | * @return string 137 | */ 138 | public function getName() 139 | { 140 | return $this->name; 141 | } 142 | 143 | /** 144 | * Get the value of URL of the user's avatar 145 | * 146 | * @return string 147 | */ 148 | public function getAvatar() 149 | { 150 | return $this->avatar; 151 | } 152 | 153 | /** 154 | * Get the value of User's country code 155 | * 156 | * @return string 157 | */ 158 | public function getCountry() 159 | { 160 | return $this->country; 161 | } 162 | 163 | /** 164 | * Get the value of User’s phone language. Will be returned according to the device language 165 | * 166 | * @return string 167 | */ 168 | public function getLanguage() 169 | { 170 | return $this->language; 171 | } 172 | 173 | /** 174 | * Get the value of The operating system type and version of the user's primary device. 175 | * 176 | * @return string 177 | */ 178 | public function getPrimaryDeviceOs() 179 | { 180 | return $this->primary_device_os; 181 | } 182 | 183 | /** 184 | * Get the value of Max API version, matching the most updated user's device 185 | * 186 | * @return integer 187 | */ 188 | public function getApiVersion() 189 | { 190 | return $this->api_version; 191 | } 192 | 193 | /** 194 | * Get the value of The Viber version installed on the user's primary device 195 | * 196 | * @return string 197 | */ 198 | public function getViberVersion() 199 | { 200 | return $this->viber_version; 201 | } 202 | 203 | /** 204 | * Get the value of Mobile country code 205 | * 206 | * @return string 207 | */ 208 | public function getMcc() 209 | { 210 | return $this->mcc; 211 | } 212 | 213 | /** 214 | * Get the value of Mobile network code 215 | * 216 | * @return string 217 | */ 218 | public function getMnc() 219 | { 220 | return $this->mnc; 221 | } 222 | } 223 | -------------------------------------------------------------------------------- /src/Api/User/State.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class State extends Entity 13 | { 14 | /** 15 | * Available status 16 | * 17 | * @var integer 18 | */ 19 | const ONLINE = 0; 20 | const OFFLINE = 1; 21 | const UNDISCLOSED = 2; 22 | const ERROR = 3; 23 | const UNAVAILABLE = 4; 24 | 25 | /** 26 | * Viber user id 27 | * @var integer 28 | */ 29 | protected $id; 30 | 31 | /** 32 | * User status 33 | * @var integer 34 | */ 35 | protected $status; 36 | 37 | /** 38 | * Status description 39 | * @var string 40 | */ 41 | protected $message; 42 | 43 | /** 44 | * {@inheritDoc} 45 | */ 46 | protected $propertiesMap = [ 47 | 'id' => 'setId', 48 | 'online_status' => 'setStatus', 49 | 'online_status_message' => 'setMessage', 50 | ]; 51 | 52 | /** 53 | * {@inheritDoc} 54 | */ 55 | public function toArray() 56 | { 57 | return [ 58 | 'id' => $this->getId(), 59 | 'online_status' => $this->getStatus(), 60 | 'online_status_message' => $this->getMessage(), 61 | ]; 62 | } 63 | 64 | /** 65 | * Get the value of Viber user id 66 | * 67 | * @return integer 68 | */ 69 | public function getId() 70 | { 71 | return $this->id; 72 | } 73 | 74 | /** 75 | * Set the value of Viber user id 76 | * 77 | * @param integer id 78 | * 79 | * @return self 80 | */ 81 | public function setId($id) 82 | { 83 | $this->id = $id; 84 | 85 | return $this; 86 | } 87 | 88 | /** 89 | * Get the value of Status code 90 | * 91 | * @return integer 92 | */ 93 | public function getStatus() 94 | { 95 | return $this->status; 96 | } 97 | 98 | /** 99 | * Set the value of Status code 100 | * 101 | * @param integer status 102 | * 103 | * @return self 104 | */ 105 | public function setStatus($status) 106 | { 107 | $this->status = $status; 108 | 109 | return $this; 110 | } 111 | 112 | /** 113 | * Get the value of Status description 114 | * 115 | * @return string 116 | */ 117 | public function getMessage() 118 | { 119 | return $this->message; 120 | } 121 | 122 | /** 123 | * Set the value of Status description 124 | * 125 | * @param string message 126 | * 127 | * @return self 128 | */ 129 | public function setMessage($message) 130 | { 131 | $this->message = $message; 132 | 133 | return $this; 134 | } 135 | 136 | /** 137 | * Is user online? 138 | * 139 | * @return boolean 140 | */ 141 | public function isOnline() 142 | { 143 | return $this->status == self::ONLINE; 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /src/Bot.php: -------------------------------------------------------------------------------- 1 | 16 | */ 17 | class Bot 18 | { 19 | /** 20 | * Api client 21 | * 22 | * @var \Viber\Client 23 | */ 24 | protected $client; 25 | 26 | /** 27 | * Event managers collection 28 | * 29 | * @var array 30 | */ 31 | protected $managers = []; 32 | 33 | /** 34 | * Init client 35 | * 36 | * Required options (one of two): 37 | * token string 38 | * client \Viber\Client 39 | * 40 | * @throws \RuntimeException 41 | * @param array $options 42 | */ 43 | public function __construct(array $options) 44 | { 45 | if (isset($options['token'])) { 46 | $this->client = new Client($options); 47 | } elseif (isset($options['client']) && $options['client'] instanceof Client) { 48 | $this->client = $options['client']; 49 | } else { 50 | throw new \RuntimeException('Specify "client" or "token" parameter'); 51 | } 52 | } 53 | 54 | /** 55 | * Get current bot client 56 | * 57 | * @return |Viber\Client 58 | */ 59 | public function getClient() 60 | { 61 | return $this->client; 62 | } 63 | 64 | /** 65 | * Register event handler callback 66 | * 67 | * @param \Closure $checker checker function 68 | * @param \Closure $handler handler function 69 | * 70 | * @return \Viber\Bot 71 | */ 72 | public function on(\Closure $checker, \Closure $handler) 73 | { 74 | $this->managers[] = new Manager($checker, $handler); 75 | 76 | return $this; 77 | } 78 | 79 | /** 80 | * Register text message handler by PCRE 81 | * 82 | * @param string $regexp valid regular expression 83 | * @param Closure $handler event handler 84 | * @return \Viber\Bot 85 | */ 86 | public function onText($regexp, \Closure $handler) 87 | { 88 | $this->managers[] = new Manager(function (Event $event) use ($regexp) { 89 | return ( 90 | $event instanceof \Viber\Api\Event\Message 91 | && $event->getMessage() instanceof \Viber\Api\Message\Text 92 | && preg_match($regexp, $event->getMessage()->getText()) 93 | ); 94 | }, $handler); 95 | 96 | return $this; 97 | } 98 | 99 | /** 100 | * Register subscrive event handler 101 | * 102 | * @param Closure $handler valid function 103 | * @return \Viber\Bot 104 | */ 105 | public function onSubscribe(\Closure $handler) 106 | { 107 | $this->managers[] = new Manager(function (Event $event) { 108 | return ($event instanceof \Viber\Api\Event\Subscribed); 109 | }, $handler); 110 | 111 | return $this; 112 | } 113 | 114 | /** 115 | * Register conversation event handler 116 | * 117 | * @param Closure $handler valid function 118 | * @return \Viber\Bot 119 | */ 120 | public function onConversation(\Closure $handler) 121 | { 122 | $this->managers[] = new Manager(function (Event $event) { 123 | return ($event instanceof \Viber\Api\Event\Conversation); 124 | }, $handler); 125 | 126 | return $this; 127 | } 128 | 129 | /** 130 | * Register picture message handler 131 | * 132 | * @param Closure $handler event handler 133 | * @return \Viber\Bot 134 | */ 135 | public function onPicture(\Closure $handler) 136 | { 137 | $this->managers[] = new Manager(function (Event $event) { 138 | return ( 139 | $event instanceof \Viber\Api\Event\Message 140 | && $event->getMessage() instanceof \Viber\Api\Message\Picture 141 | ); 142 | }, $handler); 143 | 144 | return $this; 145 | } 146 | 147 | /** 148 | * Get signature header 149 | * 150 | * @throws \RuntimeException 151 | * @return string 152 | */ 153 | public function getSignHeaderValue() 154 | { 155 | $signature = ''; 156 | if (isset($_SERVER['HTTP_X_VIBER_CONTENT_SIGNATURE'])) { 157 | $signature = $_SERVER['HTTP_X_VIBER_CONTENT_SIGNATURE']; 158 | } elseif (isset($_GET['sig'])) { 159 | $signature = $_GET['sig']; 160 | } 161 | if (empty($signature)) { 162 | throw new \RuntimeException('Signature header not found', 1); 163 | } 164 | 165 | return $signature; 166 | } 167 | 168 | /** 169 | * Get bot input stream 170 | * 171 | * @return string 172 | */ 173 | public function getInputBody() 174 | { 175 | return file_get_contents('php://input'); 176 | } 177 | 178 | /** 179 | * Response with entity 180 | * 181 | * @param Entity $entity 182 | * @return void 183 | */ 184 | public function outputEntity(Entity $entity) 185 | { 186 | header('Content-Type: application/json'); 187 | echo json_encode($entity->toApiArray()); 188 | } 189 | 190 | /** 191 | * Start bot process 192 | * 193 | * @throws \RuntimeException 194 | * @param \Viber\Api\Event $event start bot with some event 195 | * @return \Viber\Bot 196 | */ 197 | public function run($event = null) 198 | { 199 | if (null === $event) { 200 | // check body 201 | $eventBody = $this->getInputBody(); 202 | 203 | if (!Signature::isValid( 204 | $this->getSignHeaderValue(), 205 | $eventBody, 206 | $this->getClient()->getToken() 207 | )) { 208 | throw new \RuntimeException('Invalid signature header', 2); 209 | } 210 | // check json 211 | $eventBody = json_decode($eventBody, true); 212 | if (json_last_error() || empty($eventBody) || !is_array($eventBody)) { 213 | throw new \RuntimeException('Invalid json request', 3); 214 | } 215 | // make event from json 216 | $event = Factory::makeFromApi($eventBody); 217 | } elseif (!$event instanceof Event) { 218 | throw new \RuntimeException('Event must be instance of \Viber\Api\Event', 4); 219 | } 220 | // main bot loop 221 | foreach ($this->managers as $manager) { 222 | if ($manager->isMatch($event)) { 223 | $returnValue = $manager->runHandler($event); 224 | if ($returnValue && $returnValue instanceof Entity) { // reply with entity 225 | $this->outputEntity($returnValue); 226 | } 227 | break; 228 | } 229 | } 230 | 231 | return $this; 232 | } 233 | } 234 | -------------------------------------------------------------------------------- /src/Bot/Manager.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class Manager 13 | { 14 | /** 15 | * Check if we trigger handler 16 | * 17 | * @var \Closure 18 | */ 19 | protected $checker; 20 | 21 | /** 22 | * Handler function 23 | * 24 | * @var \Closure 25 | */ 26 | protected $handler; 27 | 28 | /** 29 | * Create new event manager (event checker and event handler) 30 | * 31 | * @param \Closure $checker 32 | * @param \Closure $handler 33 | */ 34 | public function __construct(\Closure $checker, \Closure $handler) 35 | { 36 | $this->checker = $checker; 37 | $this->handler = $handler; 38 | } 39 | 40 | /** 41 | * Get the value of Check if we trigger handler 42 | * 43 | * @return \Closure 44 | */ 45 | public function getChecker() 46 | { 47 | return $this->checker; 48 | } 49 | 50 | /** 51 | * Get the value of Handler function 52 | * 53 | * @return \Closure 54 | */ 55 | public function getHandler() 56 | { 57 | return $this->handler; 58 | } 59 | 60 | /** 61 | * While event checker match current event? 62 | * 63 | * @param \Viber\Api\Event $event 64 | * @return boolean 65 | */ 66 | public function isMatch(Event $event) 67 | { 68 | if (is_callable($this->checker)) { 69 | return call_user_func($this->checker, $event); 70 | } 71 | return false; 72 | } 73 | 74 | /** 75 | * Process event with handler function 76 | * 77 | * @param \Viber\Api\Event $event 78 | * @return mixed event handler result 79 | */ 80 | public function runHandler(Event $event) 81 | { 82 | if (is_callable($this->handler)) { 83 | return call_user_func($this->handler, $event); 84 | } 85 | return false; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/Client.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | class Client 17 | { 18 | /** 19 | * Api endpoint base 20 | * 21 | * @var string 22 | */ 23 | const BASE_URI = 'https://chatapi.viber.com/pa/'; 24 | 25 | /** 26 | * Access token 27 | * 28 | * @var string 29 | */ 30 | protected $token; 31 | 32 | /** 33 | * Http network client 34 | * 35 | * @var \GuzzleHttp\Client 36 | */ 37 | protected $http; 38 | 39 | /** 40 | * Create api client. Options: 41 | * token required string authentication token 42 | * http optional array adapter parameters 43 | * 44 | * @throws \Viber\Api\Exception\ApiException 45 | * @param array $options 46 | */ 47 | public function __construct($options) 48 | { 49 | if (!isset($options['token'])) { 50 | throw new ApiException('No token provided'); 51 | } 52 | $this->token = $options['token']; 53 | $httpInit = [ 54 | 'base_uri' => self::BASE_URI, 55 | ]; 56 | if (isset($options['http']) && is_array($options['http'])) { 57 | $httpInit = array_merge($options['http'], $httpInit); 58 | } 59 | $this->http = new \GuzzleHttp\Client($httpInit); 60 | } 61 | 62 | /** 63 | * Get access token 64 | * 65 | * @return string 66 | */ 67 | public function getToken() 68 | { 69 | return $this->token; 70 | } 71 | 72 | /** 73 | * Call api method 74 | * 75 | * @throws \Viber\Api\Exception\ApiException 76 | * @param string $method method name 77 | * @param mixed $data method data 78 | * @return \Viber\Api\Response 79 | */ 80 | public function call($method, $data) 81 | { 82 | try { 83 | $response = $this->http->request('POST', $method, [ 84 | 'headers' => [ 85 | 'X-Viber-Auth-Token' => $this->token 86 | ], 87 | 'json' => $data 88 | ]); 89 | return \Viber\Api\Response::create($response); 90 | } catch (\RuntimeException $e) { 91 | throw new ApiException($e->getMessage(), $e->getCode(), $e); 92 | } 93 | } 94 | 95 | /** 96 | * Set webhook url. 97 | * 98 | * For security reasons only URLs with valid and * official SSL certificate 99 | * from a trusted CA will be allowed. 100 | * 101 | * @see \Viber\Api\Event\Type 102 | * @throws \Viber\Api\Exception\ApiException 103 | * @param string $url webhook url 104 | * @param array|null $eventTypes subscribe to certain events 105 | * @return \Viber\Api\Response 106 | */ 107 | public function setWebhook($url, $eventTypes = null) 108 | { 109 | if (null === $eventTypes) { 110 | $eventTypes = [Type::SUBSCRIBED, Type::CONVERSATION, Type::MESSAGE]; 111 | } 112 | if (empty($url) || !preg_match('|^https://.*|s', $url)) { 113 | throw new ApiException('Invalid webhook url: ' . $url); 114 | } 115 | 116 | return $this->call('set_webhook', [ 117 | 'url' => $url, 118 | 'event_types' => $eventTypes, 119 | ]); 120 | } 121 | 122 | /** 123 | * Delete webhook url. 124 | * 125 | * @return \Viber\Api\Response 126 | */ 127 | public function deleteWebhook() 128 | { 129 | return $this->call('set_webhook', [ 130 | 'url' => '', 131 | ]); 132 | } 133 | 134 | /** 135 | * Fetch the public account’s details as registered in Viber 136 | * 137 | * @throws \Viber\Api\Exception\ApiException 138 | * @return \Viber\Api\Response 139 | */ 140 | public function getAccountInfo() 141 | { 142 | return $this->call('get_account_info', [1 => 1]); 143 | } 144 | 145 | /** 146 | * Fetch the details of a specific Viber user based on his unique user ID. 147 | * 148 | * The user ID can be obtained from the callbacks sent to the PA regrading 149 | * user's actions. This request can be sent twice during a 12 hours period 150 | * for each user ID. 151 | * 152 | * @throws \Viber\Api\Exception\ApiException 153 | * @param string $userId 154 | * @return \Viber\Api\Response 155 | */ 156 | public function getUserDetails($userId) 157 | { 158 | return $this->call('get_user_details', [ 159 | 'id' => $userId 160 | ]); 161 | } 162 | 163 | /** 164 | * Fetch the online status of a given subscribed PA members. 165 | * 166 | * The API supports up to 100 user id per request and those users must be 167 | * subscribed to the PA. 168 | * 169 | * @throws \Viber\Api\Exception\ApiException 170 | * @param array $userIds list of user ids 171 | * @return \Viber\Api\Response 172 | */ 173 | public function getOnlineStatus(array $userIds) 174 | { 175 | return $this->call('get_online', [ 176 | 'ids' => $userIds 177 | ]); 178 | } 179 | 180 | /** 181 | * Send messages to Viber users who subscribe to the PA. 182 | * 183 | * @param \Viber\Api\Message $message 184 | * @return \Viber\Api\Response 185 | */ 186 | public function sendMessage(Message $message) 187 | { 188 | return $this->call('send_message', $message->toApiArray()); 189 | } 190 | 191 | /** 192 | * Send messages to multiple Viber users who subscribe to the PA. 193 | * 194 | * @param \Viber\Api\Message $message 195 | * @return \Viber\Api\Response 196 | */ 197 | public function broadcastMessage(Message $message) 198 | { 199 | return $this->call('broadcast_message', $message->toApiArray()); 200 | } 201 | } 202 | -------------------------------------------------------------------------------- /test/Viber/Tests/Api/EntityTest.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | class EntityTest extends TestCase 12 | { 13 | public function testApiArray() 14 | { 15 | $e = new Entity([]); 16 | $this->assertEquals([], $e->toApiArray()); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /test/Viber/Tests/Api/Event/FactoryTest.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class FactoryTest extends TestCase 13 | { 14 | /** 15 | * @expectedException \Viber\Api\Exception\ApiException 16 | * @expectedExceptionMessageRegExp |Unknow.*| 17 | */ 18 | public function testUnknowEvent() 19 | { 20 | Factory::makeFromApi([]); 21 | } 22 | 23 | /** 24 | * @expectedException \Viber\Api\Exception\ApiException 25 | * @expectedExceptionMessageRegExp |Unknow.*| 26 | */ 27 | public function testUnknowEventName() 28 | { 29 | Factory::makeFromApi([ 30 | 'event' => '8)' 31 | ]); 32 | } 33 | 34 | public function testMakeFromApi() 35 | { 36 | $eventDataList = [ 37 | [ 38 | 'type' => Type::DELIVERED, 39 | 'class' => \Viber\Api\Event\Delivered::class, 40 | 'sample' => [ 41 | "event" => "delivered", 42 | "timestamp" => 1457764197627, 43 | "message_token" => 491266184665523145, 44 | "user_id" => "01234567890A=" 45 | ] 46 | ], 47 | [ 48 | 'type' => Type::SUBSCRIBED, 49 | 'class' => \Viber\Api\Event\Subscribed::class, 50 | 'sample' => [ 51 | "event" => "subscribed", 52 | "timestamp" => 1457764197627, 53 | "user" => [ 54 | "id" => "01234567890A=", 55 | "name" => "John McClane", 56 | "avatar" => "http://avatar.example.com", 57 | "country" => "UK", 58 | "language" => "en", 59 | "api_version" => 1 60 | ], 61 | "message_token" => 4912661846655238145 62 | ] 63 | ], 64 | [ 65 | 'type' => Type::UNSUBSCRIBED, 66 | 'class' => \Viber\Api\Event\Unsubscribed::class, 67 | 'sample' => [ 68 | "event" => "unsubscribed", 69 | "timestamp" => 1457764197627, 70 | "user_id" => "01234567890A=", 71 | "message_token" => 4912661846655238145 72 | ], 73 | ], 74 | [ 75 | 'type' => Type::CONVERSATION, 76 | 'class' => \Viber\Api\Event\Conversation::class, 77 | 'sample' => [ 78 | "event" => "conversation_started", 79 | "timestamp" => 1457764197627, 80 | "message_token" => 4912661846655238145, 81 | "type" => "open", 82 | "context" => "context information", 83 | "user" => [ 84 | "id" => "01234567890A=", 85 | "name" => "John McClane", 86 | "avatar" => "http://avatar.example.com", 87 | "country" => "UK", 88 | "language" => "en", 89 | "api_version" => 1 90 | ] 91 | ], 92 | ], 93 | [ 94 | 'type' => Type::DELIVERED, 95 | 'class' => \Viber\Api\Event\Delivered::class, 96 | 'sample' => [ 97 | "event" => "delivered", 98 | "timestamp" => 1457764197627, 99 | "message_token" => 4912661846655238145, 100 | "user_id" => "01234567890A=" 101 | ] 102 | ], 103 | [ 104 | 'type' => Type::SEEN, 105 | 'class' => \Viber\Api\Event\Seen::class, 106 | 'sample' => [ 107 | "event" => "seen", 108 | "timestamp" => 1457764197627, 109 | "message_token" => 4912661846655238145, 110 | "user_id" => "01234567890A=" 111 | ] 112 | ], 113 | [ 114 | 'type' => Type::FAILED, 115 | 'class' => \Viber\Api\Event\Failed::class, 116 | 'sample' => [ 117 | "event" => "failed", 118 | "timestamp" => 1457764197627, 119 | "message_token" => 4912661846655238145, 120 | "user_id" => "01234567890A=", 121 | "desc" => "failure description." 122 | ] 123 | ], 124 | [ 125 | 'type' => Type::MESSAGE, 126 | 'class' => \Viber\Api\Event\Message::class, 127 | 'sample' => [ 128 | "event" => "message", 129 | "timestamp" => 1457764197627, 130 | "message_token" => 4912661846655238145, 131 | "sender" => [ 132 | "id" => "01234567890A=", 133 | "name" => "John McClane", 134 | "avatar" => "http://avatar.example.com" 135 | ], 136 | "message" => [ 137 | "type" => "text", 138 | "text" => "a message to the service", 139 | "media" => "http://example.com", 140 | "location" => [ 141 | "lat" => 50.76891, 142 | "lon" => 6.11499 143 | ], 144 | "tracking_data" => "tracking data" 145 | ] 146 | ] 147 | ], 148 | ]; 149 | 150 | foreach ($eventDataList as $dataItem) { 151 | $event = Factory::makeFromApi($dataItem['sample']); 152 | $this->assertInstanceOf($dataItem['class'], $event); 153 | $this->assertEquals($dataItem['sample']['event'], $event->getEventType()); 154 | } 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /test/Viber/Tests/Api/Event/MessageTest.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | class MessageTest extends TestCase 12 | { 13 | public function testConstructor() 14 | { 15 | $event = new MessageEvent([ 16 | "event" => "message", 17 | "timestamp" => 1457764197627, 18 | "message_token" => 4912661846655238145, 19 | "sender" => [ 20 | "id" => "01234567890A=", 21 | "name" => "John McClane", 22 | "avatar" => "http://avatar.example.com" 23 | ], 24 | "message" => [ 25 | "type" => "text", 26 | "text" => "msg", 27 | "media" => "http://example.com", 28 | "location" => [ 29 | "lat" => 50.76891, 30 | "lon" => 6.11499 31 | ], 32 | "tracking_data" => "tracking data" 33 | ] 34 | ]); 35 | $this->assertEquals("msg", $event->getMessage()->getText()); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /test/Viber/Tests/Api/Keyboard/ButtonTest.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | class ButtonTest extends TestCase 11 | { 12 | public function testToArray() 13 | { 14 | $btn = new \Viber\Api\Keyboard\Button(); 15 | $btn 16 | ->setColumns(1) 17 | ->setRows(1) 18 | ->setBgColor('#000') 19 | ->setBgMediaType('picture') 20 | ->setBgMedia('https://some.url') 21 | ->setBgLoop(true) 22 | ->setActionType('reply') 23 | ->setActionBody('btn') 24 | ->setImage('https://some.url') 25 | ->setText('btn text') 26 | ->setTextVAlign('top') 27 | ->setTextHAlign('center') 28 | ->setTextOpacity(50) 29 | ->setTextSize('small'); 30 | 31 | $this->assertEquals([ 32 | 'Columns' => 1, 33 | 'Rows' => 1, 34 | 'BgColor' => '#000', 35 | 'BgMediaType' => 'picture', 36 | 'BgMedia' => 'https://some.url', 37 | 'BgLoop' => true, 38 | 'ActionType' => 'reply', 39 | 'ActionBody' => 'btn', 40 | 'Image' => 'https://some.url', 41 | 'Text' => 'btn text', 42 | 'TextVAlign' => 'top', 43 | 'TextHAlign' => 'center', 44 | 'TextOpacity' => 50, 45 | 'TextSize' => 'small', 46 | ], $btn->toApiArray(), "Equal Button constructor", 0.0, 1,true); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /test/Viber/Tests/Api/Message/FactoryTest.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class FactoryTest extends TestCase 13 | { 14 | /** 15 | * @expectedException \Viber\Api\Exception\ApiException 16 | */ 17 | public function testUnknowType() 18 | { 19 | Factory::makeFromApi(['type' => '-']); 20 | } 21 | 22 | // TODO implement each sample 23 | private function build($type) 24 | { 25 | return [ 26 | "type" => $type, 27 | "text" => "a message to the service", 28 | "media" => "http://example.com", 29 | "location" => [ 30 | "lat" => 50.76891, 31 | "lon" => 6.11499 32 | ], 33 | "tracking_data" => "tracking data", 34 | ]; 35 | } 36 | 37 | public function testMakeFromApi() 38 | { 39 | $testList = [ 40 | Type::TEXT, 41 | Type::PICTURE, 42 | Type::VIDEO, 43 | Type::FILE, 44 | Type::STICKER, 45 | Type::CONTACT, 46 | Type::URL, 47 | Type::LOCATION 48 | ]; 49 | foreach ($testList as $messageType) { 50 | $message = Factory::makeFromApi( $this->build($messageType) ); 51 | $this->assertEquals('Viber\\Api\\Message\\'.ucfirst($messageType), get_class($message)); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /test/Viber/Tests/Api/MessageTest.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | class MessageTest extends TestCase 15 | { 16 | public function testNestedToArray() 17 | { 18 | $message = 19 | (new Message()) 20 | ->setSender(new Sender()) 21 | ->setReceiver('some-user-id') 22 | ->setTrackingData('user-track') 23 | ->setKeyboard( 24 | (new Keyboard()) 25 | ->setButtons([ 26 | (new Button()) 27 | ->setActionType('open-url') 28 | ->setActionBody('https://some.url') 29 | ]) 30 | ); 31 | 32 | $this->assertEquals('open-url', 33 | $message->getKeyboard()->getButtons()[0]->getActionType()); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /test/Viber/Tests/Api/ResponseTest.php: -------------------------------------------------------------------------------- 1 | 12 | */ 13 | class ResponseTest extends TestCase 14 | { 15 | /** 16 | * @expectedException \Viber\Api\Exception\ApiException 17 | * @expectedExceptionMessageRegExp |.*body.*| 18 | */ 19 | public function testEmptyBody() 20 | { 21 | $r = \Viber\Api\Response::create( 22 | new Response(200, [], '') 23 | ); 24 | } 25 | 26 | /** 27 | * @expectedException \Viber\Api\Exception\ApiException 28 | * @expectedExceptionMessageRegExp |Remote error.*| 29 | */ 30 | public function testWhenErrorStatus() 31 | { 32 | $responseData = json_encode([ 33 | 'status' => 1, 34 | ]); 35 | $r = \Viber\Api\Response::create( 36 | new Response(200, [], $responseData) 37 | ); 38 | } 39 | 40 | /** 41 | * @expectedException \Viber\Api\Exception\ApiException 42 | * @expectedExceptionMessageRegExp |.*json.*| 43 | */ 44 | public function testInvalidJson() 45 | { 46 | $responseData = json_encode([ 47 | 'no_status' => 'no_status' 48 | ]); 49 | $r = \Viber\Api\Response::create( 50 | new Response(200, [], $responseData) 51 | ); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /test/Viber/Tests/Api/SenderTest.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | class SenderTest extends TestCase 12 | { 13 | public function testToArray() 14 | { 15 | $properties = [ 16 | 'id' => '1', 17 | 'name' => '2', 18 | 'avatar' => '3' 19 | ]; 20 | $sender = new Sender($properties); 21 | $this->assertEquals($properties, $sender->toArray()); 22 | $this->assertEquals($properties, $sender->toApiArray()); 23 | } 24 | 25 | public function testNullValues() 26 | { 27 | $properties = [ 28 | 'name' => '2' 29 | ]; 30 | $sender = new Sender($properties); 31 | $this->assertEquals($properties, $sender->toApiArray()); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /test/Viber/Tests/Api/SignatureTest.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | class SignatureTest extends TestCase 12 | { 13 | public function testIsValid() 14 | { 15 | $this->assertTrue( 16 | Signature::isValid( 17 | '4703d481ddedca88184497744b52937586bef3b273645082c04529f73b85456e', 18 | '1', 19 | '2' 20 | ) 21 | ); 22 | $this->assertFalse( 23 | Signature::isValid( 24 | '-', 25 | '1', 26 | '2' 27 | ) 28 | ); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /test/Viber/Tests/ApiMock.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | class ApiMock 11 | { 12 | // response array samples 13 | protected $callList = [ 14 | 'set_webhook' => [ 15 | "url" => "https://my.host.com", 16 | "event_types" => ["delivered", "seen", "failed", "subscribed", "unsubscribed", "conversation_started"] 17 | ], 18 | 'get_account_info' => [ 19 | "status" => 0, 20 | "status_message" => "ok", 21 | "id" => "pa:75346594275468546724", 22 | "name" => "account name", 23 | "uri" => "accountUri", 24 | "icon" => "http://example.com", 25 | "background" => "http://example.com", 26 | "category" => "category", 27 | "subcategory" => "sub category", 28 | "location" => [ 29 | "lon" => 0.1, 30 | "lat" => 0.2 31 | ], 32 | "country" => "UK", 33 | "webhook" => "https://my.site.com", 34 | "event_types" => ["delivered", "seen"], 35 | "subscribers_count" => 35, 36 | "members" => [ 37 | [ 38 | "id" => "01234567890A=", 39 | "name" => "my name", 40 | "avatar" => "http://example.com", 41 | "role" => "admin" 42 | ] 43 | ] 44 | ], 45 | 'get_user_details' => [ 46 | "status" => 0, 47 | "status_message" => "ok", 48 | "message_token" => 4912661846655238145, 49 | "user" => [ 50 | "id" => "01234567890A=", 51 | "name" => "John McClane", 52 | "avatar" => "http://avatar.example.com", 53 | "country" => "UK", 54 | "language" => "en", 55 | "primary_device_os" => "android 7.1", 56 | "api_version" => 1, 57 | "viber_version" => "6.5.0", 58 | "mcc" => 1, 59 | "mnc" => 1 60 | ] 61 | ], 62 | 'get_online' => [ 63 | "status" => 0, 64 | "status_message" => "ok", 65 | "users" => [[ 66 | "id" => "01234567890=", 67 | "online_status" => 0, 68 | "online_status_message" => "online" 69 | ], [ 70 | "id" => "01234567891=", 71 | "online_status" => 1, 72 | "online_status_message" => "offline", 73 | "last_online" => 1457764197627 74 | ], [ 75 | "id" => "01234567892=", 76 | "online_status" => 2, 77 | "online_status_message" => "undisclosed" 78 | ], [ 79 | "id" => "01234567893=", 80 | "online_status" => 3, 81 | "online_status_message" => "tryLater" 82 | ], [ 83 | "id" => "01234567894=", 84 | "online_status" => 4, 85 | "online_status_message" => "unavailable" 86 | ]] 87 | ], 88 | 'post' => [ 89 | "from" => "01234567890A=", 90 | "sender" => [ 91 | "name" => "Yarden from the PA", 92 | "avatar" => "http://avatar.example.com" 93 | ], 94 | "type" => "text", 95 | "text" => "a message from pa" 96 | ], 97 | 'send_message' => [ 98 | "receiver" => "01234567890A=", 99 | "min_api_version" => 1, 100 | "sender" => [ 101 | "name" => "John McClane", 102 | "avatar" => "http://avatar.example.com" 103 | ], 104 | "tracking_data" => "tracking data", 105 | "type" => "text", 106 | "text" => "a message from pa" 107 | ] 108 | ]; 109 | 110 | 111 | /** 112 | * Find mock by method name 113 | */ 114 | public static function getMockByName($name) 115 | { 116 | if (isset($this->callList[$name])) { 117 | return $this->callList[$name]; 118 | } 119 | return []; 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /test/Viber/Tests/Bot/ManagerTest.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class ManagerTest extends TestCase 13 | { 14 | public function testChecker() 15 | { 16 | $totalCalls = 0; 17 | $m = new Manager(function($e) use (&$totalCalls) { 18 | $totalCalls++; 19 | return true; 20 | }, function($e) use (&$totalCalls) { 21 | $totalCalls++; 22 | return true; 23 | }); 24 | $m->isMatch(new Event([])); 25 | $this->assertEquals(1, $totalCalls); 26 | } 27 | 28 | public function testHandler() 29 | { 30 | $totalCalls = 0; 31 | $m = new Manager(function($e) use (&$totalCalls) { 32 | $totalCalls++; 33 | return true; 34 | }, function($e) use (&$totalCalls) { 35 | $totalCalls++; 36 | return true; 37 | }); 38 | $m->runHandler(new Event([])); 39 | $this->assertEquals(1, $totalCalls); 40 | } 41 | 42 | public function testIsMatch() 43 | { 44 | $totalCalls = 0; 45 | $m = new Manager(function($e) use (&$totalCalls) { 46 | $totalCalls++; 47 | return false; 48 | }, function($e) use (&$totalCalls) { 49 | $totalCalls++; 50 | return true; 51 | }); 52 | $event = new Event([]); 53 | if ($m->isMatch($event)) { 54 | $m->runHandler($event); 55 | } 56 | $this->assertEquals(1, $totalCalls); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /test/Viber/Tests/BotTest.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | class BotTest extends TestCase 15 | { 16 | /** 17 | * @expectedException \RuntimeException 18 | */ 19 | public function testNoOptions() 20 | { 21 | new Bot([]); 22 | } 23 | 24 | public function testGetClient() 25 | { 26 | $bot = new Bot(['token' => '-']); 27 | $this->assertInstanceOf(\Viber\Client::class, $bot->getClient()); 28 | } 29 | 30 | public function testRegisterHandler() 31 | { 32 | $bot = new Bot(['token' => '-']); 33 | $totalCalls = 0; 34 | $bot 35 | ->on( 36 | function ($e) use (&$totalCalls) { 37 | $totalCalls++; 38 | return true; 39 | }, 40 | function ($e) use (&$totalCalls) { 41 | $totalCalls++; 42 | return true; 43 | } 44 | ) 45 | ->run(new Event([])); 46 | $this->assertEquals(2, $totalCalls); 47 | } 48 | 49 | public function testTextHandler() 50 | { 51 | $bot = new Bot(['token' => '-']); 52 | $totalCalls = 0; 53 | $bot 54 | ->onText( 55 | '|-|s', 56 | function ($e) use (&$totalCalls) { 57 | $totalCalls++; 58 | return true; 59 | } 60 | ) 61 | ->run(new Event([])); 62 | $this->assertEquals(0, $totalCalls); 63 | } 64 | 65 | /** 66 | * @expectedException \RuntimeException 67 | * @expectedExceptionMessageRegExp |.*header not found.*| 68 | */ 69 | public function testRunNoHeader() 70 | { 71 | $bot = new Bot(['token' => '-']); 72 | $bot->run(); 73 | } 74 | 75 | /** 76 | * @expectedException \RuntimeException 77 | * @expectedExceptionMessageRegExp |.*Event.*| 78 | */ 79 | public function testRunInvalidParams() 80 | { 81 | $bot = new Bot(['token' => '-']); 82 | $bot->run('some arg'); 83 | } 84 | 85 | /** 86 | * @expectedException \RuntimeException 87 | * @expectedExceptionMessageRegExp |Invalid signature.*| 88 | */ 89 | public function testInvalidSignature() 90 | { 91 | $stub = $this->getMock( 92 | Bot::class, 93 | ['getInputBody', 'getSignHeaderValue'], 94 | [['token' => '-']] 95 | ); 96 | 97 | $stub->method('getInputBody') 98 | ->willReturn('1'); 99 | $stub->method('getSignHeaderValue') 100 | ->willReturn('2'); 101 | 102 | $stub->run(); 103 | } 104 | 105 | /** 106 | * @expectedException \RuntimeException 107 | * @expectedExceptionMessageRegExp |Invalid json.*| 108 | */ 109 | public function testInvalidBody() 110 | { 111 | $stub = $this->getMock( 112 | Bot::class, 113 | ['getInputBody', 'getSignHeaderValue'], 114 | [['token' => '-']] 115 | ); 116 | 117 | $inputBody = '1'; // valid json 118 | 119 | $stub->method('getInputBody') 120 | ->willReturn($inputBody); 121 | 122 | $stub->method('getSignHeaderValue') 123 | ->willReturn( 124 | Signature::make($inputBody, '-') 125 | ); 126 | 127 | $stub->run(); 128 | } 129 | 130 | /** 131 | * @expectedException \RuntimeException 132 | * @expectedExceptionMessageRegExp |Invalid json.*| 133 | */ 134 | public function testInvalidJsonBody() 135 | { 136 | $stub = $this->getMock( 137 | Bot::class, 138 | ['getInputBody', 'getSignHeaderValue'], 139 | [['token' => '-']] 140 | ); 141 | 142 | $inputBody = '}{'; // invalid json 143 | 144 | $stub->method('getInputBody') 145 | ->willReturn($inputBody); 146 | $stub->method('getSignHeaderValue') 147 | ->willReturn( 148 | Signature::make($inputBody, '-') 149 | ); 150 | 151 | $stub->run(); 152 | } 153 | 154 | /** 155 | * @expectedException \RuntimeException 156 | * @expectedExceptionMessageRegExp |Invalid json.*| 157 | */ 158 | public function testEmptyJsonBody() 159 | { 160 | $stub = $this->getMock( 161 | Bot::class, 162 | ['getInputBody', 'getSignHeaderValue'], 163 | [['token' => '-']] 164 | ); 165 | 166 | $inputBody = '{}'; // empty object 167 | 168 | $stub->method('getInputBody') 169 | ->willReturn($inputBody); 170 | $stub->method('getSignHeaderValue') 171 | ->willReturn( 172 | Signature::make($inputBody, '-') 173 | ); 174 | 175 | $stub->run(); 176 | } 177 | 178 | /** 179 | * @expectedException \Viber\Api\Exception\ApiException 180 | */ 181 | public function testUnknowJsonBody() 182 | { 183 | $stub = $this->getMock( 184 | Bot::class, 185 | ['getInputBody', 'getSignHeaderValue'], 186 | [['token' => '-']] 187 | ); 188 | 189 | $inputBody = '{"event": "-"}'; // unknow event 190 | 191 | $stub->method('getInputBody') 192 | ->willReturn($inputBody); 193 | $stub->method('getSignHeaderValue') 194 | ->willReturn( 195 | Signature::make($inputBody, '-') 196 | ); 197 | 198 | $stub->run(); 199 | } 200 | 201 | public function testOnText() 202 | { 203 | $bot = new Bot(['token' => '-']); 204 | $totalCalls = 0; 205 | $bot 206 | ->onText('|ping .*|s', function ($e) use (&$totalCalls) { 207 | $totalCalls++; 208 | }) 209 | ->onText('|pong .*|s', function ($e) use (&$totalCalls) { 210 | $totalCalls++; 211 | }) 212 | ->run( 213 | new \Viber\Api\Event\Message([ 214 | "event" => "message", 215 | "timestamp" => 1457764197627, 216 | "message_token" => 4912661846655238145, 217 | "sender" => [ 218 | "id" => "01234567890A=", 219 | "name" => "John McClane", 220 | "avatar" => "http://avatar.example.com" 221 | ], 222 | "message" => [ 223 | "type" => "text", 224 | "text" => "ping me", 225 | "media" => "http://example.com", 226 | "location" => [ 227 | "lat" => 50.76891, 228 | "lon" => 6.11499 229 | ], 230 | "tracking_data" => "tracking data" 231 | ] 232 | ]) 233 | ); 234 | $this->assertEquals(1, $totalCalls); 235 | } 236 | 237 | public function testOnPicture() 238 | { 239 | $bot = new Bot(['token' => '-']); 240 | $totalCalls = 0; 241 | $bot 242 | ->onPicture(function ($e) use (&$totalCalls) { 243 | $totalCalls++; 244 | }) 245 | ->run( 246 | new \Viber\Api\Event\Message([ 247 | "event" => "message", 248 | "timestamp" => 1457764197627, 249 | "message_token" => 4912661846655238145, 250 | "sender" => [ 251 | "id" => "01234567890A=", 252 | "name" => "John McClane", 253 | "avatar" => "http://avatar.example.com" 254 | ], 255 | "message" => [ 256 | "type" => "picture", 257 | "text" => "Photo description", 258 | "media" => "http://www.images.com/img.jpg", 259 | "thumbnail" => "http://www.images.com/thumb.jpg", 260 | "tracking_data" => "tracking data" 261 | ] 262 | ]) 263 | ); 264 | $this->assertEquals(1, $totalCalls); 265 | } 266 | 267 | public function testOnConversation() 268 | { 269 | $bot = new Bot(['token' => '-']); 270 | $totalCalls = 0; 271 | $bot 272 | ->onConversation(function ($e) use (&$totalCalls) { 273 | $totalCalls++; 274 | }) 275 | ->run( 276 | new \Viber\Api\Event\Conversation([ 277 | "event" => "conversation_started", 278 | "timestamp" => 1457764197627, 279 | "message_token" => 4912661846655238145, 280 | "type" => "open", 281 | "context" => "context information", 282 | "user" => [ 283 | "id" => "01234567890A=", 284 | "name" => "John McClane", 285 | "avatar" => "http://avatar.example.com", 286 | "country" => "UK", 287 | "language" => "en", 288 | "api_version" => 1 289 | ] 290 | ]) 291 | ); 292 | $this->assertEquals(1, $totalCalls); 293 | } 294 | 295 | public function testConversationReply() 296 | { 297 | $textMessage = new \Viber\Api\Message\Text([ 298 | 'sender' => [ 299 | 'name' => 'hi bot', 300 | 'avatar' => 'https://my.avatar/pict.jpg' 301 | ], 302 | 'receiver' => '01234567890A=', 303 | 'text' => 'Can i help you?', 304 | 'tracking_data' => 'hi-conversation', 305 | ]); 306 | \Viber\Output::reset(); 307 | $this->expectOutputString( 308 | json_encode($textMessage->toApiArray()) 309 | ); 310 | // build bot 311 | (new Bot(['token' => '-'])) 312 | ->onConversation(function ($e) use ($textMessage) { 313 | return $textMessage; 314 | }) 315 | ->run( 316 | new \Viber\Api\Event\Conversation([ 317 | "event" => "conversation_started", 318 | "timestamp" => 1457764197627, 319 | "message_token" => 4912661846655238145, 320 | "type" => "open", 321 | "context" => "context information", 322 | "user" => [ 323 | "id" => "01234567890A=", 324 | "name" => "John McClane", 325 | "avatar" => "http://avatar.example.com", 326 | "country" => "UK", 327 | "language" => "en", 328 | "api_version" => 1 329 | ] 330 | ]) 331 | ); 332 | $this->assertContains('Content-Type: application/json', \Viber\Output::$headers); 333 | } 334 | } 335 | -------------------------------------------------------------------------------- /test/Viber/Tests/ClientTest.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | class ClientTest extends TestCase 17 | { 18 | /** 19 | * @expectedException \Viber\Api\Exception\ApiException 20 | * @expectedExceptionMessageRegExp |^No token .*| 21 | */ 22 | public function testNoToken() 23 | { 24 | new Client([]); 25 | } 26 | 27 | /** 28 | * @expectedException \Viber\Api\Exception\ApiException 29 | * @expectedExceptionMessageRegExp |^Invalid webhook .*| 30 | */ 31 | public function testInvalidHttpHook() 32 | { 33 | (new Client([ 34 | 'token' => 'some-token' 35 | ])) 36 | ->setWebhook('http://some.url'); 37 | } 38 | 39 | /** 40 | * @expectedException \Viber\Api\Exception\ApiException 41 | * @expectedExceptionMessageRegExp |Remote error: ...| 42 | */ 43 | public function testServerError() 44 | { 45 | $responseData = json_encode([ 46 | 'status' => 3, 47 | 'status_message' => '...', 48 | ]); 49 | $handler = HandlerStack::create( 50 | new MockHandler([ 51 | new Response(200, [], $responseData), 52 | ]) 53 | ); 54 | $client = new Client([ 55 | 'token' => 'token', 56 | 'http' => [ 57 | 'handler' => $handler 58 | ] 59 | ]); 60 | $apinInfo = $client->call('get_account_info', []); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /test/Viber/Tests/Functions.php: -------------------------------------------------------------------------------- 1 | 7 | */ 8 | class TestCase extends \PHPUnit_Framework_TestCase 9 | { 10 | } 11 | -------------------------------------------------------------------------------- /test/bootstrap.php: -------------------------------------------------------------------------------- 1 | add('Viber\Tests', __DIR__); 16 | return $loader; 17 | --------------------------------------------------------------------------------