├── .babelrc ├── .gitignore ├── .styleci.yml ├── .travis.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── composer.json ├── config ├── fb-messenger.php └── menu.php ├── docs └── images │ ├── debug-route.gif │ ├── persistent-menu.png │ ├── quick-reply.png │ └── typing.png ├── package.json ├── phpunit.xml ├── public └── fb-messenger │ └── dist.js ├── resources ├── assets │ └── js │ │ ├── bootstrap.js │ │ └── components │ │ ├── App.vue │ │ ├── Panel.vue │ │ └── Status.vue └── views │ └── debug.blade.php ├── scripts ├── api.sh └── sami │ ├── composer.json │ ├── composer.lock │ └── sami.php ├── src ├── Collections │ ├── BaseCollection.php │ ├── ButtonCollection.php │ ├── ElementCollection.php │ ├── QuickReplyCollection.php │ └── ReceiveMessageCollection.php ├── Commands │ ├── BaseCommand.php │ ├── DomainWhitelistingCommand.php │ ├── GetStartButtonCommand.php │ ├── GreetingTextCommand.php │ ├── HomeUrlCommand.php │ ├── MessengerCodeCommand.php │ └── PersistentMenuCommand.php ├── Contracts │ ├── AutoTypingHandler.php │ ├── BaseHandler.php │ ├── Bot.php │ ├── CommandHandler.php │ ├── Debug │ │ ├── Debug.php │ │ └── Handler.php │ ├── DefaultHandler.php │ ├── HandleMessageResponse.php │ ├── HandlerInterface.php │ ├── Messages │ │ ├── Attachment.php │ │ ├── CodeInterface.php │ │ ├── Message.php │ │ ├── MessageInterface.php │ │ ├── ProfileInterface.php │ │ ├── Template.php │ │ └── UserInterface.php │ ├── PostbackHandler.php │ ├── RequestType.php │ └── WebhookHandler.php ├── Controllers │ ├── DebugController.php │ └── WebhookController.php ├── Events │ └── Broadcast.php ├── Exceptions │ ├── DefaultActionInvalidTypeException.php │ ├── ListElementCountException.php │ ├── NotCreateBotException.php │ ├── NotOptionsException.php │ ├── OptionNotComparedException.php │ ├── RequiredArgumentException.php │ ├── UnknownTypeException.php │ └── ValidatorStructureException.php ├── Facades │ └── MessengerMenu.php ├── LaravelFbMessengerServiceProvider.php ├── Messages │ ├── Audio.php │ ├── Button.php │ ├── ButtonTemplate.php │ ├── DomainWhitelisting.php │ ├── Element.php │ ├── File.php │ ├── GenericTemplate.php │ ├── Greeting.php │ ├── HomeUrl.php │ ├── Image.php │ ├── ListElement.php │ ├── ListTemplate.php │ ├── MessengerCode.php │ ├── PersistentMenuMessage.php │ ├── QuickReply.php │ ├── Quickable.php │ ├── ReceiveMessage.php │ ├── Receiver.php │ ├── StartButton.php │ ├── Text.php │ ├── Typing.php │ ├── UrlButton.php │ ├── User.php │ └── Video.php ├── Middleware │ └── RequestReceived.php ├── PersistentMenu │ └── Menu.php ├── Providers │ ├── MenuServiceProvider.php │ └── RouteServiceProvider.php ├── Transformers │ ├── ButtonTransformer.php │ ├── GenericTransformer.php │ ├── ListTransformer.php │ └── StructuredTransformer.php └── routes │ └── web.php ├── tests ├── Collections │ ├── ButtonCollectionTest.php │ ├── ElementCollectionTest.php │ ├── QuickReplyCollectionTest.php │ └── ReceiveMessageCollectionTest.php ├── CommandTrait.php ├── Commands │ ├── DomainWhitelistingCommandTest.php │ ├── GetStartButtonCommandTest.php │ ├── GreetingTextCommandTest.php │ ├── MessengerCodeCommandTest.php │ └── PersistentMenuCommandTest.php ├── Contracts │ ├── BaseHandlerTest.php │ ├── BotTest.php │ ├── CommandHandlerTest.php │ ├── Debug │ │ └── HandlerTest.php │ ├── DefaultHandlerTest.php │ ├── HandleMessageResponseTest.php │ ├── Messages │ │ ├── AttachmentTest.php │ │ └── StructuredTest.php │ ├── RequestTypeTest.php │ └── WebhookHandlerTest.php ├── Controllers │ ├── DebugControllerTest.php │ └── WebhookControllerTest.php ├── Events │ └── BroadcastTest.php ├── Messages │ ├── AudioTest.php │ ├── ButtonTemplateTest.php │ ├── ButtonTest.php │ ├── DomainWhitelistingTest.php │ ├── ElementTest.php │ ├── FileTest.php │ ├── GenericTemplateTest.php │ ├── GreetingTest.php │ ├── ImageTest.php │ ├── ListElementTest.php │ ├── ListTemplateTest.php │ ├── MessengerCodeTest.php │ ├── PersistentMenuMessageTest.php │ ├── QuickReplyTest.php │ ├── QuickableTest.php │ ├── ReceiveMessageTest.php │ ├── ReceiverTest.php │ ├── StartButtonTest.php │ ├── TextTest.php │ ├── UrlButtonTest.php │ ├── UserTest.php │ └── VideoTest.php ├── Middleware │ └── RequestReceivedTest.php ├── PersistentMenu │ └── MenuTest.php ├── Providers │ ├── LaravelFbMessengerServiceProviderTest.php │ └── RouteServiceProviderTest.php ├── TestCase.php ├── Transformers │ ├── ButtonTransformerTest.php │ ├── GenericTransformerTest.php │ └── ListTransformerTest.php ├── bootstrap.php └── stub │ └── menu.php ├── webpack └── webpack.config.js └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015"] 3 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /vendor 3 | /build 4 | /.idea 5 | /_ide_helper.php 6 | /.phpstorm.meta.php 7 | /composer.lock 8 | /scripts/sami/vendor/ 9 | /scripts/sami/build/ 10 | /scripts/sami/cache/ 11 | /scripts/sami/project/ 12 | /scripts/sami/gh-pages/ 13 | .DS_Store 14 | -------------------------------------------------------------------------------- /.styleci.yml: -------------------------------------------------------------------------------- 1 | preset: psr2 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | sudo: false 3 | env: 4 | global: 5 | - secure: >- 6 | h4K+jmSu8/qlyRX17vhZb3HsHfNyYKWvRCa6iYQ8OlBdKWhQBqcqDExd/wcC4uoHoUeehxVMAgIcxGtn3GlFwm2xNAeNxL9XKyLoIq6Oa27drzmXrfHhq/NsKLZN0+0S9ZT/O2oc6AxQNyduWBbwPHnKVZZF8kiFdAbPd/dH52UTwOHmNwlnjWSFT0BIikmc5vJAuxBJlSDb3k9YIRrVg8mxueSofp/ktAxRppg929TDhd5z64bi+C7EqMQXrIpkTVy8+E3vn1CZqPedAz6GjJdx7GRdQruQTuJteDlOACivZ7SP2YCwLBumyJQT/PjK+ZS6UJYXTeJNd7s0WXRcD/wDONGcjNTJKisjoLZ7hjCSCSsX/R5RKJiAPkWbDcRbj9Q7RdctL2nuh7Qwz1xTyMc8cGkhgmYLjoQ+w/Lr/pxNBdpQflLo7WU8jgBhBf590p3aTvvo8FnNUY/PK6OrhYx+5JJEXhMkZWnpRbfb3UCPBq8MRLHVh8l2SNiYRi+4dS8kB+1oYu9VTAbYI44RrZIrX8uQiV5vJxLrNwZaf0pXCGfUmyQPRh/tjNGLzqsi7nF5MtfqfMEPf6Rx8gU+8o1tybVkx4q5SOSj7oEkDG5O3iJWrWEQkN1e5VNsotYrHYp7OU6EBikXTIrxot6ADZjVWaw2oI7D4hW/ik4XnOg= 7 | php: 8 | - 5.6 9 | - 7 10 | - 7.1 11 | install: 12 | - travis_retry composer install --prefer-dist 13 | script: 14 | - vendor/bin/phpunit 15 | after_script: 16 | - travis_retry php vendor/bin/coveralls -v 17 | after_success: 18 | - >- 19 | test $TRAVIS_PULL_REQUEST == "false" && test $TRAVIS_BRANCH == "master" && 20 | test $TRAVIS_PHP_VERSION = "7.1" && bash ./scripts/api.sh 21 | cache: 22 | directories: 23 | - $HOME/.composer/cache 24 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Contributions are **welcome** and will be fully **credited**. 4 | 5 | We accept contributions via Pull Requests on [Github](https://github.com/CasperLaiTW/laravel-fb-messenger). 6 | 7 | 8 | ## Pull Requests 9 | 10 | - **[PSR-2 Coding Standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)** - The easiest way to apply the conventions is to install [PHP Code Sniffer](http://pear.php.net/package/PHP_CodeSniffer). 11 | 12 | - **Add tests!** - Your patch won't be accepted if it doesn't have tests. 13 | 14 | - **Document any change in behaviour** - Make sure the `README.md` and any other relevant documentation are kept up-to-date. 15 | 16 | - **Consider our release cycle** - We try to follow [SemVer v2.0.0](http://semver.org/). Randomly breaking public APIs is not an option. 17 | 18 | - **Create feature branches** - Don't ask us to pull from your master branch. 19 | 20 | - **One pull request per feature** - If you want to do more than one thing, send multiple pull requests. 21 | 22 | - **Send coherent history** - Make sure each individual commit in your pull request is meaningful. If you had to make multiple intermediate commits while developing, please [squash them](http://www.git-scm.com/book/en/v2/Git-Tools-Rewriting-History#Changing-Multiple-Commit-Messages) before submitting. 23 | 24 | 25 | ## Running Tests 26 | 27 | ``` bash 28 | $ composer test 29 | ``` 30 | 31 | 32 | **Happy coding**! 33 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Casper Lai 4 | 5 | > Permission is hereby granted, free of charge, to any person obtaining a copy 6 | > of this software and associated documentation files (the "Software"), to deal 7 | > in the Software without restriction, including without limitation the rights 8 | > to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | > copies of the Software, and to permit persons to whom the Software is 10 | > furnished to do so, subject to the following conditions: 11 | > 12 | > The above copyright notice and this permission notice shall be included in 13 | > all copies or substantial portions of the Software. 14 | > 15 | > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | > OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | > THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "casperlaitw/laravel-fb-messenger", 3 | "type": "library", 4 | "description": "A Laravel Package to Integrate Facebook Messenger API", 5 | "keywords": [ 6 | "facebook", 7 | "messenger", 8 | "chatbot", 9 | "laravel" 10 | ], 11 | "homepage": "https://github.com/casperlaitw/laravel-fb-messenger", 12 | "license": "MIT", 13 | "authors": [ 14 | { 15 | "name": "Casper Lai", 16 | "email": "casper.lai@sleepingdesign.com", 17 | "role": "Developer" 18 | } 19 | ], 20 | "require": { 21 | "illuminate/support": "^7.0|^8.0", 22 | "illuminate/routing": "^7.0|^8.0", 23 | "illuminate/console": "^7.0|^8.0", 24 | "illuminate/view": "^7.0|^8.0", 25 | "illuminate/broadcasting": "^7.0|^8.0", 26 | "illuminate/queue": "^7.0|^8.0", 27 | "php": "^7.2.5|^7.3|^8.0", 28 | "guzzlehttp/guzzle": "^6.0|^7.0" 29 | }, 30 | "require-dev": { 31 | "phpunit/phpunit": "~5.0", 32 | "nesbot/carbon": "^1.21|^2.0", 33 | "satooshi/php-coveralls": "^2.0", 34 | "mockery/mockery": "^0.9.5", 35 | "fzaninotto/faker": "^1.6" 36 | }, 37 | "autoload": { 38 | "psr-4": { 39 | "Casperlaitw\\LaravelFbMessenger\\": "src" 40 | } 41 | }, 42 | "autoload-dev": { 43 | "classmap": ["tests/"] 44 | }, 45 | "config": { 46 | "preferred-install": "dist", 47 | "sort-packages": true 48 | }, 49 | "extra": { 50 | "branch-alias": { 51 | "dev-master": "1.4.x-dev" 52 | }, 53 | "laravel": { 54 | "providers": [ 55 | "Casperlaitw\\LaravelFbMessenger\\LaravelFbMessengerServiceProvider" 56 | ], 57 | "aliases": { 58 | "Menu": "Casperlaitw\\LaravelFbMessenger\\Facades\\MessengerMenu" 59 | } 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /config/fb-messenger.php: -------------------------------------------------------------------------------- 1 | env('APP_DEBUG', false), 4 | 'verify_token' => env('MESSENGER_VERIFY_TOKEN'), 5 | 'app_token' => env('MESSENGER_APP_TOKEN'), 6 | 'app_secret' => env('MESSENGER_APP_SECRET'), 7 | 'auto_typing' => true, 8 | 'handlers' => [ 9 | Casperlaitw\LaravelFbMessenger\Contracts\DefaultHandler::class 10 | ], 11 | 'custom_url' => '/webhook', 12 | 'postbacks' => [], 13 | 'home_url' => [ 14 | 'url' => env('MESSENGER_HOME_URL'), 15 | 'webview_share_button' => 'show', 16 | 'in_test' => true, 17 | ], 18 | ]; 19 | -------------------------------------------------------------------------------- /config/menu.php: -------------------------------------------------------------------------------- 1 | 2 | 15 | 16 | 17 | ./tests 18 | 19 | 20 | 21 | 22 | ./src 23 | 24 | ./src/routes/web.php 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /resources/assets/js/bootstrap.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by casperlai on 2016/11/20. 3 | */ 4 | import Vue from 'vue'; 5 | import App from './components/App.vue'; 6 | 7 | new Vue({ 8 | el: '#app', 9 | render: h => h(App), 10 | }); -------------------------------------------------------------------------------- /resources/assets/js/components/App.vue: -------------------------------------------------------------------------------- 1 | 25 | 78 | -------------------------------------------------------------------------------- /resources/assets/js/components/Panel.vue: -------------------------------------------------------------------------------- 1 | 45 | 88 | -------------------------------------------------------------------------------- /resources/assets/js/components/Status.vue: -------------------------------------------------------------------------------- 1 | 4 | 24 | -------------------------------------------------------------------------------- /resources/views/debug.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | 9 | Laravel Facebook Messenger Chatbot Debug Router 10 | 11 | 12 | 13 | 14 |
15 |
16 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /scripts/api.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -o errexit -o nounset 3 | 4 | if [ "$TRAVIS_BRANCH" != "master" ] 5 | then 6 | echo "This commit was made against the $TRAVIS_BRANCH and not the master! No deploy!" 7 | exit 0 8 | fi 9 | 10 | base=$(pwd) 11 | sami=${base}/scripts/sami 12 | default=$(git describe --abbrev=0 --tags) 13 | rev=$(git rev-parse --short HEAD) 14 | 15 | cd ${sami} 16 | composer install 17 | rm -rf ${sami}/build 18 | rm -rf ${sami}/cache 19 | rm -rf ${sami}/project 20 | rm -rf ${sami}/gh-pages 21 | 22 | # compile 23 | git clone https://github.com/CasperLaiTW/laravel-fb-messenger.git ${sami}/project 24 | ${sami}/vendor/bin/sami.php update ${sami}/sami.php 25 | 26 | # copy to gh-pages 27 | git clone "https://$GH_TOKEN@github.com/CasperLaiTW/laravel-fb-messenger.git" ${sami}/gh-pages 28 | cd ${sami}/gh-pages 29 | git config user.name "Travis Auto Deploy" 30 | git config user.email "$GIT_AUTH_EMAIL" 31 | git checkout gh-pages || git checkout --orphan gh-pages 32 | git rm -rf . 33 | 34 | cp -R ${sami}/build/* ${sami}/gh-pages/ 35 | 36 | # Deploy to gh-page 37 | cd ${sami}/gh-pages 38 | echo "" > index.html 39 | git add -A . 40 | git commit -m "rebuild pages at ${rev}" 41 | git push origin gh-pages -q 42 | 43 | # cleanup 44 | rm -rf ${sami}/build 45 | rm -rf ${sami}/cache 46 | rm -rf ${sami}/project 47 | rm -rf ${sami}/gh-pages -------------------------------------------------------------------------------- /scripts/sami/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "require": { 3 | "sami/sami": "^4" 4 | } 5 | } -------------------------------------------------------------------------------- /scripts/sami/sami.php: -------------------------------------------------------------------------------- 1 | files() 17 | ->name('*.php') 18 | ->in($dir = __DIR__.'/project/src'); 19 | 20 | $versions = GitVersionCollection::create($dir) 21 | ->addFromTags('v1.6.*') 22 | ->add('1.3', '1.3') 23 | ->add('1.2', '1.2') 24 | ->add('1.1', '1.1'); 25 | 26 | return new Sami($iterator, array( 27 | 'title' => 'Laravel Facebook Messenger API', 28 | 'versions' => $versions, 29 | 'build_dir' => __DIR__.'/build/%version%', 30 | 'cache_dir' => __DIR__.'/cache/%version%', 31 | 'default_opened_level' => 2, 32 | 'remote_repository' => new GitHubRemoteRepository('CasperLaiTW/laravel-fb-messenger', dirname($dir)), 33 | )); 34 | -------------------------------------------------------------------------------- /src/Collections/BaseCollection.php: -------------------------------------------------------------------------------- 1 | elements; 31 | } 32 | 33 | /** 34 | * Add item to collection 35 | * 36 | * @param $element 37 | */ 38 | public function add($element) 39 | { 40 | if ($this->validator($element)) { 41 | $this->elements[] = $element; 42 | } 43 | } 44 | 45 | /** 46 | * Get all elements array data 47 | * 48 | * @return array 49 | */ 50 | public function toData() 51 | { 52 | $data = []; 53 | foreach ($this->elements as $element) { 54 | $data[] = $element->toData(); 55 | } 56 | 57 | return $data; 58 | } 59 | 60 | /** 61 | * Collection is empty 62 | * 63 | * @return bool 64 | */ 65 | public function isEmpty() 66 | { 67 | return count($this->elements) === 0; 68 | } 69 | 70 | /** 71 | * Validate collection item 72 | * 73 | * @param $elements 74 | * 75 | * @return bool 76 | */ 77 | abstract public function validator($elements); 78 | } 79 | -------------------------------------------------------------------------------- /src/Collections/ButtonCollection.php: -------------------------------------------------------------------------------- 1 | add($button); 28 | } 29 | } 30 | 31 | /** 32 | * Add postback button 33 | * 34 | * @param $text 35 | * @param $payload 36 | * 37 | * @return ButtonCollection 38 | */ 39 | public function addPostBackButton($text, $payload = '') 40 | { 41 | $this->add(new Button(Button::TYPE_POSTBACK, $text, $payload)); 42 | 43 | return $this; 44 | } 45 | 46 | /** 47 | * Add web url button 48 | * 49 | * @param $text 50 | * @param $url 51 | * 52 | * @return ButtonCollection 53 | */ 54 | public function addWebButton($text, $url) 55 | { 56 | $this->add(new Button(Button::TYPE_WEB, $text, $url)); 57 | 58 | return $this; 59 | } 60 | 61 | /** 62 | * Add account link button 63 | * 64 | * @param $url 65 | * 66 | * @return $this 67 | */ 68 | public function addAccountLinkButton($url) 69 | { 70 | $this->add(new Button(Button::TYPE_ACCOUNT_LINK, null, $url)); 71 | 72 | return $this; 73 | } 74 | 75 | /** 76 | * Add phone call button 77 | * 78 | * @param $title 79 | * @param $phone 80 | * 81 | * @return $this 82 | * @throws \Casperlaitw\LaravelFbMessenger\Exceptions\OnlyUseByItselfException 83 | */ 84 | public function addCallButton($title, $phone) 85 | { 86 | $this->add(new Button(Button::TYPE_CALL, $title, $phone)); 87 | 88 | return $this; 89 | } 90 | 91 | /** 92 | * Add share button 93 | * 94 | * @return $this 95 | */ 96 | public function addShareButton($shareContent = null) 97 | { 98 | $button = new Button(Button::TYPE_SHARE, ''); 99 | if ($shareContent) { 100 | $button->setExtra($shareContent); 101 | } 102 | $this->add($button); 103 | 104 | return $this; 105 | } 106 | 107 | /** 108 | * Valid the added element 109 | * @param $elements 110 | * 111 | * @return bool 112 | * @throws \Casperlaitw\LaravelFbMessenger\Exceptions\ValidatorStructureException 113 | */ 114 | public function validator($elements) 115 | { 116 | if (!$elements instanceof Button) { 117 | throw new ValidatorStructureException( 118 | 'The `button` object should be instance of `\Casperlaitw\LaravelFbMessenger\Messages\Button`' 119 | ); 120 | } 121 | 122 | return true; 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/Collections/ElementCollection.php: -------------------------------------------------------------------------------- 1 | add($element); 28 | } 29 | } 30 | 31 | /** 32 | * Add element 33 | * 34 | * @param $title 35 | * @param $description 36 | * @param string $image 37 | * 38 | * @return Element 39 | * @internal param string $url 40 | * 41 | */ 42 | public function addElement($title, $description, $image = '') 43 | { 44 | $element = new Element($title, $description, $image); 45 | $this->add($element); 46 | 47 | return $element; 48 | } 49 | 50 | /** 51 | * Valid the added element 52 | * 53 | * @param $elements 54 | * 55 | * @return bool 56 | * @throws \Casperlaitw\LaravelFbMessenger\Exceptions\ValidatorStructureException 57 | */ 58 | public function validator($elements) 59 | { 60 | if (!$elements instanceof Element) { 61 | throw new ValidatorStructureException( 62 | 'The `generic` structure item should be instance of `Casperlaitw\LaravelFbMessenger\Messages\Element`' 63 | ); 64 | } 65 | 66 | return true; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/Collections/QuickReplyCollection.php: -------------------------------------------------------------------------------- 1 | getElements()) > 0; 25 | } 26 | 27 | /** 28 | * Validate collection item 29 | * 30 | * @param $element 31 | * 32 | * @return bool 33 | * @throws ValidatorStructureException 34 | */ 35 | public function validator($element) 36 | { 37 | if (!$element instanceof QuickReply) { 38 | throw new ValidatorStructureException( 39 | 'The `element` object should be instance of `\Casperlaitw\LaravelFbMessenger\Messages\QuickReply`' 40 | ); 41 | } 42 | 43 | return true; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Collections/ReceiveMessageCollection.php: -------------------------------------------------------------------------------- 1 | filter(function (ReceiveMessage $message) { 27 | return !$message->isSkip(); 28 | }); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Commands/BaseCommand.php: -------------------------------------------------------------------------------- 1 | handler = $handler->createBot( 35 | $config->get('fb-messenger.app_token'), 36 | $config->get('fb-messenger.app_secret') 37 | ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Commands/DomainWhitelistingCommand.php: -------------------------------------------------------------------------------- 1 | option('read')) { 41 | $this->read(); 42 | return; 43 | } 44 | 45 | $this->addOrRemove(); 46 | } 47 | 48 | /** 49 | * Read domains 50 | */ 51 | private function read() 52 | { 53 | $command = new DomainWhitelisting(); 54 | $command->setAction(DomainWhitelisting::TYPE_READ)->useGet(); 55 | $response = collect(Arr::get($this->handler->send($command)->getResponse(), 'data.0.whitelisted_domains', [])) 56 | ->map(function ($item) { 57 | return [$item]; 58 | }); 59 | 60 | $headers = ['Domains']; 61 | 62 | $this->table($headers, $response); 63 | } 64 | 65 | /** 66 | * 67 | */ 68 | private function addOrRemove() 69 | { 70 | $domains = $this->option('domain'); 71 | 72 | if (count($domains) > 10) { 73 | $this->error('Domains max: 10 items'); 74 | return; 75 | } 76 | 77 | $command = new DomainWhitelisting($domains); 78 | 79 | if ($this->option('delete')) { 80 | $command->setAction(DomainWhitelisting::TYPE_DELETE)->useDelete(); 81 | } 82 | 83 | $this->comment($this->handler->send($command)->getResponse()); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/Commands/GetStartButtonCommand.php: -------------------------------------------------------------------------------- 1 | argument('payload'); 40 | $deleteOption = $this->option('delete'); 41 | 42 | if (!$deleteOption && empty($payload)) { 43 | $this->error('If you want to add start button, please input the payload'); 44 | return; 45 | } 46 | 47 | $startButton = new StartButton($payload); 48 | if ($deleteOption) { 49 | $startButton->useDelete(); 50 | } 51 | 52 | $this->comment($this->handler->send($startButton)->getResponse()); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/Commands/GreetingTextCommand.php: -------------------------------------------------------------------------------- 1 | option('greeting'); 39 | $locales = $this->option('locale'); 40 | 41 | if (count($texts) === 0) { 42 | $this->error('Please input greeting'); 43 | return; 44 | } 45 | 46 | if (count($locales) === 0) { 47 | $greetings[] = [ 48 | 'locale' => 'default', 49 | 'text' => $texts[0], 50 | ]; 51 | } else { 52 | foreach ($texts as $key => $text) { 53 | $greetings[] = [ 54 | 'locale' => $locales[$key], 55 | 'text' => $text, 56 | ]; 57 | } 58 | } 59 | 60 | $greeting = new Greeting($greetings); 61 | $this->comment($this->handler->send($greeting)->getResponse()); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/Commands/HomeUrlCommand.php: -------------------------------------------------------------------------------- 1 | option('read')) { 39 | $this->read(); 40 | return; 41 | } 42 | 43 | $this->addOrRemove(); 44 | } 45 | 46 | /** 47 | * Read domains 48 | */ 49 | private function read() 50 | { 51 | $command = new HomeUrl(); 52 | $command->setAction(HomeUrl::TYPE_READ)->useGet(); 53 | $response = collect(Arr::get($this->handler->send($command)->getResponse(), 'data.0.home_url', [])) 54 | ->map(function ($item) { 55 | return [$item]; 56 | }); 57 | 58 | $headers = ['Home Url']; 59 | 60 | $this->table($headers, $response); 61 | } 62 | 63 | /** 64 | * 65 | * @throws \Casperlaitw\LaravelFbMessenger\Exceptions\NotCreateBotException 66 | */ 67 | private function addOrRemove() 68 | { 69 | $command = new HomeUrl(config('fb-messenger.home_url', [])); 70 | 71 | if ($this->option('delete')) { 72 | $command->setAction(HomeUrl::TYPE_DELETE)->useDelete(); 73 | } 74 | 75 | $this->comment($this->handler->send($command)->getResponse()); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/Commands/MessengerCodeCommand.php: -------------------------------------------------------------------------------- 1 | option('size')) { 32 | $message->setImageSize($size); 33 | } 34 | 35 | if ($ref = $this->option('ref')) { 36 | $message->setRef($ref); 37 | } 38 | 39 | $this->comment(Arr::get($this->handler->send($message), 'uri')); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Commands/PersistentMenuCommand.php: -------------------------------------------------------------------------------- 1 | option('delete')) { 46 | $persistentMenuMessage->useDelete(); 47 | } 48 | 49 | if ($this->option('read')) { 50 | $persistentMenuMessage->useGet(); 51 | } 52 | 53 | if ($persistentMenuMessage->getCurlType() === Bot::TYPE_POST && MessengerMenu::isEmpty()) { 54 | $this->warn('Menu tree is empty.'); 55 | return; 56 | } 57 | 58 | $response = $this->handler->send($persistentMenuMessage)->getResponse(); 59 | 60 | $this->comment($this->option('read') ? json_encode($response) : $response); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/Contracts/AutoTypingHandler.php: -------------------------------------------------------------------------------- 1 | getSender()); 30 | $this->send($typing); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Contracts/BaseHandler.php: -------------------------------------------------------------------------------- 1 | bot = new Bot($token); 36 | $this->bot->setSecret($secret); 37 | 38 | return $this; 39 | } 40 | 41 | /** 42 | * @param $debug 43 | * @return $this 44 | */ 45 | public function debug($debug) 46 | { 47 | $this->bot->setDebug($debug); 48 | 49 | return $this; 50 | } 51 | 52 | /** 53 | * Send message to api 54 | * 55 | * @param Message $message 56 | * 57 | * @return HandleMessageResponse|array 58 | * @throws \Casperlaitw\LaravelFbMessenger\Exceptions\NotCreateBotException 59 | */ 60 | public function send(Message $message) 61 | { 62 | if ($this->bot === null) { 63 | throw new NotCreateBotException; 64 | } 65 | $arguments = [$message]; 66 | if (in_array(RequestType::class, class_uses($message))) { 67 | $arguments[] = $message->getCurlType(); 68 | } 69 | 70 | return call_user_func_array([$this->bot, 'send'], $arguments); 71 | } 72 | 73 | /** 74 | * Handle the chatbot message 75 | * 76 | * @param ReceiveMessage $message 77 | * 78 | * @return mixed 79 | */ 80 | abstract public function handle(ReceiveMessage $message); 81 | } 82 | -------------------------------------------------------------------------------- /src/Contracts/CommandHandler.php: -------------------------------------------------------------------------------- 1 | dispatcher = $dispatcher; 58 | } 59 | 60 | /** 61 | * @param $webhook 62 | * 63 | * @return $this 64 | */ 65 | public function setWebhook($webhook) 66 | { 67 | $this->webhook = $webhook; 68 | $this->id = Carbon::now()->toDateTimeString(); 69 | 70 | return $this; 71 | } 72 | 73 | /** 74 | * @param $request 75 | * 76 | * @return $this 77 | */ 78 | public function setRequest($request) 79 | { 80 | $this->request = $request; 81 | 82 | return $this; 83 | } 84 | 85 | /** 86 | * @param $response 87 | * 88 | * @return $this 89 | */ 90 | public function setResponse($response) 91 | { 92 | $this->response = $response; 93 | 94 | return $this; 95 | } 96 | 97 | /** 98 | * @return string 99 | */ 100 | public function getResponse() 101 | { 102 | return $this->response; 103 | } 104 | 105 | /** 106 | * @param $status 107 | * 108 | * @return $this 109 | */ 110 | public function setStatus($status) 111 | { 112 | $this->status = $status; 113 | 114 | return $this; 115 | } 116 | 117 | /** 118 | * @return int 119 | */ 120 | public function getStatus() 121 | { 122 | return $this->status; 123 | } 124 | 125 | /** 126 | * Set error message. 127 | * 128 | * @param $error 129 | * 130 | * @return $this 131 | */ 132 | public function setError($error) 133 | { 134 | $this->response = $error; 135 | $this->status = 500; 136 | 137 | return $this; 138 | } 139 | 140 | /** 141 | * 142 | */ 143 | public function broadcast() 144 | { 145 | $this->dispatcher->dispatch( 146 | new Broadcast($this->id, $this->webhook, $this->request, $this->response, $this->status) 147 | ); 148 | 149 | $this->webhook = $this->request = $this->response = $this->status = null; 150 | } 151 | 152 | /** 153 | * Clear all. 154 | */ 155 | public function clear() 156 | { 157 | $this->webhook = $this->request = $this->response = $this->status = $this->id = null; 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /src/Contracts/Debug/Handler.php: -------------------------------------------------------------------------------- 1 | exceptionHandler = $exceptionHandler; 34 | $this->debug = $debug; 35 | } 36 | 37 | /** 38 | * Report or log an exception. 39 | * 40 | * @param Throwable $e 41 | * 42 | * @return void 43 | * @throws Throwable 44 | */ 45 | public function report(Throwable $e) 46 | { 47 | if ($this->exceptionHandler !== null) { 48 | $this->exceptionHandler->report($e); 49 | } 50 | } 51 | 52 | /** 53 | * Render an exception into an HTTP response. 54 | * 55 | * @param \Illuminate\Http\Request $request 56 | * @param Throwable $e 57 | * @return \Symfony\Component\HttpFoundation\Response 58 | * @throws \UnexpectedValueException 59 | */ 60 | public function render($request, Throwable $e) 61 | { 62 | $errors = [ 63 | 'message' => $e->getMessage(), 64 | 'trace' => collect($e->getTrace())->map(function ($item) { 65 | return [ 66 | 'file' => Arr::get($item, 'file'), 67 | 'line' => Arr::get($item, 'line'), 68 | 'method' => Arr::get($item, 'function'), 69 | ]; 70 | })->toArray(), 71 | ]; 72 | $this->debug->setError($errors)->broadcast(); 73 | 74 | return $this->exceptionHandler->render($request, $e); 75 | } 76 | 77 | /** 78 | * Render an exception to the console. 79 | * 80 | * @param \Symfony\Component\Console\Output\OutputInterface $output 81 | * @param Throwable $e 82 | * @return void 83 | */ 84 | public function renderForConsole($output, Throwable $e) 85 | { 86 | if ($this->exceptionHandler !== null) { 87 | $this->exceptionHandler->renderForConsole($output, $e); 88 | } 89 | } 90 | 91 | /** 92 | * Determine if the exception should be reported. 93 | * 94 | * @param Throwable $e 95 | * 96 | * @return bool 97 | */ 98 | public function shouldReport(Throwable $e) 99 | { 100 | return true; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/Contracts/DefaultHandler.php: -------------------------------------------------------------------------------- 1 | send(new Text($message->getSender(), "Default Handler: {$message->getMessage()}")); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Contracts/HandleMessageResponse.php: -------------------------------------------------------------------------------- 1 | response = $response; 31 | } 32 | 33 | /** 34 | * Get API response message 35 | * @return string 36 | */ 37 | public function getResponse() 38 | { 39 | if (!empty($this->response['error'])) { 40 | return $this->handleError($this->response['error']); 41 | } 42 | return Arr::get($this->response, 'result', $this->response); 43 | } 44 | 45 | /** 46 | * Get error message 47 | * @param $error 48 | * 49 | * @return string 50 | */ 51 | private function handleError($error) 52 | { 53 | return $error['message']; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/Contracts/HandlerInterface.php: -------------------------------------------------------------------------------- 1 | type = $type; 69 | $this->setPayload($payload); 70 | } 71 | 72 | /** 73 | * Get payload 74 | * 75 | * @return array 76 | */ 77 | public function getPayload() 78 | { 79 | return $this->payload; 80 | } 81 | 82 | /** 83 | * Set Payload 84 | * 85 | * @param array $payload 86 | * 87 | * @return $this 88 | */ 89 | public function setPayload(array $payload) 90 | { 91 | $this->payload = $payload; 92 | return $this; 93 | } 94 | 95 | /** 96 | * Enable reuse 97 | * 98 | * @return $this 99 | */ 100 | public function enableReuse() 101 | { 102 | $this->payload['is_reusable'] = true; 103 | return $this; 104 | } 105 | 106 | /** 107 | * Disable reuse 108 | * 109 | * @return $this 110 | */ 111 | public function disableReuse() 112 | { 113 | unset($this->payload['is_reusable']); 114 | return $this; 115 | } 116 | 117 | /** 118 | * Set attachment id 119 | * 120 | * @param $id 121 | * 122 | * @return $this 123 | */ 124 | public function setAttachmentId($id) 125 | { 126 | $this->payload['attachment_id'] = $id; 127 | unset($this->payload['url'], $this->payload['is_reusable']); 128 | 129 | return $this; 130 | } 131 | 132 | /** 133 | * To array for send api 134 | * 135 | * @return array 136 | */ 137 | public function toData() 138 | { 139 | return [ 140 | 'recipient' => [ 141 | 'id' => $this->getSender() 142 | ], 143 | 'message' => [ 144 | 'attachment' => [ 145 | 'type' => $this->type, 146 | 'payload' => $this->getPayload(), 147 | ], 148 | ], 149 | ]; 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /src/Contracts/Messages/CodeInterface.php: -------------------------------------------------------------------------------- 1 | sender = $sender; 29 | } 30 | 31 | /** 32 | * Get sender id 33 | * @return mixed 34 | */ 35 | public function getSender() 36 | { 37 | return $this->sender; 38 | } 39 | 40 | /** 41 | * To array for send api 42 | * @return array 43 | */ 44 | abstract public function toData(); 45 | } 46 | -------------------------------------------------------------------------------- /src/Contracts/Messages/MessageInterface.php: -------------------------------------------------------------------------------- 1 | collections = $app->make($this->collection()); 44 | $this->bootQuick(); 45 | } 46 | 47 | 48 | /** 49 | * Add elements 50 | * @param $elements 51 | * 52 | * @return $this 53 | */ 54 | public function add($elements) 55 | { 56 | if (is_array($elements)) { 57 | foreach ($elements as $element) { 58 | $this->add($element); 59 | } 60 | } else { 61 | $this->collections->add($elements); 62 | } 63 | 64 | return $this; 65 | } 66 | 67 | /** 68 | * @param $name 69 | * @param $arguments 70 | * 71 | * @return mixed 72 | */ 73 | public function __call($name, $arguments) 74 | { 75 | if (method_exists($this->collections, $name)) { 76 | return call_user_func_array([$this->collections, $name], $arguments); 77 | } 78 | } 79 | 80 | /** 81 | * Get collection 82 | * 83 | * @return BaseCollection 84 | */ 85 | public function getCollections() 86 | { 87 | return $this->collections; 88 | } 89 | 90 | /** 91 | * To array for send api 92 | * 93 | * @return array 94 | */ 95 | public function toData() 96 | { 97 | if (!$this->share) { 98 | $this->setPayload(array_merge($this->getPayload(), [ 99 | 'sharable' => false, 100 | ])); 101 | } 102 | return $this->makeQuickReply(parent::toData()); 103 | } 104 | 105 | /** 106 | * Disable share 107 | * 108 | * @return $this 109 | */ 110 | public function disableShare() 111 | { 112 | $this->share = false; 113 | 114 | return $this; 115 | } 116 | 117 | /** 118 | * Collection class name 119 | * 120 | * @return mixed 121 | */ 122 | abstract protected function collection(); 123 | } 124 | -------------------------------------------------------------------------------- /src/Contracts/Messages/UserInterface.php: -------------------------------------------------------------------------------- 1 | payload; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Contracts/RequestType.php: -------------------------------------------------------------------------------- 1 | type = Bot::TYPE_POST; 32 | 33 | return $this; 34 | } 35 | 36 | /** 37 | * Use get type 38 | * 39 | * @return $this 40 | */ 41 | public function useGet() 42 | { 43 | $this->type = Bot::TYPE_GET; 44 | 45 | return $this; 46 | } 47 | 48 | /** 49 | * Use delete type 50 | * 51 | * @return $this 52 | */ 53 | public function useDelete() 54 | { 55 | $this->type = Bot::TYPE_DELETE; 56 | 57 | return $this; 58 | } 59 | 60 | /** 61 | * Get request type 62 | * 63 | * @return string 64 | */ 65 | public function getCurlType() 66 | { 67 | return $this->type; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/Controllers/DebugController.php: -------------------------------------------------------------------------------- 1 | make('laravel-fb-messenger::debug'); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Controllers/WebhookController.php: -------------------------------------------------------------------------------- 1 | config = $config; 45 | $this->debug = $debug; 46 | if ($this->config->get('fb-messenger.debug')) { 47 | $this->middleware(RequestReceived::class); 48 | } 49 | } 50 | 51 | /** 52 | * Webhook verify request 53 | * @param Request $request 54 | * 55 | * @return \Illuminate\Contracts\Routing\ResponseFactory|\Symfony\Component\HttpFoundation\Response|void 56 | * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException 57 | * @throws \InvalidArgumentException 58 | */ 59 | public function index(Request $request) 60 | { 61 | if ($request->get('hub_mode') === 'subscribe' 62 | && $request->get('hub_verify_token') === $this->config->get('fb-messenger.verify_token')) { 63 | return new Response($request->get('hub_challenge')); 64 | } 65 | 66 | throw new NotFoundHttpException('Not found resources'); 67 | } 68 | 69 | /** 70 | * Receive the webhook request 71 | * 72 | * @param Request $request 73 | */ 74 | public function receive(Request $request) 75 | { 76 | $receive = new Receiver($request); 77 | $webhook = new WebhookHandler($receive->getMessages(), $this->config, $this->debug); 78 | $webhook->handle(); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/Events/Broadcast.php: -------------------------------------------------------------------------------- 1 | request = $request; 51 | $this->response = $response; 52 | $this->status = $status; 53 | $this->webhook = $webhook; 54 | $this->id = $id; 55 | } 56 | 57 | /** 58 | * Get the data to broadcast. 59 | * 60 | * @return array 61 | */ 62 | public function broadcastWith() 63 | { 64 | return [ 65 | 'id' => $this->id, 66 | 'webhook' => $this->webhook, 67 | 'request' => $this->request, 68 | 'response' => $this->response, 69 | 'status' => $this->status, 70 | ]; 71 | } 72 | 73 | /** 74 | * Get the channels the event should broadcast on. 75 | * 76 | * @return array 77 | */ 78 | public function broadcastOn() 79 | { 80 | return ['laravel-fb-messenger']; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/Exceptions/DefaultActionInvalidTypeException.php: -------------------------------------------------------------------------------- 1 | publishes([ 45 | $this->configPath => $this->app->configPath().'/fb-messenger.php', 46 | ], 'config'); 47 | 48 | $this->publishes([ 49 | $this->menuPath => $this->app->basePath().'/routes/menu.php', 50 | ], 'menu'); 51 | 52 | $this->loadViewsFrom(__DIR__ . '/../resources/views', 'laravel-fb-messenger'); 53 | $this->publishes([__DIR__.'/../public' => $this->app->basePath().'/public/vendor'], 'public'); 54 | 55 | if ($this->app['config']->get('fb-messenger.debug')) { 56 | $this->app->extend(ExceptionHandler::class, function ($exceptionHandler, $app) { 57 | $debug = $app->make(Debug::class); 58 | return new Handler($exceptionHandler, $debug); 59 | }); 60 | } 61 | } 62 | 63 | /** 64 | * Register any package services. 65 | * 66 | * @return void 67 | */ 68 | public function register() 69 | { 70 | $this->mergeConfigFrom($this->configPath, 'fb-messenger'); 71 | $this->app->register(RouteServiceProvider::class); 72 | $this->app->register(MenuServiceProvider::class); 73 | $this->app->singleton(Debug::class, Debug::class); 74 | $this->registerCommands(); 75 | } 76 | 77 | /** 78 | * Register commands 79 | */ 80 | private function registerCommands() 81 | { 82 | $this->commands([ 83 | GreetingTextCommand::class, 84 | GetStartButtonCommand::class, 85 | PersistentMenuCommand::class, 86 | DomainWhitelistingCommand::class, 87 | MessengerCodeCommand::class, 88 | HomeUrlCommand::class, 89 | ]); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/Messages/Audio.php: -------------------------------------------------------------------------------- 1 | $audio]); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Messages/Button.php: -------------------------------------------------------------------------------- 1 | type = $type; 78 | $this->title = $title; 79 | $this->payload = empty($payload) ? $title : $payload; 80 | } 81 | 82 | /** 83 | * To array for send api 84 | * 85 | * @return array 86 | * @throws \Casperlaitw\LaravelFbMessenger\Exceptions\UnknownTypeException 87 | */ 88 | public function toData() 89 | { 90 | switch ($this->type) { 91 | case self::TYPE_SHARE: 92 | return array_merge([ 93 | 'type' => $this->type, 94 | ], $this->extra); 95 | case self::TYPE_ACCOUNT_LINK: 96 | return [ 97 | 'type' => $this->type, 98 | 'url' => $this->payload, 99 | ]; 100 | default: 101 | return [ 102 | 'type' => $this->type, 103 | 'title' => $this->title, 104 | ] + $this->makePayload(); 105 | } 106 | } 107 | 108 | /** 109 | * Make payload by type 110 | * 111 | * @return array 112 | * @throws \Casperlaitw\LaravelFbMessenger\Exceptions\UnknownTypeException 113 | */ 114 | private function makePayload() 115 | { 116 | $payload = []; 117 | switch ($this->type) { 118 | case self::TYPE_POSTBACK: 119 | case self::TYPE_CALL: 120 | $payload = ['payload' => $this->payload]; 121 | break; 122 | case self::TYPE_WEB: 123 | $payload = ['url' => $this->payload]; 124 | break; 125 | default: 126 | throw new UnknownTypeException; 127 | } 128 | 129 | return array_merge($payload, $this->extra); 130 | } 131 | 132 | /** 133 | * Get button type. 134 | * 135 | * @return string 136 | */ 137 | public function getType() 138 | { 139 | return $this->type; 140 | } 141 | 142 | /** 143 | * Set button extra 144 | * 145 | * @param array $value 146 | * 147 | * @return $this 148 | */ 149 | public function setExtra(array $value) 150 | { 151 | $this->extra = $value; 152 | 153 | return $this; 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /src/Messages/ButtonTemplate.php: -------------------------------------------------------------------------------- 1 | add($elements); 36 | $this->text = $text; 37 | } 38 | 39 | 40 | /** 41 | * To array for send api 42 | * 43 | * @return array 44 | * @throws \Casperlaitw\LaravelFbMessenger\Exceptions\RequiredArgumentException 45 | */ 46 | public function toData() 47 | { 48 | $payload = (new ButtonTransformer)->transform($this); 49 | $this->setPayload($payload); 50 | 51 | return parent::toData(); 52 | } 53 | 54 | /** 55 | * Set text 56 | * 57 | * @param string $text 58 | * 59 | * @return ButtonTemplate 60 | */ 61 | public function setText($text) 62 | { 63 | $this->text = $text; 64 | 65 | return $this; 66 | } 67 | 68 | /** 69 | * Get text 70 | * 71 | * @return string 72 | */ 73 | public function getText() 74 | { 75 | return $this->text; 76 | } 77 | 78 | /** 79 | * Get button collection 80 | * 81 | * @return mixed 82 | */ 83 | protected function collection() 84 | { 85 | return ButtonCollection::class; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/Messages/DomainWhitelisting.php: -------------------------------------------------------------------------------- 1 | setDomains($domains); 55 | } 56 | 57 | /** 58 | * Set domain whitelisting action 59 | * 60 | * @param $action 61 | * @return $this 62 | */ 63 | public function setAction($action) 64 | { 65 | $this->action = $action; 66 | 67 | return $this; 68 | } 69 | 70 | /** 71 | * Set domains 72 | * 73 | * @param []|string $domains 74 | * @return $this 75 | */ 76 | public function setDomains($domains) 77 | { 78 | if (is_array($domains)) { 79 | foreach ($domains as $domain) { 80 | $this->setDomains($domain); 81 | } 82 | return $this; 83 | } 84 | 85 | $this->domains[] = $domains; 86 | 87 | return $this; 88 | } 89 | 90 | /** 91 | * To array for send api 92 | * @return array 93 | */ 94 | public function toData() 95 | { 96 | if ($this->action === self::TYPE_READ) { 97 | return [ 98 | 'fields' => 'whitelisted_domains', 99 | ]; 100 | } 101 | 102 | if ($this->action === self::TYPE_DELETE) { 103 | return [ 104 | 'fields' => [ 105 | 'whitelisted_domains', 106 | ], 107 | ]; 108 | } 109 | 110 | return [ 111 | 'whitelisted_domains' => $this->domains, 112 | ]; 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/Messages/Element.php: -------------------------------------------------------------------------------- 1 | title = $title; 64 | $this->description = $description; 65 | $this->image = $image; 66 | $this->buttons = new ButtonCollection; 67 | } 68 | 69 | /** 70 | * Get button collection 71 | * 72 | * @return ButtonCollection 73 | */ 74 | public function buttons() 75 | { 76 | return $this->buttons; 77 | } 78 | 79 | /** 80 | * Set default action button 81 | * 82 | * @param UrlButton $button 83 | * @return $this 84 | * @throws DefaultActionInvalidTypeException 85 | */ 86 | public function setDefaultAction(UrlButton $button) 87 | { 88 | $this->defaultAction = $button; 89 | 90 | return $this; 91 | } 92 | 93 | /** 94 | * To array for send api 95 | * 96 | * @return array 97 | */ 98 | public function toData() 99 | { 100 | $button = $this->buttons()->isEmpty() ? [] : ['buttons' => $this->buttons->toData()]; 101 | 102 | $data = array_merge([ 103 | 'title' => $this->title, 104 | 'subtitle' => $this->description, 105 | 'image_url' => $this->image, 106 | ], $button); 107 | 108 | if ($this->defaultAction) { 109 | $defaultActionData = $this->defaultAction->toData(); 110 | unset($defaultActionData['title']); 111 | $data['default_action'] = $defaultActionData; 112 | } 113 | 114 | return $data; 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/Messages/File.php: -------------------------------------------------------------------------------- 1 | $file]); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Messages/GenericTemplate.php: -------------------------------------------------------------------------------- 1 | add($elements); 44 | } 45 | 46 | /** 47 | * To array for send api 48 | * 49 | * @return array 50 | */ 51 | public function toData() 52 | { 53 | $payload = (new GenericTransformer)->transform($this); 54 | $this->setPayload($payload); 55 | 56 | return parent::toData(); 57 | } 58 | 59 | /** 60 | * Set image aspect ratio 61 | * 62 | * @param $value 63 | * @return $this 64 | */ 65 | public function setImageRatio($value) 66 | { 67 | $this->imageRatio = $value; 68 | 69 | return $this; 70 | } 71 | 72 | /** 73 | * Get image ratio 74 | * @return string 75 | */ 76 | public function getImageRatio() 77 | { 78 | return $this->imageRatio; 79 | } 80 | 81 | /** 82 | * @return mixed 83 | */ 84 | protected function collection() 85 | { 86 | return ElementCollection::class; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/Messages/Greeting.php: -------------------------------------------------------------------------------- 1 | greetings = $greetings; 33 | } 34 | 35 | /** 36 | * Message to send 37 | * 38 | * @return array 39 | */ 40 | public function toData() 41 | { 42 | return [ 43 | 'greeting' => $this->greetings, 44 | ]; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Messages/HomeUrl.php: -------------------------------------------------------------------------------- 1 | config = $config; 47 | } 48 | 49 | 50 | /** 51 | * Set domain whitelisting action 52 | * 53 | * @param $action 54 | * @return $this 55 | */ 56 | public function setAction($action) 57 | { 58 | $this->action = $action; 59 | 60 | return $this; 61 | } 62 | 63 | /** 64 | * To array for send api 65 | * @return array 66 | */ 67 | public function toData() 68 | { 69 | if ($this->action === self::TYPE_READ) { 70 | return [ 71 | 'fields' => 'home_url', 72 | ]; 73 | } 74 | 75 | if ($this->action === self::TYPE_DELETE) { 76 | return [ 77 | 'fields' => [ 78 | 'home_url', 79 | ], 80 | ]; 81 | } 82 | 83 | return [ 84 | 'home_url' => array_merge([ 85 | 'webview_height_ratio' => 'tall', 86 | ], $this->config), 87 | ]; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/Messages/Image.php: -------------------------------------------------------------------------------- 1 | $image]); 29 | $this->bootQuick(); 30 | } 31 | 32 | /** 33 | * To array for send api 34 | * 35 | * @return array 36 | */ 37 | public function toData() 38 | { 39 | return $this->makeQuickReply(parent::toData()); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Messages/ListElement.php: -------------------------------------------------------------------------------- 1 | add($elements); 51 | } 52 | 53 | /** 54 | * @return array 55 | * @throws ListElementCountException 56 | */ 57 | public function toData() 58 | { 59 | $elements = $this->getCollections()->getElements(); 60 | if (count($elements) < 2 || count($elements) > 5) { 61 | throw new ListElementCountException('At least 2 elements and at most 4 elements'); 62 | } 63 | $payload = (new ListTransformer)->transform($this); 64 | $this->setPayload($payload); 65 | 66 | return parent::toData(); 67 | } 68 | 69 | /** 70 | * Collection class name 71 | * 72 | * @return mixed 73 | */ 74 | protected function collection() 75 | { 76 | return ElementCollection::class; 77 | } 78 | 79 | /** 80 | * @return Button 81 | */ 82 | public function getButton() 83 | { 84 | return $this->button; 85 | } 86 | 87 | /** 88 | * @param Button $value 89 | * @return $this 90 | */ 91 | public function setButton(Button $value) 92 | { 93 | $this->button = $value; 94 | 95 | return $this; 96 | } 97 | 98 | /** 99 | * Set top style 100 | * 101 | * @param $style 102 | * 103 | * @return $this 104 | */ 105 | public function setTopStyle($style) 106 | { 107 | $this->topStyle = $style; 108 | 109 | return $this; 110 | } 111 | 112 | /** 113 | * Get top style 114 | * 115 | * @return string 116 | */ 117 | public function getTopStyle() 118 | { 119 | return $this->topStyle; 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/Messages/MessengerCode.php: -------------------------------------------------------------------------------- 1 | imageSize = $value; 41 | 42 | return $this; 43 | } 44 | 45 | /** 46 | * @param $value 47 | * @return $this 48 | */ 49 | public function setRef($value) 50 | { 51 | $this->ref = [ 52 | 'ref' => $value, 53 | ]; 54 | 55 | return $this; 56 | } 57 | 58 | /** 59 | * To array for send api 60 | * @return array 61 | */ 62 | public function toData() 63 | { 64 | $data = [ 65 | 'type' => 'standard', 66 | ]; 67 | 68 | if ($this->imageSize) { 69 | $data['image_size'] = $this->imageSize; 70 | } 71 | 72 | if ($this->ref) { 73 | $data['data'] = $this->ref; 74 | } 75 | 76 | 77 | return $data; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/Messages/PersistentMenuMessage.php: -------------------------------------------------------------------------------- 1 | menus = $menus; 37 | } 38 | 39 | /** 40 | * Message to send 41 | * @return array 42 | */ 43 | public function toData() 44 | { 45 | if ($this->type === Bot::TYPE_DELETE) { 46 | return [ 47 | 'fields' => [ 48 | 'persistent_menu', 49 | ], 50 | ]; 51 | } 52 | 53 | if ($this->type === Bot::TYPE_GET) { 54 | return [ 55 | 'fields' => 'persistent_menu', 56 | ]; 57 | } 58 | 59 | return [ 60 | 'persistent_menu' => $this->menus, 61 | ]; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/Messages/QuickReply.php: -------------------------------------------------------------------------------- 1 | title = $title; 58 | $this->payload = $payload; 59 | $this->type = self::TYPE_TEXT; 60 | } 61 | 62 | /** 63 | * Set location quick reply 64 | * 65 | * @return $this 66 | */ 67 | public function setLocation() 68 | { 69 | $this->type = self::TYPE_LOCATION; 70 | 71 | return $this; 72 | } 73 | 74 | /** 75 | * Set image url 76 | * 77 | * @param $image 78 | * 79 | * @return $this 80 | */ 81 | public function setImage($image) 82 | { 83 | $this->imageUrl = $image; 84 | 85 | return $this; 86 | } 87 | 88 | /** 89 | * To array for send api 90 | * @return array 91 | */ 92 | public function toData() 93 | { 94 | if ($this->type === self::TYPE_LOCATION) { 95 | return ['content_type' => 'location']; 96 | } 97 | 98 | return array_merge([ 99 | 'content_type' => 'text', 100 | 'title' => $this->title, 101 | 'payload' => $this->payload, 102 | ], $this->imageUrl ? ['image_url' => $this->imageUrl] : []); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/Messages/Quickable.php: -------------------------------------------------------------------------------- 1 | quicks = new QuickReplyCollection(); 29 | } 30 | 31 | /** 32 | * @param $quick 33 | * 34 | * @return $this 35 | */ 36 | public function addQuick($quick) 37 | { 38 | $this->quicks->add($quick); 39 | 40 | return $this; 41 | } 42 | 43 | /** 44 | * @return bool 45 | */ 46 | public function isEmpty() 47 | { 48 | return $this->quicks->isEmpty(); 49 | } 50 | 51 | /** 52 | * @param $data 53 | * 54 | * @return array 55 | */ 56 | public function makeQuickReply($data = []) 57 | { 58 | if (!$this->isEmpty()) { 59 | $data['message']['quick_replies'] = $this->quicks->toData(); 60 | } 61 | 62 | return $data; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/Messages/Receiver.php: -------------------------------------------------------------------------------- 1 | messaging = $request->input('entry.0.messaging') ? $request->input('entry.0.messaging') : []; 44 | $this->filterSkip = $filterSkip; 45 | $this->boot(); 46 | } 47 | 48 | /** 49 | * Boot to reorganize messages 50 | */ 51 | private function boot() 52 | { 53 | $messages = []; 54 | foreach ($this->messaging as $message) { 55 | $receiveMessage = new ReceiveMessage(Arr::get($message, 'recipient.id'), Arr::get($message, 'sender.id')); 56 | // is payload 57 | if (Arr::has($message, 'postback.payload') || Arr::has($message, 'message.quick_reply.payload')) { 58 | $receiveMessage 59 | ->setMessage(Arr::get($message, 'message.text')) 60 | ->setReferral(Arr::get($message, 'postback.referral', [])) 61 | ->setPostback(Arr::get( 62 | $message, 63 | 'postback.payload', 64 | Arr::get( 65 | $message, 66 | 'message.quick_reply.payload' 67 | ) 68 | )) 69 | ->setPayload(true); 70 | } else { 71 | $receiveMessage 72 | ->setMessage(Arr::get($message, 'message.text')) 73 | ->setReferral(Arr::get($message, 'referral', [])) 74 | ->setSkip( 75 | Arr::has($message, 'delivery') || 76 | Arr::has($message, 'message.is_echo') || 77 | (!Arr::has($message, 'message.text') && !Arr::has($message, 'message.attachments') && !Arr::has($message, 'referral')) 78 | ) 79 | ->setAttachments(Arr::get($message, 'message.attachments', [])) 80 | ->setNlp(Arr::get($message, 'message.nlp', [])); 81 | } 82 | 83 | $messages[] = $receiveMessage; 84 | } 85 | 86 | $this->collection = new ReceiveMessageCollection($messages); 87 | 88 | if ($this->filterSkip) { 89 | $this->collection = $this->collection->filterSkip(); 90 | } 91 | } 92 | 93 | /** 94 | * Get message collection 95 | * 96 | * @return ReceiveMessageCollection 97 | */ 98 | public function getMessages() 99 | { 100 | return $this->collection; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/Messages/StartButton.php: -------------------------------------------------------------------------------- 1 | payload = $payload; 37 | } 38 | 39 | 40 | /** 41 | * Message to send 42 | * @return array 43 | */ 44 | public function toData() 45 | { 46 | if ($this->getCurlType() === Bot::TYPE_DELETE) { 47 | return [ 48 | 'fields' => [ 49 | 'get_started', 50 | ], 51 | ]; 52 | } 53 | 54 | return [ 55 | 'get_started' => [ 56 | 'payload' => $this->payload, 57 | ], 58 | ]; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Messages/Text.php: -------------------------------------------------------------------------------- 1 | message = $message; 35 | $this->bootQuick(); 36 | } 37 | 38 | /** 39 | * To array for send api 40 | * 41 | * @return array 42 | */ 43 | public function toData() 44 | { 45 | return $this->makeQuickReply([ 46 | 'recipient' => [ 47 | 'id' => $this->getSender(), 48 | ], 49 | 'message' => [ 50 | 'text' => $this->message, 51 | ], 52 | ]); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/Messages/Typing.php: -------------------------------------------------------------------------------- 1 | type = $type; 48 | } 49 | 50 | 51 | /** 52 | * To array for send api 53 | * @return array 54 | */ 55 | public function toData() 56 | { 57 | return [ 58 | 'recipient' => [ 59 | 'id' => $this->getSender(), 60 | ], 61 | 'sender_action' => $this->type, 62 | ]; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/Messages/UrlButton.php: -------------------------------------------------------------------------------- 1 | messenger = true; 79 | 80 | return $this; 81 | } 82 | 83 | /** 84 | * Set webview height ratio 85 | * 86 | * @param $value 87 | * 88 | * @return $this 89 | */ 90 | public function setWebviewHeightRatio($value) 91 | { 92 | $this->ratio = $value; 93 | 94 | return $this; 95 | } 96 | 97 | /** 98 | * Set fallback url 99 | * 100 | * @param string $url 101 | * 102 | * @return $this 103 | */ 104 | public function setFallbackUrl($url) 105 | { 106 | $this->fallback = $url; 107 | 108 | return $this; 109 | } 110 | 111 | /** 112 | * Disable share 113 | * 114 | * @return $this 115 | */ 116 | public function disableShare() 117 | { 118 | $this->share = false; 119 | 120 | return $this; 121 | } 122 | 123 | /** 124 | * To array for send api 125 | * 126 | * @return array 127 | */ 128 | public function toData() 129 | { 130 | $data = []; 131 | if ($this->messenger) { 132 | $data['messenger_extensions'] = true; 133 | } 134 | 135 | if ($this->ratio !== null) { 136 | $data['webview_height_ratio'] = $this->ratio; 137 | } 138 | 139 | if ($this->fallback !== null) { 140 | $data['fallback_url'] = $this->fallback; 141 | } 142 | 143 | if (!$this->share) { 144 | $data['webview_share_button'] = 'hide'; 145 | } 146 | 147 | return array_merge(parent::toData(), $data); 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /src/Messages/User.php: -------------------------------------------------------------------------------- 1 | $video]); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Middleware/RequestReceived.php: -------------------------------------------------------------------------------- 1 | debug = $debug; 31 | } 32 | 33 | /** 34 | * @param $request 35 | * @param $next 36 | * 37 | * @return mixed 38 | */ 39 | public function handle($request, $next) 40 | { 41 | $this->debug->setWebhook($request->all()); 42 | 43 | return $next($request); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/PersistentMenu/Menu.php: -------------------------------------------------------------------------------- 1 | menus; 33 | } 34 | 35 | /** 36 | * Menu is empty 37 | * 38 | * @return bool 39 | */ 40 | public function isEmpty() 41 | { 42 | return empty($this->menus); 43 | } 44 | 45 | /** 46 | * Disable input 47 | */ 48 | public function disableInput() 49 | { 50 | $this->createMenu(['composer_input_disabled' => true]); 51 | } 52 | 53 | /** 54 | * Create postback menu 55 | * 56 | * @param $text 57 | * @param string $payload 58 | * @throws \Casperlaitw\LaravelFbMessenger\Exceptions\UnknownTypeException 59 | */ 60 | public function postback($text, $payload = '') 61 | { 62 | $this->createMenu([ 63 | 'call_to_actions' => [ 64 | (new Button(Button::TYPE_POSTBACK, $text, $payload))->toData(), 65 | ], 66 | ]); 67 | } 68 | 69 | /** 70 | * @param array $args 71 | */ 72 | public function webUrl(...$args) 73 | { 74 | if (count($args) === 1 && $args[0] instanceof UrlButton) { 75 | $this->createMenu([ 76 | 'call_to_actions' => [ 77 | $args[0]->toData(), 78 | ], 79 | ]); 80 | } else { 81 | $this->createMenu([ 82 | 'call_to_actions' => [ 83 | (new UrlButton($args[0], $args[1]))->toData(), 84 | ], 85 | ]); 86 | } 87 | } 88 | 89 | /** 90 | * Nested menu 91 | * 92 | * @param $title 93 | * @param $menus 94 | */ 95 | public function nested($title, $menus) 96 | { 97 | $nested = [ 98 | 'title' => $title, 99 | 'type' => 'nested', 100 | 'call_to_actions' => [], 101 | ]; 102 | $this->stack[] = $nested; 103 | 104 | $this->loadMenus($menus); 105 | $this->createMenu(['call_to_actions' => [array_pop($this->stack)]]); 106 | } 107 | 108 | /** 109 | * Set locale menu 110 | * 111 | * @param $locale 112 | * @param $menus 113 | */ 114 | public function locale($locale, $menus) 115 | { 116 | $this->stack[] = [ 117 | 'locale' => $locale, 118 | ]; 119 | 120 | $this->loadMenus($menus); 121 | 122 | $this->menus[] = array_pop($this->stack); 123 | } 124 | 125 | /** 126 | * Load menu 127 | * 128 | * @param $menus 129 | */ 130 | protected function loadMenus($menus) 131 | { 132 | if ($menus instanceof Closure) { 133 | $menus($this); 134 | } 135 | } 136 | 137 | /** 138 | * Create menu 139 | * 140 | * @param $menu 141 | */ 142 | protected function createMenu($menu) 143 | { 144 | $this->mergeWithLastStack($menu); 145 | } 146 | 147 | /** 148 | * Merge menus to last menu stack 149 | * 150 | * @param $menu 151 | */ 152 | protected function mergeWithLastStack($menu) 153 | { 154 | $stack = array_pop($this->stack); 155 | $this->stack[] = array_merge_recursive($stack, $menu); 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /src/Providers/MenuServiceProvider.php: -------------------------------------------------------------------------------- 1 | map(); 22 | } 23 | 24 | /** 25 | * 26 | */ 27 | public function register() 28 | { 29 | $this->app->singleton('fbMenu', function () { 30 | return new Menu(); 31 | }); 32 | } 33 | 34 | /** 35 | * 36 | */ 37 | public function map() 38 | { 39 | if (file_exists("{$this->app->basePath()}/routes/menu.php")) { 40 | require "{$this->app->basePath()}/routes/menu.php"; 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Providers/RouteServiceProvider.php: -------------------------------------------------------------------------------- 1 | app->routesAreCached()) { 32 | $router->group([ 33 | 'namespace' => $this->namespace, 34 | ], function (Router $router) { 35 | require __DIR__.'/../routes/web.php'; 36 | }); 37 | } 38 | } 39 | 40 | /** 41 | * Stub required to satisfy Laravel 5.1's ServiceProvider 42 | */ 43 | public function register() 44 | { 45 | // 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Transformers/ButtonTransformer.php: -------------------------------------------------------------------------------- 1 | getText())) { 30 | throw new RequiredArgumentException('Text is required'); 31 | } 32 | 33 | return [ 34 | 'template_type' => 'button', 35 | 'text' => $message->getText(), 36 | 'buttons' => $message->getCollections()->toData(), 37 | ]; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Transformers/GenericTransformer.php: -------------------------------------------------------------------------------- 1 | 'generic', 30 | 'image_aspect_ratio' => $message->getImageRatio(), 31 | 'elements' => $message->getCollections()->toData() 32 | ]; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Transformers/ListTransformer.php: -------------------------------------------------------------------------------- 1 | 'list', 31 | 'top_element_style' => $message->getTopStyle(), 32 | 'elements' => $message->getCollections()->toData(), 33 | ], $message->getButton() !== null ? ['buttons' => [$message->getButton()->toData()]] : []); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Transformers/StructuredTransformer.php: -------------------------------------------------------------------------------- 1 | get($this->app['config']->get('fb-messenger.custom_url', '/webhook'), 'WebhookController@index'); 4 | $router->post($this->app['config']->get('fb-messenger.custom_url', '/webhook'), 'WebhookController@receive'); 5 | if ($this->app['config']->get('fb-messenger.debug')) { 6 | $router->get('fb-messenger/debug', 'DebugController@index'); 7 | } 8 | -------------------------------------------------------------------------------- /tests/Collections/ButtonCollectionTest.php: -------------------------------------------------------------------------------- 1 | expectException(ValidatorStructureException::class); 20 | 21 | $collection = new ButtonCollection(); 22 | $collection->validator([]); 23 | } 24 | 25 | public function test_pass_validator() 26 | { 27 | $collection = new ButtonCollection(); 28 | $button = m::mock(Button::class); 29 | $this->assertTrue($collection->validator($button)); 30 | } 31 | 32 | public function test_add_postback_button() 33 | { 34 | $collection = new ButtonCollection(); 35 | $text = 'test'; 36 | $url = 'abc'; 37 | $collection->addPostBackButton('test', 'abc'); 38 | $expected = new Button(Button::TYPE_POSTBACK, $text, $url); 39 | 40 | $this->assertEquals($expected, $collection->getElements()[0]); 41 | } 42 | 43 | public function test_add_web_button() 44 | { 45 | $collection = new ButtonCollection(); 46 | $text = 'test'; 47 | $url = 'abc'; 48 | $collection->addWebButton($text, $url); 49 | 50 | $expected = new Button(Button::TYPE_WEB, $text, $url); 51 | $this->assertEquals($expected, $collection->getElements()[0]); 52 | } 53 | 54 | public function test_add_call_button() 55 | { 56 | $fake = Factory::create(); 57 | $collection = new ButtonCollection(); 58 | $title = Str::random(); 59 | $phone = $fake->phoneNumber; 60 | $collection->addCallButton($title, $phone); 61 | 62 | $expected = new Button(Button::TYPE_CALL, $title, $phone); 63 | $this->assertEquals($expected, $collection->getElements()[0]); 64 | } 65 | 66 | public function test_add_share_button() 67 | { 68 | $collection = new ButtonCollection(); 69 | $collection->addShareButton(); 70 | $expected = new Button(Button::TYPE_SHARE, ''); 71 | 72 | $this->assertEquals($expected, $collection->getElements()[0]); 73 | } 74 | 75 | public function test_add_account_link_button() 76 | { 77 | $url = Str::random(); 78 | $collection = new ButtonCollection(); 79 | $collection->addAccountLinkButton($url); 80 | $expected = new Button(Button::TYPE_ACCOUNT_LINK, null, $url); 81 | 82 | $this->assertEquals($expected, $collection->getElements()[0]); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /tests/Collections/ElementCollectionTest.php: -------------------------------------------------------------------------------- 1 | addElement($title, $description); 21 | 22 | $expected = new Element($title, $description); 23 | 24 | $this->assertEquals($expected, $element->getElements()[0]); 25 | } 26 | 27 | public function test_to_array() 28 | { 29 | $element = new ElementCollection(); 30 | $elementMessages = [ 31 | new Element('title1', 'description1'), 32 | new Element('title2', 'description2'), 33 | ]; 34 | foreach ($elementMessages as $message) { 35 | $element->add($message); 36 | } 37 | foreach ($element->toData() as $key => $value) { 38 | $this->assertEquals($elementMessages[$key]->toData(), $value); 39 | } 40 | } 41 | 42 | public function test_validator_fail() 43 | { 44 | $this->expectException(ValidatorStructureException::class); 45 | 46 | $element = new ElementCollection(); 47 | $element->validator([]); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /tests/Collections/QuickReplyCollectionTest.php: -------------------------------------------------------------------------------- 1 | assertTrue($collection->validator($quickReply)); 20 | } 21 | 22 | public function test_validator_fail() 23 | { 24 | $this->expectException(ValidatorStructureException::class); 25 | 26 | $element = new QuickReplyCollection(); 27 | $element->validator([]); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /tests/Collections/ReceiveMessageCollectionTest.php: -------------------------------------------------------------------------------- 1 | createReceiveMessageMock(true), 18 | $this->createReceiveMessageMock(false), 19 | $this->createReceiveMessageMock(true), 20 | ]; 21 | 22 | $collection = new ReceiveMessageCollection($messages); 23 | 24 | $this->assertCount(3, $collection->toArray()); 25 | 26 | $this->assertCount(1, $collection->filterSkip()->toArray()); 27 | } 28 | 29 | private function createReceiveMessageMock($skip) 30 | { 31 | $mock = m::mock(ReceiveMessage::class)->shouldReceive('isSkip')->andReturn($skip)->getMock(); 32 | return $mock; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /tests/CommandTrait.php: -------------------------------------------------------------------------------- 1 | shouldReceive('get') 22 | ->with('fb-messenger.app_token') 23 | ->andReturn(getenv('MESSENGER_APP_TOKEN')) 24 | ->shouldReceive('get') 25 | ->with('fb-messenger.app_secret') 26 | ->andReturn(getenv('MESSENGER_APP_SECRET')) 27 | ->getMock(); 28 | 29 | $response = m::mock(HandleMessageResponse::class)->makePartial(); 30 | $handler = m::mock(CommandHandler::class.'[send]'); 31 | $handler 32 | ->shouldReceive('send') 33 | ->andReturn($response); 34 | 35 | $commandClass = $this->command(); 36 | $command = new $commandClass($handler, $configMock); 37 | $command->setLaravel($container); 38 | $application->add($command); 39 | 40 | return $application; 41 | } 42 | 43 | private function createCommandTester($command) 44 | { 45 | $artisan = $this->getArtisan(new Container()); 46 | $command = $artisan->find($command); 47 | 48 | return new CommandTester($command); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /tests/Commands/DomainWhitelistingCommandTest.php: -------------------------------------------------------------------------------- 1 | createCommandTester('fb:whitelisting'); 11 | $commandTester->execute([ 12 | '--domain' => [ 13 | 'https://example.com', 14 | 'https://example2.com', 15 | ], 16 | ]); 17 | } 18 | 19 | public function test_read_domains() 20 | { 21 | $commandTester = $this->createCommandTester('fb:whitelisting'); 22 | $commandTester->execute([ 23 | '--read' => true 24 | ]); 25 | } 26 | 27 | public function test_delete_domains() 28 | { 29 | $commandTester = $this->createCommandTester('fb:whitelisting'); 30 | $commandTester->execute([ 31 | '--domain' => 'https://example.com', 32 | '--delete' => true, 33 | ]); 34 | } 35 | 36 | public function test_over_ten_domains() 37 | { 38 | $commandTester = $this->createCommandTester('fb:whitelisting'); 39 | $commandTester->execute([ 40 | '--domain' => [ 41 | 'https://example.com', 42 | 'https://example2.com', 43 | 'https://example3.com', 44 | 'https://example4.com', 45 | 'https://example5.com', 46 | 'https://example6.com', 47 | 'https://example7.com', 48 | 'https://example8.com', 49 | 'https://example9.com', 50 | 'https://example10.com', 51 | 'https://example11.com', 52 | ], 53 | ]); 54 | 55 | $this->assertEquals('Domains max: 10 items'.PHP_EOL, $commandTester->getDisplay()); 56 | } 57 | 58 | private function command() 59 | { 60 | return DomainWhitelistingCommand::class; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /tests/Commands/GetStartButtonCommandTest.php: -------------------------------------------------------------------------------- 1 | createCommandTester('fb:get-start'); 16 | $commandTester->execute([ 17 | 'payload' => 'GET_START', 18 | ]); 19 | } 20 | 21 | public function test_delete_get_start_api() 22 | { 23 | $commandTester = $this->createCommandTester('fb:get-start'); 24 | $commandTester->execute([ 25 | '--delete' => true, 26 | ]); 27 | } 28 | 29 | public function test_empty_payload() 30 | { 31 | $commandTester = $this->createCommandTester('fb:get-start'); 32 | $commandTester->execute([ 33 | ]); 34 | } 35 | 36 | private function command() 37 | { 38 | return GetStartButtonCommand::class; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /tests/Commands/GreetingTextCommandTest.php: -------------------------------------------------------------------------------- 1 | createCommandTester('fb:greeting'); 16 | $commandTester->execute([ 17 | '--locale' => ['default', 'zh_TW'], 18 | '--greeting' => ['Hi', 'Hi, zh_TW'], 19 | ]); 20 | } 21 | 22 | public function test_empty_greeting() 23 | { 24 | $commandTester = $this->createCommandTester('fb:greeting'); 25 | $commandTester->execute([]); 26 | 27 | $this->assertEquals('Please input greeting'.PHP_EOL, $commandTester->getDisplay()); 28 | } 29 | 30 | public function test_default_greeting() 31 | { 32 | $commandTester = $this->createCommandTester('fb:greeting'); 33 | $commandTester->execute([ 34 | '--greeting' => ['Hi'], 35 | ]); 36 | } 37 | 38 | private function command() 39 | { 40 | return GreetingTextCommand::class; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /tests/Commands/MessengerCodeCommandTest.php: -------------------------------------------------------------------------------- 1 | createCommandTester('fb:code'); 12 | $commandTester->execute([ 13 | '--size' => 2000, 14 | '--ref' => 'test-on-set-ref', 15 | ]); 16 | } 17 | 18 | private function command() 19 | { 20 | return MessengerCodeCommand::class; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /tests/Commands/PersistentMenuCommandTest.php: -------------------------------------------------------------------------------- 1 | mockMenu(); 22 | $container = new Container(); 23 | 24 | $container->singleton(MessengerMenu::class, function () use ($menu) { 25 | return $menu; 26 | }); 27 | return $this->getParentArtisan($container); 28 | } 29 | 30 | public function test_delete_api() 31 | { 32 | $commandTester = $this->createCommandTester('fb:menus'); 33 | $commandTester->execute([ 34 | '--delete' => true, 35 | ]); 36 | } 37 | 38 | public function test_setting_api() 39 | { 40 | $commandTester = $this->createCommandTester('fb:menus'); 41 | $commandTester->execute([]); 42 | } 43 | 44 | public function test_read_api() 45 | { 46 | $commandTester = $this->createCommandTester('fb:menus'); 47 | $commandTester->execute([ 48 | '--read' => true, 49 | ]); 50 | } 51 | 52 | private function command() 53 | { 54 | return PersistentMenuCommand::class; 55 | } 56 | 57 | protected function mockMenu() 58 | { 59 | $mock = MessengerMenu::shouldReceive('getMenus') 60 | ->andReturn([]) 61 | ->shouldReceive('isEmpty') 62 | ->andReturn(true) 63 | ->getMock(); 64 | 65 | return $mock; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /tests/Contracts/BaseHandlerTest.php: -------------------------------------------------------------------------------- 1 | shouldReceive('send') 22 | ->withAnyArgs() 23 | ->andReturnNull() 24 | ->getMock(); 25 | $handler = $this->getMockForAbstractClass(BaseHandler::class); 26 | $this->getPrivateProperty(BaseHandler::class, 'bot')->setValue($handler, $bot); 27 | 28 | $message = new MessageStub(null); 29 | $message->useDelete(); 30 | $handler->send($message); 31 | } 32 | 33 | public function test_bot_not_create() 34 | { 35 | $this->expectException(NotCreateBotException::class); 36 | $handler = $this->getMockForAbstractClass(BaseHandler::class); 37 | $message = new MessageStub(null); 38 | $handler->send($message); 39 | } 40 | 41 | public function test_debug() 42 | { 43 | $bot = m::mock(Bot::class) 44 | ->shouldReceive('setDebug') 45 | ->withAnyArgs() 46 | ->andReturnSelf() 47 | ->getMock(); 48 | $handler = $this->getMockForAbstractClass(BaseHandler::class); 49 | $this->getPrivateProperty(BaseHandler::class, 'bot')->setValue($handler, $bot); 50 | $handler->debug(new Dispatcher()); 51 | } 52 | } 53 | 54 | class MessageStub extends Message 55 | { 56 | use RequestType; 57 | 58 | /** 59 | * Message to send object 60 | * @return array 61 | */ 62 | public function toData() 63 | { 64 | // TODO: Implement toData() method. 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /tests/Contracts/BotTest.php: -------------------------------------------------------------------------------- 1 | bot = new Bot(getenv('MESSENGER_APP_TOKEN')); 24 | } 25 | 26 | public function test_send_success() 27 | { 28 | $message = new Greeting(['locale' => 'default', 'text' => Str::random()]); 29 | $this->bot->setSecret('test_app_secret'); 30 | $this->bot->send($message); 31 | } 32 | 33 | public function test_get_user() 34 | { 35 | $user = new User('1282081671850626'); 36 | $response = $this->bot->send($user); 37 | 38 | $this->assertEquals('Casper', $response['first_name']); 39 | } 40 | 41 | public function test_send_array_message() 42 | { 43 | $message = m::mock(Message::class); 44 | $message 45 | ->shouldReceive('toData') 46 | ->andReturn([]); 47 | $this->bot->send($message); 48 | } 49 | 50 | public function test_debug_mode() 51 | { 52 | $dispatch = m::mock(Dispatcher::class); 53 | $dispatch 54 | ->shouldReceive('dispatch') 55 | ->andReturnNull(); 56 | $debug = new Debug($dispatch); 57 | $this->bot->setDebug($debug); 58 | $message = m::mock(Message::class); 59 | $message 60 | ->shouldReceive('toData') 61 | ->andReturn([]); 62 | $this->bot->send($message); 63 | } 64 | 65 | public function test_pusher_connect_fail() 66 | { 67 | $dispatch = m::mock(Dispatcher::class); 68 | $debug = m::mock(Debug::class.'[broadcast]', [$dispatch]); 69 | $debug 70 | ->shouldReceive('broadcast') 71 | ->andReturnUsing(function () { 72 | throw new BroadcastException(); 73 | }); 74 | 75 | $this->bot->setDebug($debug); 76 | $message = m::mock(Message::class); 77 | $message 78 | ->shouldReceive('toData') 79 | ->andReturn([]); 80 | $this->bot->send($message); 81 | } 82 | 83 | public function test_securing_request() 84 | { 85 | $appSecret = 'test_app_secret'; 86 | $expected = hash_hmac('sha256', getenv('MESSENGER_APP_TOKEN'), $appSecret); 87 | $this->bot->setSecret($appSecret); 88 | $actual = $this->getPrivateProperty(Bot::class, 'secret')->getValue($this->bot); 89 | 90 | $this->assertEquals($expected, $actual); 91 | } 92 | 93 | public function test_messenger_code_success() 94 | { 95 | $messenger = new MessengerCode(); 96 | $this->bot->setSecret('test_app_secret'); 97 | $this->bot->send($messenger); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /tests/Contracts/CommandHandlerTest.php: -------------------------------------------------------------------------------- 1 | expectException(Exception::class); 17 | $command = new CommandHandler(); 18 | $command->handle(m::mock(ReceiveMessage::class)->makePartial()); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /tests/Contracts/Debug/HandlerTest.php: -------------------------------------------------------------------------------- 1 | exceptionHandler = m::mock(ExceptionHandler::class); 28 | 29 | $dispatcher = m::mock(Dispatcher::class); 30 | $this->debug = m::mock(Debug::class.'[broadcast]', [$dispatcher]); 31 | $this->debug->shouldReceive('broadcast'); 32 | 33 | $this->handler = new Handler($this->exceptionHandler, $this->debug); 34 | } 35 | 36 | public function test_report() 37 | { 38 | $this->exceptionHandler->shouldReceive('report')->once(); 39 | $this->handler->report(m::mock(Exception::class)); 40 | } 41 | 42 | public function test_renderForConsole() 43 | { 44 | $this->exceptionHandler->shouldReceive('renderForConsole')->once(); 45 | $this->handler->renderForConsole('', m::mock(Exception::class)); 46 | } 47 | 48 | public function test_render() 49 | { 50 | $response = m::mock(Response::class); 51 | $this->exceptionHandler 52 | ->shouldReceive('render') 53 | ->andReturn($response); 54 | 55 | $exception = new Exception('ERROR'); 56 | $this->handler->render(null, $exception); 57 | 58 | $actual = $this->getPrivateProperty(Handler::class, 'debug')->getValue($this->handler); 59 | $this->assertEquals(500, $actual->getStatus()); 60 | $this->assertEquals('ERROR', Arr::get($actual->getResponse(), 'message')); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /tests/Contracts/DefaultHandlerTest.php: -------------------------------------------------------------------------------- 1 | shouldReceive('send') 18 | ->withAnyArgs() 19 | ->andReturnNull() 20 | ->getMock(); 21 | $handler = new DefaultHandler(); 22 | $this->getPrivateProperty(DefaultHandler::class, 'bot')->setValue($handler, $bot); 23 | 24 | $handler->handle(m::mock(ReceiveMessage::class)->makePartial()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tests/Contracts/HandleMessageResponseTest.php: -------------------------------------------------------------------------------- 1 | 'error', 15 | 'error' => [ 16 | 'message' => 'Occur error' 17 | ], 18 | ]; 19 | 20 | $handler = new HandleMessageResponse($response); 21 | 22 | $this->assertEquals('Occur error', $handler->getResponse()); 23 | } 24 | 25 | public function test_get_response() 26 | { 27 | $response = [ 28 | 'result' => 'Pass' 29 | ]; 30 | $handler = new HandleMessageResponse($response); 31 | 32 | $this->assertEquals('Pass', $handler->getResponse()); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /tests/Contracts/Messages/AttachmentTest.php: -------------------------------------------------------------------------------- 1 | faker = Factory::create(); 26 | $sender = Str::random(); 27 | $type = Attachment::TYPE_IMAGE; 28 | $image = $this->faker->url; 29 | $this->attachment = new AttachmentStub($sender, $type, ['url' => $image]); 30 | } 31 | 32 | public function test_set_payload() 33 | { 34 | $expected = [ 35 | 'url' => $this->faker->url, 36 | ]; 37 | $this->attachment->setPayload($expected); 38 | $this->assertEquals($expected, $this->attachment->getPayload()); 39 | } 40 | 41 | public function test_payload_is_not_array() 42 | { 43 | $this->expectException(interface_exists('Throwable') ? Throwable::class : PHPUnit_Framework_Error::class); 44 | $this->attachment->setPayload(''); 45 | } 46 | 47 | public function test_enable_reuse() 48 | { 49 | $this->attachment->enableReuse(); 50 | $this->assertArraySubset(['is_reusable' => true], $this->attachment->getPayload()); 51 | } 52 | 53 | public function test_disable_reuse() 54 | { 55 | $this->attachment->disableReuse(); 56 | $this->assertArrayNotHasKey('is_reusable', $this->attachment->getPayload()); 57 | } 58 | 59 | public function test_set_attachment_id() 60 | { 61 | $id = Str::random(); 62 | $this->attachment->setAttachmentId($id); 63 | 64 | $this->assertEquals(['attachment_id' => $id], $this->attachment->getPayload()); 65 | $this->assertArrayNotHasKey('url', $this->attachment->getPayload()); 66 | } 67 | } 68 | 69 | class AttachmentStub extends Attachment 70 | { 71 | } 72 | -------------------------------------------------------------------------------- /tests/Contracts/Messages/StructuredTest.php: -------------------------------------------------------------------------------- 1 | assertTrue($button->validator($this->getMessageButtonMock())); 19 | } 20 | 21 | public function test_non_collection_method() 22 | { 23 | $button = new ButtonTemplate(Str::random(), Str::random()); 24 | $button->getError(); 25 | } 26 | 27 | private function getMessageButtonMock() 28 | { 29 | return m::mock(Button::class)->makePartial(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /tests/Contracts/RequestTypeTest.php: -------------------------------------------------------------------------------- 1 | getMockForTrait(RequestType::class); 11 | 12 | $mock->usePost(); 13 | 14 | $this->assertEquals(Bot::TYPE_POST, $mock->getCurlType()); 15 | } 16 | 17 | public function test_use_delete() 18 | { 19 | $mock = $this->getMockForTrait(RequestType::class); 20 | 21 | $mock->useDelete(); 22 | 23 | $this->assertEquals(Bot::TYPE_DELETE, $mock->getCurlType()); 24 | } 25 | 26 | public function test_use_get() 27 | { 28 | $mock = $this->getMockForTrait(RequestType::class); 29 | 30 | $mock->useGet(); 31 | 32 | $this->assertEquals(Bot::TYPE_GET, $mock->getCurlType()); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /tests/Controllers/DebugControllerTest.php: -------------------------------------------------------------------------------- 1 | shouldReceive('make')->once()->andReturnSelf(); 18 | $controller = new DebugController(); 19 | $controller->index($factory); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tests/Events/BroadcastTest.php: -------------------------------------------------------------------------------- 1 | id = $this->response = $this->code = $this->webhook = $this->request = Str::random(); 25 | $this->broadcast = new Broadcast($this->id, $this->webhook, $this->request, $this->response, $this->code); 26 | } 27 | 28 | public function test_broadcast_on() 29 | { 30 | $this->assertEquals(['laravel-fb-messenger'], $this->broadcast->broadcastOn()); 31 | } 32 | 33 | public function test_broadcast_with() 34 | { 35 | $this->assertEquals([ 36 | 'id' => $this->id, 37 | 'webhook' => $this->webhook, 38 | 'request' => $this->request, 39 | 'response' => $this->response, 40 | 'status' => $this->code, 41 | ], $this->broadcast->broadcastWith()); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /tests/Messages/AudioTest.php: -------------------------------------------------------------------------------- 1 | url; 19 | 20 | $actual = new Audio($sender, $url); 21 | 22 | $expected = [ 23 | 'recipient' => [ 24 | 'id' => $sender, 25 | ], 26 | 'message' => [ 27 | 'attachment' => [ 28 | 'type' => 'audio', 29 | 'payload' => [ 30 | 'url' => $url, 31 | ], 32 | ], 33 | ], 34 | ]; 35 | 36 | $this->assertEquals($expected, $actual->toData()); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /tests/Messages/ButtonTemplateTest.php: -------------------------------------------------------------------------------- 1 | sender = Str::random(); 24 | $this->text = 'abc'; 25 | $this->case = new ButtonTemplate($this->sender, $this->text); 26 | } 27 | 28 | public function test_to_data() 29 | { 30 | $expected = [ 31 | 'recipient' => [ 32 | 'id' => $this->sender, 33 | ], 34 | 'message' => [ 35 | 'attachment' => [ 36 | 'type' => 'template', 37 | 'payload' => [ 38 | 'template_type' => 'button', 39 | 'text' => $this->text, 40 | 'buttons' => [] 41 | ], 42 | ] 43 | ], 44 | ]; 45 | $this->assertEquals($expected, $this->case->toData()); 46 | } 47 | 48 | public function test_get_text() 49 | { 50 | $this->assertEquals($this->text, $this->case->getText()); 51 | } 52 | 53 | public function test_set_text() 54 | { 55 | $expected = 'change_text'; 56 | $this->case->setText($expected); 57 | 58 | $this->assertEquals($expected, $this->case->getText()); 59 | } 60 | 61 | public function test_disable_share() 62 | { 63 | $expected = [ 64 | 'recipient' => [ 65 | 'id' => $this->sender, 66 | ], 67 | 'message' => [ 68 | 'attachment' => [ 69 | 'type' => 'template', 70 | 'payload' => [ 71 | 'template_type' => 'button', 72 | 'text' => $this->text, 73 | 'buttons' => [], 74 | 'sharable' => false, 75 | ], 76 | ] 77 | ], 78 | ]; 79 | $this->case->disableShare(); 80 | $this->assertEquals($expected, $this->case->toData()); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /tests/Messages/ButtonTest.php: -------------------------------------------------------------------------------- 1 | 'element_share', 17 | ]; 18 | $this->assertEquals($expected, $button->toData()); 19 | } 20 | 21 | public function test_unknown_type_button() 22 | { 23 | $this->expectException(UnknownTypeException::class); 24 | 25 | $button = new Button('unknown', ''); 26 | $button->toData(); 27 | } 28 | 29 | public function test_set_extra() 30 | { 31 | $button = new Button(Button::TYPE_WEB, 'title', 'url'); 32 | $button->setExtra(['extra' => 'test']); 33 | 34 | $expected = [ 35 | 'type' => 'web_url', 36 | 'title' => 'title', 37 | 'url' => 'url', 38 | 'extra' => 'test', 39 | ]; 40 | 41 | $this->assertEquals($expected, $button->toData()); 42 | } 43 | 44 | public function test_get_type() 45 | { 46 | $button = new Button(Button::TYPE_POSTBACK, 'title', 'payload'); 47 | 48 | $this->assertEquals('postback', $button->getType()); 49 | } 50 | 51 | public function test_account_type_button() 52 | { 53 | $url = 'https://www.example.com/authorize'; 54 | $button = new Button(Button::TYPE_ACCOUNT_LINK, null, $url); 55 | $expected = [ 56 | 'type' => 'account_link', 57 | 'url' => $url, 58 | ]; 59 | 60 | $this->assertEquals($expected, $button->toData()); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /tests/Messages/DomainWhitelistingTest.php: -------------------------------------------------------------------------------- 1 | setAction(DomainWhitelisting::TYPE_READ); 19 | 20 | $this->assertArraySubset(['fields' => 'whitelisted_domains'], $message->toData()); 21 | } 22 | 23 | public function test_delete_domains_to_data() 24 | { 25 | $domains = ['https://example.com']; 26 | 27 | $message = new DomainWhitelisting($domains); 28 | $message->setAction(DomainWhitelisting::TYPE_DELETE); 29 | 30 | $this->assertArraySubset([ 31 | 'fields' => [ 32 | 'whitelisted_domains', 33 | ], 34 | ], $message->toData()); 35 | } 36 | 37 | public function test_add_domains_to_data() 38 | { 39 | $domains = ['https://example.com']; 40 | 41 | $message = new DomainWhitelisting($domains); 42 | 43 | $this->assertArraySubset([ 44 | 'whitelisted_domains' => [ 45 | 'https://example.com', 46 | ], 47 | ], $message->toData()); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /tests/Messages/ElementTest.php: -------------------------------------------------------------------------------- 1 | assertInstanceOf(ButtonCollection::class, $element->buttons()); 19 | } 20 | 21 | public function test_default_action() 22 | { 23 | $element = new Element(Str::random(), Str::random()); 24 | $element->setDefaultAction(new UrlButton('title', 'url')); 25 | 26 | $this->assertArraySubset([ 27 | 'default_action' => [ 28 | 'type' => 'web_url', 29 | 'url' => 'url', 30 | ], 31 | ], $element->toData()); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /tests/Messages/FileTest.php: -------------------------------------------------------------------------------- 1 | url; 18 | 19 | $actual = new File($sender, $url); 20 | 21 | $expected = [ 22 | 'recipient' => [ 23 | 'id' => $sender, 24 | ], 25 | 'message' => [ 26 | 'attachment' => [ 27 | 'type' => 'file', 28 | 'payload' => [ 29 | 'url' => $url, 30 | ], 31 | ], 32 | ], 33 | ]; 34 | 35 | $this->assertEquals($expected, $actual->toData()); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /tests/Messages/GenericTemplateTest.php: -------------------------------------------------------------------------------- 1 | sender = Str::random(); 22 | $this->case = [ 23 | new Element('title1', 'description1'), 24 | new Element('title2', 'description2') 25 | ]; 26 | } 27 | 28 | public function test_to_data() 29 | { 30 | $elementExpected = []; 31 | foreach ($this->case as $case) { 32 | $elementExpected[] = $case->toData(); 33 | } 34 | $expected = [ 35 | 'recipient' => [ 36 | 'id' => $this->sender, 37 | ], 38 | 'message' => [ 39 | 'attachment' => [ 40 | 'type' => 'template', 41 | 'payload' => [ 42 | 'template_type' => 'generic', 43 | 'elements' => $elementExpected, 44 | 'image_aspect_ratio' => 'horizontal', 45 | ], 46 | ], 47 | ], 48 | ]; 49 | 50 | $actual = new GenericTemplate($this->sender, $this->case); 51 | 52 | $this->assertEquals($expected, $actual->toData()); 53 | } 54 | 55 | public function test_disable_share() 56 | { 57 | $elementExpected = []; 58 | foreach ($this->case as $case) { 59 | $elementExpected[] = $case->toData(); 60 | } 61 | $expected = [ 62 | 'recipient' => [ 63 | 'id' => $this->sender, 64 | ], 65 | 'message' => [ 66 | 'attachment' => [ 67 | 'type' => 'template', 68 | 'payload' => [ 69 | 'template_type' => 'generic', 70 | 'elements' => $elementExpected, 71 | 'sharable' => false, 72 | 'image_aspect_ratio' => 'horizontal', 73 | ], 74 | ], 75 | ], 76 | ]; 77 | 78 | $actual = new GenericTemplate($this->sender, $this->case); 79 | $actual->disableShare(); 80 | 81 | $this->assertEquals($expected, $actual->toData()); 82 | } 83 | 84 | public function test_image_ratio() 85 | { 86 | $elementExpected = []; 87 | foreach ($this->case as $case) { 88 | $elementExpected[] = $case->toData(); 89 | } 90 | $expected = [ 91 | 'recipient' => [ 92 | 'id' => $this->sender, 93 | ], 94 | 'message' => [ 95 | 'attachment' => [ 96 | 'type' => 'template', 97 | 'payload' => [ 98 | 'template_type' => 'generic', 99 | 'elements' => $elementExpected, 100 | 'image_aspect_ratio' => 'square', 101 | ], 102 | ], 103 | ], 104 | ]; 105 | 106 | $actual = new GenericTemplate($this->sender, $this->case); 107 | $actual->setImageRatio(GenericTemplate::IMAGE_SQUARE); 108 | 109 | $this->assertEquals($expected, $actual->toData()); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /tests/Messages/GreetingTest.php: -------------------------------------------------------------------------------- 1 | [ 17 | [ 18 | 'locale' => 'default', 19 | 'text' => $greetingText, 20 | ], 21 | ], 22 | ]; 23 | 24 | $greeting = new Greeting([['locale' => 'default', 'text' => $greetingText]]); 25 | $this->assertEquals($expected, $greeting->toData()); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /tests/Messages/ImageTest.php: -------------------------------------------------------------------------------- 1 | [ 18 | 'id' => $sender, 19 | ], 20 | 'message' => [ 21 | 'attachment' => [ 22 | 'type' => 'image', 23 | 'payload' => [ 24 | 'url' => $image, 25 | ], 26 | ], 27 | ], 28 | ]; 29 | $this->assertEquals($expected, (new Image($sender, $image))->toData()); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /tests/Messages/ListElementTest.php: -------------------------------------------------------------------------------- 1 | setDefaultAction($button); 19 | 20 | $actual = $this->getPrivateProperty(Element::class, 'defaultAction')->getValue($list); 21 | 22 | $this->assertEquals($button, $actual); 23 | } 24 | 25 | public function test_to_data() 26 | { 27 | $button = new UrlButton('title', 'http://www.google.com'); 28 | 29 | $list = new Element('title', 'description', 'image'); 30 | $list->setDefaultAction($button); 31 | 32 | $expected = [ 33 | 'title' => 'title', 34 | 'subtitle' => 'description', 35 | 'image_url' => 'image', 36 | 'default_action' => [ 37 | 'type' => 'web_url', 38 | 'url' => 'http://www.google.com', 39 | ], 40 | ]; 41 | 42 | $this->assertEquals($expected, $list->toData()); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /tests/Messages/ListTemplateTest.php: -------------------------------------------------------------------------------- 1 | sender = Str::random(); 23 | $this->case = [ 24 | new Element('title1', 'description1', 'image1'), 25 | new Element('title2', 'description2', 'image2'), 26 | ]; 27 | $this->button = new Button(Button::TYPE_POSTBACK, 'GET_MORE'); 28 | } 29 | 30 | public function test_to_data() 31 | { 32 | $elementExpected = []; 33 | foreach ($this->case as $case) { 34 | $elementExpected[] = $case->toData(); 35 | } 36 | 37 | $expected = [ 38 | 'recipient' => [ 39 | 'id' => $this->sender, 40 | ], 41 | 'message' => [ 42 | 'attachment' => [ 43 | 'type' => 'template', 44 | 'payload' => [ 45 | 'template_type' => 'list', 46 | 'top_element_style' => 'compact', 47 | 'elements' => $elementExpected, 48 | 'buttons' => [$this->button->toData()], 49 | ], 50 | ], 51 | ], 52 | ]; 53 | 54 | $actual = new ListTemplate($this->sender, $this->case); 55 | $actual->setTopStyle(ListTemplate::STYLE_COMPACT); 56 | $actual->setButton($this->button); 57 | $this->assertEquals($expected, $actual->toData()); 58 | } 59 | 60 | public function test_to_data_fail() 61 | { 62 | $this->expectException(ListElementCountException::class); 63 | $actual = new ListTemplate($this->sender, []); 64 | $actual->toData(); 65 | } 66 | 67 | public function test_disable_share() 68 | { 69 | $elementExpected = []; 70 | foreach ($this->case as $case) { 71 | $elementExpected[] = $case->toData(); 72 | } 73 | 74 | $expected = [ 75 | 'recipient' => [ 76 | 'id' => $this->sender, 77 | ], 78 | 'message' => [ 79 | 'attachment' => [ 80 | 'type' => 'template', 81 | 'payload' => [ 82 | 'template_type' => 'list', 83 | 'top_element_style' => 'compact', 84 | 'elements' => $elementExpected, 85 | 'buttons' => [$this->button->toData()], 86 | 'sharable' => false, 87 | ], 88 | ], 89 | ], 90 | ]; 91 | 92 | $actual = new ListTemplate($this->sender, $this->case); 93 | $actual->setTopStyle(ListTemplate::STYLE_COMPACT); 94 | $actual->setButton($this->button); 95 | $actual->disableShare(); 96 | $this->assertEquals($expected, $actual->toData()); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /tests/Messages/MessengerCodeTest.php: -------------------------------------------------------------------------------- 1 | setImageSize(2000); 11 | $messengerCode->setRef('test-on-set-ref'); 12 | 13 | $expected = [ 14 | 'type' => 'standard', 15 | 'image_size' => 2000, 16 | 'data' => [ 17 | 'ref' => 'test-on-set-ref', 18 | ], 19 | ]; 20 | 21 | $this->assertEquals($expected, $messengerCode->toData()); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /tests/Messages/PersistentMenuMessageTest.php: -------------------------------------------------------------------------------- 1 | getMenu(); 24 | require __DIR__ . '/../stub/menu.php'; 25 | $persistent = new PersistentMenuMessage($menu->getMenus()); 26 | $expected = [ 27 | 'persistent_menu' => [ 28 | [ 29 | 'locale' => 'default', 30 | 'call_to_actions' => [ 31 | [ 32 | 'type' => 'postback', 33 | 'title' => 'Test Button', 34 | 'payload' => 'TEST_POSTBACK', 35 | ], 36 | [ 37 | 'type' => 'web_url', 38 | 'title' => 'WebUrl', 39 | 'url' => 'https://github.com/CasperLaiTW/laravel-fb-messenger', 40 | ], 41 | [ 42 | 'title' => 'SubMenu', 43 | 'type' => 'nested', 44 | 'call_to_actions' => [ 45 | [ 46 | 'type' => 'postback', 47 | 'title' => 'SubMenu-Button', 48 | 'payload' => 'TEST_SUB_BUTTON', 49 | ], 50 | [ 51 | 'type' => 'web_url', 52 | 'title' => 'SubMenu-WebUrl', 53 | 'url' => 'https://github.com/CasperLaiTW/laravel-fb-messenger', 54 | ], 55 | ], 56 | ], 57 | ], 58 | ], 59 | [ 60 | 'locale' => 'zh_TW', 61 | 'composer_input_disabled' => true, 62 | 'call_to_actions' => [ 63 | [ 64 | 'type' => 'postback', 65 | 'title' => 'zh_TW Test Button', 66 | 'payload' => 'TEST_POSTBACK', 67 | ], 68 | [ 69 | 'type' => 'postback', 70 | 'title' => 'zh_TW Test Button', 71 | 'payload' => 'TEST_POSTBACK', 72 | ], 73 | [ 74 | 'type' => 'postback', 75 | 'title' => 'zh_TW Test Button', 76 | 'payload' => 'TEST_POSTBACK', 77 | ], 78 | ], 79 | ], 80 | ], 81 | ]; 82 | $this->assertEquals($expected, $persistent->toData()); 83 | } 84 | 85 | /** 86 | * @return Menu 87 | */ 88 | protected function getMenu() 89 | { 90 | $container = new Container(); 91 | 92 | $menu = new Menu(); 93 | 94 | $container->singleton(Menu::class, function () use ($menu) { 95 | return $menu; 96 | }); 97 | 98 | return $menu; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /tests/Messages/QuickReplyTest.php: -------------------------------------------------------------------------------- 1 | 'text', 20 | 'title' => $title, 21 | 'payload' => $payload, 22 | ]; 23 | 24 | $this->assertEquals($expected, (new QuickReply($title, $payload))->toData()); 25 | } 26 | 27 | public function test_set_location() 28 | { 29 | $expected = [ 30 | 'content_type' => 'location', 31 | ]; 32 | 33 | $this->assertEquals($expected, (new QuickReply(null, null))->setLocation()->toData()); 34 | } 35 | 36 | public function test_set_image() 37 | { 38 | $title = 'Red'; 39 | $payload = 'PAYLOAD_RED'; 40 | $image = Str::random(); 41 | 42 | $expected = [ 43 | 'content_type' => 'text', 44 | 'title' => $title, 45 | 'payload' => $payload, 46 | 'image_url' => $image, 47 | ]; 48 | 49 | $this->assertEquals($expected, (new QuickReply($title, $payload))->setImage($image)->toData()); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /tests/Messages/QuickableTest.php: -------------------------------------------------------------------------------- 1 | bootQuick(); 17 | 18 | $title = 'Red'; 19 | $payload = 'PAYLOAD_RED'; 20 | 21 | $stub->addQuick(new QuickReply($title, $payload)); 22 | 23 | $expected = [ 24 | 'message' => [ 25 | 'quick_replies' => [ 26 | [ 27 | 'content_type' => 'text', 28 | 'title' => $title, 29 | 'payload' => $payload, 30 | ], 31 | ], 32 | ], 33 | ]; 34 | 35 | $this->assertEquals($expected, $stub->makeQuickReply()); 36 | } 37 | } 38 | 39 | class QuickStub 40 | { 41 | use Quickable; 42 | } 43 | -------------------------------------------------------------------------------- /tests/Messages/ReceiveMessageTest.php: -------------------------------------------------------------------------------- 1 | message = Str::random(); 29 | $this->recipient = Str::random(); 30 | $this->sender = Str::random(); 31 | $this->postback = Str::random(); 32 | $this->attachments = []; 33 | $this->skip = true; 34 | $this->payload = false; 35 | } 36 | 37 | public function test_is_skip() 38 | { 39 | $actual = $this->getReceiveMessage(); 40 | $this->assertEquals($this->skip, $actual->isSkip()); 41 | } 42 | 43 | public function test_get_sender() 44 | { 45 | $actual = $this->getReceiveMessage(); 46 | $this->assertEquals($this->sender, $actual->getSender()); 47 | } 48 | 49 | public function test_get_message() 50 | { 51 | $actual = $this->getReceiveMessage(); 52 | $this->assertEquals($this->message, $actual->getMessage()); 53 | } 54 | 55 | public function test_is_payload() 56 | { 57 | $actual = $this->getReceiveMessage(); 58 | $this->assertEquals($this->payload, $actual->isPayload()); 59 | } 60 | 61 | public function test_get_postback() 62 | { 63 | $actual = $this->getReceiveMessage(); 64 | $this->assertEquals($this->postback, $actual->getPostback()); 65 | } 66 | 67 | public function test_get_recipient() 68 | { 69 | $actual = $this->getReceiveMessage(); 70 | $this->assertEquals($this->recipient, $actual->getRecipient()); 71 | } 72 | 73 | public function test_get_attachments() 74 | { 75 | $actual = $this->getReceiveMessage(); 76 | $this->assertEquals($this->attachments, $actual->getAttachments()); 77 | } 78 | 79 | public function test_has_attachments() 80 | { 81 | $actual = $this->getReceiveMessage(); 82 | $this->assertFalse($actual->hasAttachments()); 83 | } 84 | 85 | private function getReceiveMessage() 86 | { 87 | $message = new ReceiveMessage($this->recipient, $this->sender); 88 | $message 89 | ->setMessage($this->message) 90 | ->setPostback($this->postback) 91 | ->setSkip($this->skip) 92 | ->setPayload($this->payload) 93 | ->setAttachments($this->attachments); 94 | return $message; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /tests/Messages/ReceiverTest.php: -------------------------------------------------------------------------------- 1 | createRequestMock(self::$messageJson)); 23 | $this->assertInstanceOf(ReceiveMessageCollection::class, $receiver->getMessages()); 24 | $actual = $receiver->getMessages()->first(); 25 | $this->assertEquals('fsadfjojiwejf', $actual->getMessage()); 26 | $this->assertFalse($actual->isSkip()); 27 | $this->assertFalse($actual->isPayload()); 28 | } 29 | 30 | public function test_get_referral_message() 31 | { 32 | $receiver = new Receiver($this->createRequestMock(self::$referralJson)); 33 | $this->assertInstanceOf(ReceiveMessageCollection::class, $receiver->getMessages()); 34 | $actual = $receiver->getMessages()->first(); 35 | $this->assertArraySubset(['ref' => 'test-start', 'source' => 'SHORTLINK', 'type' => 'OPEN_THREAD'], $actual->getReferral()); 36 | $this->assertFalse($actual->isSkip()); 37 | } 38 | 39 | public function test_postback_message() 40 | { 41 | $receiver = new Receiver($this->createRequestMock(self::$postbackJson)); 42 | $this->assertInstanceOf(ReceiveMessageCollection::class, $receiver->getMessages()); 43 | $actual = $receiver->getMessages()->first(); 44 | $this->assertEquals('USER_DEFINED_PAYLOAD', $actual->getPostback()); 45 | $this->assertTrue($actual->isPayload()); 46 | } 47 | 48 | public function test_postback_referral_message() 49 | { 50 | $receiver = new Receiver($this->createRequestMock(self::$postbackJson)); 51 | $this->assertInstanceOf(ReceiveMessageCollection::class, $receiver->getMessages()); 52 | $actual = $receiver->getMessages()->first(); 53 | $this->assertArraySubset(['ref' => 'test-ref', 'source' => 'SHORTLINK', 'type' => 'OPEN_THREAD'], $actual->getReferral()); 54 | } 55 | 56 | private function createRequestMock($json) 57 | { 58 | $mock = m::mock(Request::class) 59 | ->shouldReceive('input') 60 | ->andReturnUsing(function () use ($json) { 61 | return Arr::get(json_decode($json, true), 'entry.0.messaging'); 62 | }) 63 | ->getMock(); 64 | return $mock; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /tests/Messages/StartButtonTest.php: -------------------------------------------------------------------------------- 1 | [ 17 | 'payload' => $payload, 18 | ], 19 | ]; 20 | 21 | $startButton = new StartButton($payload); 22 | $this->assertEquals($expected, $startButton->toData()); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /tests/Messages/TextTest.php: -------------------------------------------------------------------------------- 1 | [ 20 | 'id' => $sender, 21 | ], 22 | 'message' => [ 23 | 'text' => $message, 24 | ], 25 | ]; 26 | 27 | $this->assertEquals($expected, (new Text($sender, $message))->toData()); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /tests/Messages/UrlButtonTest.php: -------------------------------------------------------------------------------- 1 | useMessengerExtensions(); 16 | 17 | $expected = [ 18 | 'type' => 'web_url', 19 | 'title' => 'title', 20 | 'url' => 'url', 21 | 'messenger_extensions' => true, 22 | ]; 23 | 24 | $this->assertEquals($expected, $button->toData()); 25 | } 26 | 27 | public function test_set_fallback_url() 28 | { 29 | $button = new UrlButton('title', 'url'); 30 | $button->setFallbackUrl('http://www.google.com'); 31 | 32 | $expected = [ 33 | 'type' => 'web_url', 34 | 'title' => 'title', 35 | 'url' => 'url', 36 | 'fallback_url' => 'http://www.google.com', 37 | ]; 38 | 39 | $this->assertEquals($expected, $button->toData()); 40 | } 41 | 42 | public function test_set_webview_height_ratio() 43 | { 44 | $button = new UrlButton('title', 'url'); 45 | $button->setWebviewHeightRatio(UrlButton::TYPE_COMPACT); 46 | 47 | $expected = [ 48 | 'type' => 'web_url', 49 | 'title' => 'title', 50 | 'url' => 'url', 51 | 'webview_height_ratio' => 'compact', 52 | ]; 53 | 54 | $this->assertEquals($expected, $button->toData()); 55 | } 56 | 57 | public function test_disable_share() 58 | { 59 | $button = new UrlButton('title', 'url'); 60 | $button->disableShare(); 61 | 62 | $expected = [ 63 | 'type' => 'web_url', 64 | 'title' => 'title', 65 | 'url' => 'url', 66 | 'webview_share_button' => 'hide', 67 | ]; 68 | 69 | $this->assertEquals($expected, $button->toData()); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /tests/Messages/UserTest.php: -------------------------------------------------------------------------------- 1 | assertEquals([], $user->toData()); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tests/Messages/VideoTest.php: -------------------------------------------------------------------------------- 1 | url; 18 | 19 | $actual = new Video($sender, $url); 20 | 21 | $expected = [ 22 | 'recipient' => [ 23 | 'id' => $sender, 24 | ], 25 | 'message' => [ 26 | 'attachment' => [ 27 | 'type' => 'video', 28 | 'payload' => [ 29 | 'url' => $url, 30 | ], 31 | ], 32 | ], 33 | ]; 34 | 35 | $this->assertEquals($expected, $actual->toData()); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /tests/Middleware/RequestReceivedTest.php: -------------------------------------------------------------------------------- 1 | shouldReceive('all')->andReturn([]); 21 | 22 | $dispatch = m::mock(Dispatcher::class); 23 | 24 | $debug = new Debug($dispatch); 25 | 26 | $received = new RequestReceived($debug); 27 | $received->handle($request, function () { 28 | }); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /tests/PersistentMenu/MenuTest.php: -------------------------------------------------------------------------------- 1 | assertEquals(true, (new Menu())->isEmpty()); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /tests/Providers/RouteServiceProviderTest.php: -------------------------------------------------------------------------------- 1 | setUpMock(); 28 | $this->serviceProvider = new RouteServiceProvider($this->applicationMock); 29 | } 30 | 31 | protected function setUpMock() 32 | { 33 | $this->configMock = m::mock(Config::class); 34 | $this->configMock 35 | ->shouldReceive('get') 36 | ->andReturn([]); 37 | $this->applicationMock = m::mock(Application::class); 38 | $this->applicationMock 39 | ->shouldReceive('routesAreCached') 40 | ->andReturn(false) 41 | ->shouldReceive('offsetGet') 42 | ->zeroOrMoreTimes() 43 | ->with('config') 44 | ->andReturn($this->configMock); 45 | $this->router = m::mock(Router::class); 46 | $this->router 47 | ->shouldReceive('group') 48 | ->with(m::any(), m::type(Closure::class)) 49 | ->andReturnUsing(function ($options, $closure) { 50 | $closure($this->router); 51 | }) 52 | ->shouldReceive('get') 53 | ->andReturnNull() 54 | ->shouldReceive('post') 55 | ->andReturnNull(); 56 | } 57 | 58 | public function test_boot() 59 | { 60 | $this->assertNull($this->serviceProvider->boot($this->router)); 61 | } 62 | 63 | public function test_register() 64 | { 65 | $this->assertNull($this->serviceProvider->register()); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /tests/TestCase.php: -------------------------------------------------------------------------------- 1 | getProperty($propertyName); 25 | $property->setAccessible(true); 26 | return $property; 27 | } 28 | 29 | protected function getPrivateMethod($class, $methodName) 30 | { 31 | $reflector = new ReflectionClass($class); 32 | $method = $reflector->getMethod($methodName); 33 | $method->setAccessible(true); 34 | return $method; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /tests/Transformers/ButtonTransformerTest.php: -------------------------------------------------------------------------------- 1 | toData(); 30 | } 31 | 32 | $expected = [ 33 | 'template_type' => 'button', 34 | 'text' => $testText, 35 | 'buttons' => $buttonExpected, 36 | ]; 37 | 38 | $transformer = new ButtonTransformer(); 39 | $actual = $transformer->transform($this->createMessageMock($testCase, $testSender, $testText)); 40 | $this->assertEquals($expected, $actual); 41 | } 42 | 43 | public function test_empty_text_and_exception() 44 | { 45 | $this->expectException(RequiredArgumentException::class); 46 | $transformer = new ButtonTransformer(); 47 | $transformer->transform($this->createMessageMock([], null, '')); 48 | } 49 | 50 | private function createMessageMock($testCase, $testSender, $testText) 51 | { 52 | $collection = new ButtonCollection($testCase); 53 | 54 | $message = m::mock(Template::class) 55 | ->shouldReceive('getSender') 56 | ->andReturn($testSender) 57 | ->shouldReceive('getText') 58 | ->andReturn($testText) 59 | ->shouldReceive('getCollections') 60 | ->andReturn($collection) 61 | ->getMock(); 62 | 63 | return $message; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /tests/Transformers/GenericTransformerTest.php: -------------------------------------------------------------------------------- 1 | toData(); 29 | } 30 | 31 | $transformer = new GenericTransformer; 32 | 33 | $expected = [ 34 | 'template_type' => 'generic', 35 | 'image_aspect_ratio' => 'horizontal', 36 | 'elements' => $expectedCase, 37 | ]; 38 | 39 | 40 | $actual = $transformer->transform($this->createMessageMock($testCase, $testSender)); 41 | $this->assertEquals($expected, $actual); 42 | } 43 | 44 | private function createMessageMock($testCase, $testSender) 45 | { 46 | $elements = new ElementCollection($testCase); 47 | 48 | $message = m::mock(Template::class) 49 | ->shouldReceive('getSender')->andReturn($testSender) 50 | ->shouldReceive('getCollections')->andReturn($elements) 51 | ->shouldReceive('getImageRatio')->andReturn('horizontal') 52 | ->getMock(); 53 | 54 | return $message; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /tests/Transformers/ListTransformerTest.php: -------------------------------------------------------------------------------- 1 | toData(); 34 | } 35 | 36 | $transformer = new ListTransformer(); 37 | 38 | $expected = [ 39 | 'template_type' => 'list', 40 | 'top_element_style' => 'large', 41 | 'elements' => $expectedCase, 42 | 'buttons' => [$testButton->toData()] 43 | ]; 44 | 45 | 46 | $actual = $transformer->transform($this->createMessageMock($testCase, $testSender, $testButton)); 47 | $this->assertEquals($expected, $actual); 48 | } 49 | 50 | public function test_transform_without_buttons() 51 | { 52 | $testSender = Str::random(); 53 | $testCase = [ 54 | new Element('title1', 'description2', 'image_url'), 55 | new Element('title2', 'description2', 'image_url'), 56 | new Element('title2', 'description2', 'image_url'), 57 | ]; 58 | 59 | $expectedCase = []; 60 | foreach ($testCase as $case) { 61 | $expectedCase[] = $case->toData(); 62 | } 63 | 64 | $transformer = new ListTransformer(); 65 | 66 | $expected = [ 67 | 'template_type' => 'list', 68 | 'top_element_style' => 'large', 69 | 'elements' => $expectedCase, 70 | ]; 71 | 72 | 73 | $actual = $transformer->transform($this->createMessageMock($testCase, $testSender, null)); 74 | $this->assertEquals($expected, $actual); 75 | } 76 | 77 | private function createMessageMock($testCase, $testSender, $testButton) 78 | { 79 | $elements = new ElementCollection($testCase); 80 | 81 | $message = m::mock(Template::class) 82 | ->shouldReceive('getSender')->andReturn($testSender) 83 | ->shouldReceive('getCollections')->andReturn($elements) 84 | ->shouldReceive('getButton')->andReturn($testButton) 85 | ->shouldReceive('getTopStyle')->andReturn('large') 86 | ->getMock(); 87 | 88 | return $message; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /tests/bootstrap.php: -------------------------------------------------------------------------------- 1 | locale('default', function () use ($menu) { 6 | $menu->postback('Test Button', 'TEST_POSTBACK'); 7 | $menu->webUrl('WebUrl', 'https://github.com/CasperLaiTW/laravel-fb-messenger'); 8 | 9 | $menu->nested('SubMenu', function () use ($menu) { 10 | $menu->postback('SubMenu-Button', 'TEST_SUB_BUTTON'); 11 | $menu->webUrl(new UrlButton('SubMenu-WebUrl', 'https://github.com/CasperLaiTW/laravel-fb-messenger')); 12 | }); 13 | }); 14 | 15 | $menu->locale('zh_TW', function () use ($menu) { 16 | $menu->disableInput(); 17 | $menu->postback('zh_TW Test Button', 'TEST_POSTBACK'); 18 | $menu->postback('zh_TW Test Button', 'TEST_POSTBACK'); 19 | $menu->postback('zh_TW Test Button', 'TEST_POSTBACK'); 20 | }); 21 | -------------------------------------------------------------------------------- /webpack/webpack.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by casperlai on 2016/11/20. 3 | */ 4 | 5 | module.exports = { 6 | entry: { 7 | dist: './resources/assets/js/bootstrap', 8 | }, 9 | output: { 10 | path: './public/fb-messenger/', 11 | filename: '[name].js', 12 | }, 13 | resolve: { 14 | extensions: ['', '.js'], 15 | alias: { 16 | 'vue$': 'vue/dist/vue', 17 | }, 18 | }, 19 | module: { 20 | loaders: [ 21 | { 22 | test: /\.js$/, 23 | loader: 'babel', 24 | exclude: /node_modules/, 25 | }, 26 | { 27 | test: /\.vue$/, 28 | loader: 'vue', 29 | }, 30 | ], 31 | }, 32 | }; --------------------------------------------------------------------------------