├── LICENSE ├── README.md ├── composer.json ├── discovery.json ├── src ├── Extras │ └── TypingIndicator.php ├── Laravel │ ├── routes.php │ └── views │ │ └── chat.blade.php ├── Providers │ └── WebServiceProvider.php └── WebDriver.php └── stubs └── web.php /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Marcel Pociot 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 all 13 | 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 THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BotMan Web Driver 2 | 3 | [![Latest Version on Packagist](https://img.shields.io/packagist/v/botman/driver-web.svg?style=flat-square)](https://packagist.org/packages/botman/driver-web) 4 | [![Build Status](https://travis-ci.org/botman/driver-web.svg?branch=master)](https://travis-ci.org/botman/driver-web) 5 | [![codecov](https://codecov.io/gh/botman/driver-web/branch/master/graph/badge.svg)](https://codecov.io/gh/botman/driver-web) 6 | 7 | BotMan driver to use [BotMan](https://github.com/botman/botman) within your website / API 8 | 9 | ## Contributing 10 | 11 | Please see [CONTRIBUTING](CONTRIBUTING.md) for details. 12 | 13 | ## Security Vulnerabilities 14 | 15 | If you discover a security vulnerability within BotMan, please send an e-mail to Marcel Pociot at m.pociot@gmail.com. All security vulnerabilities will be promptly addressed. 16 | 17 | ## License 18 | 19 | BotMan is free software distributed under the terms of the MIT license. 20 | 21 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "botman/driver-web", 3 | "license": "MIT", 4 | "description": "Web driver for BotMan", 5 | "keywords": [ 6 | "Bot", 7 | "BotMan", 8 | "Web" 9 | ], 10 | "homepage": "http://github.com/botman/driver-web", 11 | "authors": [{ 12 | "name": "Marcel Pociot", 13 | "email": "m.pociot@gmail.com" 14 | }], 15 | "require": { 16 | "php": ">=7.0", 17 | "botman/botman": ">=2.0" 18 | }, 19 | "require-dev": { 20 | "botman/studio-addons": "~1.0", 21 | "illuminate/support": "~5.5.0", 22 | "phpunit/phpunit": "~5.0", 23 | "mockery/mockery": "^1.1", 24 | "botman/driver-facebook": "~2.0", 25 | "ext-curl": "*" 26 | }, 27 | "autoload": { 28 | "psr-4": { 29 | "BotMan\\Drivers\\Web\\": "src/" 30 | } 31 | }, 32 | "autoload-dev": { 33 | "psr-4": { 34 | "Tests\\": "tests/" 35 | } 36 | }, 37 | "scripts": { 38 | "test": "vendor/bin/phpunit", 39 | "cs": "php-cs-fixer fix" 40 | }, 41 | "extra": { 42 | "branch-alias": { 43 | "dev-master": "2.0-dev" 44 | }, 45 | "laravel": { 46 | "providers": [ 47 | "BotMan\\Drivers\\Web\\Providers\\WebServiceProvider" 48 | ] 49 | } 50 | }, 51 | "minimum-stability": "dev", 52 | "prefer-stable": true 53 | } -------------------------------------------------------------------------------- /discovery.json: -------------------------------------------------------------------------------- 1 | { 2 | "botman/driver-config": [ 3 | "stubs/web.php" 4 | ], 5 | "botman/driver": [ 6 | "BotMan\\Drivers\\Web\\WebDriver" 7 | ] 8 | } -------------------------------------------------------------------------------- /src/Extras/TypingIndicator.php: -------------------------------------------------------------------------------- 1 | timeout = $timeout; 29 | } 30 | 31 | /** 32 | * Get the instance as a web accessible array. 33 | * This will be used within the WebDriver. 34 | * 35 | * @return array 36 | */ 37 | public function toWebDriver() 38 | { 39 | return [ 40 | 'type' => 'typing_indicator', 41 | 'timeout' => $this->timeout, 42 | ]; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Laravel/routes.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | BotMan Widget 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/Providers/WebServiceProvider.php: -------------------------------------------------------------------------------- 1 | isRunningInBotManStudio()) { 20 | $this->loadDrivers(); 21 | 22 | $this->publishes([ 23 | __DIR__.'/../../stubs/web.php' => config_path('botman/web.php'), 24 | ]); 25 | 26 | $this->mergeConfigFrom(__DIR__.'/../../stubs/web.php', 'botman.web'); 27 | } 28 | 29 | $this->loadRoutesFrom(__DIR__.'/../Laravel/routes.php'); 30 | $this->loadViewsFrom(__DIR__.'/../Laravel/views', 'botman-web'); 31 | $this->publishes([ 32 | __DIR__.'/../Laravel/views' => resource_path('views/vendor/botman-web'), 33 | ]); 34 | } 35 | 36 | /** 37 | * Load BotMan drivers. 38 | */ 39 | protected function loadDrivers() 40 | { 41 | DriverManager::loadDriver(WebDriver::class); 42 | } 43 | 44 | /** 45 | * @return bool 46 | */ 47 | protected function isRunningInBotManStudio() 48 | { 49 | return class_exists(StudioServiceProvider::class); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/WebDriver.php: -------------------------------------------------------------------------------- 1 | payload = $request->request->all(); 54 | $this->event = Collection::make($this->payload); 55 | $this->files = Collection::make($request->files->all()); 56 | $this->config = Collection::make($this->config->get('web', [])); 57 | } 58 | 59 | /** 60 | * @param IncomingMessage $matchingMessage 61 | * @return \BotMan\BotMan\Users\User 62 | */ 63 | public function getUser(IncomingMessage $matchingMessage) 64 | { 65 | return new User($matchingMessage->getSender()); 66 | } 67 | 68 | /** 69 | * Determine if the request is for this driver. 70 | * 71 | * @return bool 72 | */ 73 | public function matchesRequest() 74 | { 75 | return Collection::make($this->config->get('matchingData'))->diffAssoc($this->event)->isEmpty(); 76 | } 77 | 78 | /** 79 | * @param IncomingMessage $matchingMessage 80 | * @return void 81 | */ 82 | public function types(IncomingMessage $matchingMessage) 83 | { 84 | $this->replies[] = [ 85 | 'message' => TypingIndicator::create(), 86 | 'additionalParameters' => [], 87 | ]; 88 | } 89 | 90 | /** 91 | * Send a typing indicator and wait for the given amount of seconds. 92 | * 93 | * @param IncomingMessage $matchingMessage 94 | * @param float $seconds 95 | * @return mixed 96 | */ 97 | public function typesAndWaits(IncomingMessage $matchingMessage, float $seconds) 98 | { 99 | $this->replies[] = [ 100 | 'message' => TypingIndicator::create($seconds), 101 | 'additionalParameters' => [], 102 | ]; 103 | } 104 | 105 | /** 106 | * @param IncomingMessage $message 107 | * @return \BotMan\BotMan\Messages\Incoming\Answer 108 | */ 109 | public function getConversationAnswer(IncomingMessage $message) 110 | { 111 | $interactive = $this->event->get('interactive', false); 112 | if (is_string($interactive)) { 113 | $interactive = ($interactive !== 'false') && ($interactive !== '0'); 114 | } else { 115 | $interactive = (bool) $interactive; 116 | } 117 | 118 | return Answer::create($message->getText()) 119 | ->setValue($this->event->get('value', $message->getText())) 120 | ->setMessage($message) 121 | ->setInteractiveReply($interactive); 122 | } 123 | 124 | /** 125 | * @return bool 126 | */ 127 | public function hasMatchingEvent() 128 | { 129 | $event = false; 130 | 131 | if ($this->event->has('eventData')) { 132 | $event = new GenericEvent($this->event->get('eventData')); 133 | $event->setName($this->event->get('eventName')); 134 | } 135 | 136 | return $event; 137 | } 138 | 139 | /** 140 | * Retrieve the chat message. 141 | * 142 | * @return array 143 | */ 144 | public function getMessages() 145 | { 146 | if (empty($this->messages)) { 147 | $message = $this->event->get('message'); 148 | $userId = $this->event->get('userId'); 149 | $sender = $this->event->get('sender', $userId); 150 | 151 | $incomingMessage = new IncomingMessage($message, $sender, $userId, $this->payload); 152 | 153 | $incomingMessage = $this->addAttachments($incomingMessage); 154 | 155 | $this->messages = [$incomingMessage]; 156 | } 157 | 158 | return $this->messages; 159 | } 160 | 161 | /** 162 | * @return bool 163 | */ 164 | public function isBot() 165 | { 166 | return false; 167 | } 168 | 169 | /** 170 | * @param string|Question|OutgoingMessage $message 171 | * @param IncomingMessage $matchingMessage 172 | * @param array $additionalParameters 173 | * @return Response 174 | */ 175 | public function buildServicePayload($message, $matchingMessage, $additionalParameters = []) 176 | { 177 | if (! $message instanceof WebAccess && ! $message instanceof OutgoingMessage) { 178 | $this->errorMessage = 'Unsupported message type.'; 179 | $this->replyStatusCode = 500; 180 | } 181 | 182 | return [ 183 | 'message' => $message, 184 | 'additionalParameters' => $additionalParameters, 185 | ]; 186 | } 187 | 188 | /** 189 | * @param mixed $payload 190 | * @return Response 191 | */ 192 | public function sendPayload($payload) 193 | { 194 | $this->replies[] = $payload; 195 | } 196 | 197 | /** 198 | * @param $messages 199 | * @return array 200 | */ 201 | protected function buildReply($messages) 202 | { 203 | $replyData = Collection::make($messages)->transform(function ($replyData) { 204 | $reply = []; 205 | $message = $replyData['message']; 206 | $additionalParameters = $replyData['additionalParameters']; 207 | 208 | if ($message instanceof WebAccess) { 209 | $reply = $message->toWebDriver(); 210 | } elseif ($message instanceof OutgoingMessage) { 211 | $attachmentData = (is_null($message->getAttachment())) ? null : $message->getAttachment()->toWebDriver(); 212 | $reply = [ 213 | 'type' => 'text', 214 | 'text' => $message->getText(), 215 | 'attachment' => $attachmentData, 216 | ]; 217 | } 218 | $reply['additionalParameters'] = $additionalParameters; 219 | 220 | return $reply; 221 | })->toArray(); 222 | 223 | return $replyData; 224 | } 225 | 226 | /** 227 | * Send out message response. 228 | */ 229 | public function messagesHandled() 230 | { 231 | $messages = $this->buildReply($this->replies); 232 | 233 | // Reset replies 234 | $this->replies = []; 235 | 236 | (new Response(json_encode([ 237 | 'status' => $this->replyStatusCode, 238 | 'messages' => $messages, 239 | ]), $this->replyStatusCode, [ 240 | 'Content-Type' => 'application/json', 241 | 'Access-Control-Allow-Credentials' => true, 242 | 'Access-Control-Allow-Origin' => '*', 243 | ]))->send(); 244 | } 245 | 246 | /** 247 | * @return bool 248 | */ 249 | public function isConfigured() 250 | { 251 | return false; 252 | } 253 | 254 | /** 255 | * Low-level method to perform driver specific API requests. 256 | * 257 | * @param string $endpoint 258 | * @param array $parameters 259 | * @param \BotMan\BotMan\Messages\Incoming\IncomingMessage $matchingMessage 260 | * @return void 261 | */ 262 | public function sendRequest($endpoint, array $parameters, IncomingMessage $matchingMessage) 263 | { 264 | // Not available with the web driver. 265 | } 266 | 267 | /** 268 | * Add potential attachments to the message object. 269 | * 270 | * @param IncomingMessage $incomingMessage 271 | * @return IncomingMessage 272 | */ 273 | protected function addAttachments($incomingMessage) 274 | { 275 | $attachment = $this->event->get('attachment'); 276 | 277 | if ($attachment === self::ATTACHMENT_IMAGE) { 278 | $images = $this->files->map(function ($file) { 279 | if ($file instanceof UploadedFile) { 280 | $path = $file->getRealPath(); 281 | } else { 282 | $path = $file['tmp_name']; 283 | } 284 | 285 | return new Image($this->getDataURI($path)); 286 | })->values()->toArray(); 287 | $incomingMessage->setText(Image::PATTERN); 288 | $incomingMessage->setImages($images); 289 | } elseif ($attachment === self::ATTACHMENT_AUDIO) { 290 | $audio = $this->files->map(function ($file) { 291 | if ($file instanceof UploadedFile) { 292 | $path = $file->getRealPath(); 293 | } else { 294 | $path = $file['tmp_name']; 295 | } 296 | 297 | return new Audio($this->getDataURI($path)); 298 | })->values()->toArray(); 299 | $incomingMessage->setText(Audio::PATTERN); 300 | $incomingMessage->setAudio($audio); 301 | } elseif ($attachment === self::ATTACHMENT_VIDEO) { 302 | $videos = $this->files->map(function ($file) { 303 | if ($file instanceof UploadedFile) { 304 | $path = $file->getRealPath(); 305 | } else { 306 | $path = $file['tmp_name']; 307 | } 308 | 309 | return new Video($this->getDataURI($path)); 310 | })->values()->toArray(); 311 | $incomingMessage->setText(Video::PATTERN); 312 | $incomingMessage->setVideos($videos); 313 | } elseif ($attachment === self::ATTACHMENT_FILE) { 314 | $files = $this->files->map(function ($file) { 315 | if ($file instanceof UploadedFile) { 316 | $path = $file->getRealPath(); 317 | } else { 318 | $path = $file['tmp_name']; 319 | } 320 | 321 | return new File($this->getDataURI($path)); 322 | })->values()->toArray(); 323 | $incomingMessage->setText(File::PATTERN); 324 | $incomingMessage->setFiles($files); 325 | } 326 | 327 | return $incomingMessage; 328 | } 329 | 330 | /** 331 | * @param $file 332 | * @param string $mime 333 | * @return string 334 | */ 335 | protected function getDataURI($file, $mime = '') 336 | { 337 | return 'data: '.(function_exists('mime_content_type') ? mime_content_type($file) : $mime).';base64,'.base64_encode(file_get_contents($file)); 338 | } 339 | } 340 | -------------------------------------------------------------------------------- /stubs/web.php: -------------------------------------------------------------------------------- 1 | [ 15 | 'driver' => 'web', 16 | ], 17 | ]; 18 | --------------------------------------------------------------------------------