├── .gitignore ├── src ├── Contracts │ └── Command.php ├── Console │ ├── stubs │ │ └── command.stub │ ├── BotCommand │ │ └── MakeCommand.php │ └── Webhook │ │ ├── RemoveCommand.php │ │ └── SetCommand.php ├── Facades │ ├── TelegramBot.php │ └── TelegramApi.php ├── Http │ └── BotCommands │ │ ├── PingCommand.php │ │ └── Command.php ├── Client.php ├── Bot.php └── ServiceProvider.php ├── composer.json ├── LICENSE ├── config └── telegram.php └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | composer.lock 2 | vendor 3 | -------------------------------------------------------------------------------- /src/Contracts/Command.php: -------------------------------------------------------------------------------- 1 | client->send('sendMessage', [ 18 | 'chat_id' => $this->request->json('message.chat.id'), 19 | 'text' => 'pong', 20 | ]); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "black-river/telegram-bot", 3 | "description": "Create your Telegram Bot in 5 minutes with Laravel", 4 | "license": "MIT", 5 | "keywords": ["laravel", "telegram", "bot", "lumen", "api"], 6 | "authors": [ 7 | { 8 | "name": "Oleg Seleznev", 9 | "email": "seleznevdev@gmail.com" 10 | } 11 | ], 12 | "require": { 13 | "illuminate/support": "5.2.*|5.3.*", 14 | "illuminate/contracts": "5.2.*|5.3.*", 15 | "guzzlehttp/guzzle": "~6.0" 16 | }, 17 | "autoload": { 18 | "psr-4": { 19 | "BlackRiver\\TelegramBot\\": "src/" 20 | } 21 | }, 22 | "extra": { 23 | "branch-alias": { 24 | "dev-master": "0.7-dev" 25 | } 26 | }, 27 | "minimum-stability": "dev" 28 | } 29 | -------------------------------------------------------------------------------- /src/Http/BotCommands/Command.php: -------------------------------------------------------------------------------- 1 | request = $request; 35 | 36 | $this->client = $client; 37 | } 38 | 39 | /** 40 | * {@inheritdoc} 41 | */ 42 | public function handle($message) 43 | { 44 | // 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Black River, Inc. 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 | -------------------------------------------------------------------------------- /src/Console/BotCommand/MakeCommand.php: -------------------------------------------------------------------------------- 1 | client = $client; 42 | } 43 | 44 | /** 45 | * Execute the console command. 46 | * 47 | * @return mixed 48 | */ 49 | public function handle() 50 | { 51 | $result = $this->client->send('setWebhook'); 52 | 53 | return $this->info($result['description']); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /config/telegram.php: -------------------------------------------------------------------------------- 1 | env('TELEGRAM_TOKEN'), 15 | 16 | /* 17 | |-------------------------------------------------------------------------- 18 | | Public Key Certificate Path 19 | |-------------------------------------------------------------------------- 20 | | 21 | | Whenever there is an update for the bot, Telegram will send an HTTPS POST 22 | | request to the specified url. To use a self-signed certificate, you need 23 | | to specify the path of your public key certificate. 24 | | 25 | */ 26 | 27 | 'certificate' => env('TELEGRAM_CERTIFICATE'), 28 | 29 | /* 30 | |-------------------------------------------------------------------------- 31 | | Telegram Bot Commands 32 | |-------------------------------------------------------------------------- 33 | | 34 | | Make your Bot to do awesome things! 35 | | 36 | */ 37 | 38 | 'commands' => [ 39 | '/ping' => BlackRiver\TelegramBot\Http\BotCommands\PingCommand::class, 40 | ], 41 | 42 | /* 43 | |-------------------------------------------------------------------------- 44 | | Telegram Client Options 45 | |-------------------------------------------------------------------------- 46 | | 47 | | You can specify the handler or default request options. 48 | | 49 | */ 50 | 51 | 'client_options' => [ 52 | // 53 | ], 54 | 55 | ]; 56 | -------------------------------------------------------------------------------- /src/Console/Webhook/SetCommand.php: -------------------------------------------------------------------------------- 1 | config = $config; 52 | 53 | $this->client = $client; 54 | } 55 | 56 | /** 57 | * Execute the console command. 58 | * 59 | * @return mixed 60 | */ 61 | public function handle() 62 | { 63 | $parameters = [ 64 | 'url' => $this->getUrl(), 65 | ]; 66 | 67 | if ($certificate = $this->config->get('telegram.certificate')) { 68 | $parameters['certificate'] = fopen($certificate, 'r'); 69 | } 70 | 71 | $result = $this->client->send('setWebhook', $parameters); 72 | 73 | return $this->info($result['description'].': '.$parameters['url']); 74 | } 75 | 76 | /** 77 | * Get a secure url. 78 | * 79 | * @return string 80 | */ 81 | protected function getUrl() 82 | { 83 | $url = $this->config->get('app.url', env('APP_URL')); 84 | 85 | $secureUrl = preg_replace('/^http:\/\//', 'https://', $url); 86 | 87 | $path = $this->argument('path') ?: $this->config->get('telegram.token'); 88 | 89 | return rtrim($secureUrl, '/').'/'.ltrim($path, '/'); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/Client.php: -------------------------------------------------------------------------------- 1 | config = $config; 43 | 44 | $options = $this->config->get('telegram.client_options'); 45 | 46 | $this->guzzle = new Guzzle( 47 | array_merge($options, ['base_uri' => self::BASE_URI]) 48 | ); 49 | } 50 | 51 | /** 52 | * Send a request to the Telegram Bot API. 53 | * 54 | * @param string $method 55 | * @param array $parameters 56 | * @param array $options 57 | * @return array 58 | */ 59 | public function send($method, array $parameters = [], array $options = []) 60 | { 61 | $method = ltrim($method, '/'); 62 | 63 | $uri = '/bot'.$this->config->get('telegram.token').'/'.$method; 64 | 65 | $response = $this->guzzle->request('POST', $uri, 66 | array_merge($options, $this->getRequestOption($parameters)) 67 | ); 68 | 69 | return json_decode($response->getBody()->getContents(), true); 70 | } 71 | 72 | /** 73 | * Save a file from Telegram. 74 | * 75 | * @param string $path 76 | * @param string $target 77 | * @param array $options 78 | * @return void 79 | */ 80 | public function save($path, $target, array $options = []) 81 | { 82 | $uri = '/file/bot'.$this->config->get('telegram.token').'/'.$path; 83 | 84 | $this->guzzle->request('GET', $uri, 85 | array_merge($options, ['sink' => $target]) 86 | ); 87 | } 88 | 89 | /** 90 | * Get the request option. 91 | * 92 | * @param array $parameters 93 | * @return array 94 | */ 95 | protected function getRequestOption(array $parameters) 96 | { 97 | if (array_filter($parameters, 'is_resource')) { 98 | $data = []; 99 | 100 | foreach ($parameters as $name => $contents) { 101 | $data[] = compact('name', 'contents'); 102 | } 103 | 104 | return ['multipart' => $data]; 105 | } 106 | 107 | return ['form_params' => $parameters]; 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/Bot.php: -------------------------------------------------------------------------------- 1 | config = $config; 52 | 53 | $this->request = $request; 54 | 55 | $this->client = $client; 56 | } 57 | 58 | /** 59 | * Listen commands. 60 | * 61 | * @return mixed|null 62 | */ 63 | public function listen() 64 | { 65 | if ($entity = $this->getCommandEntity()) { 66 | list($name, $message) = $this->getEntityText($entity); 67 | 68 | if ($command = $this->getCommand($name)) { 69 | return (new $command($this->request, $this->client))->handle($message); 70 | } 71 | } 72 | } 73 | 74 | /** 75 | * Get the command entity. 76 | * 77 | * @return array|null 78 | */ 79 | protected function getCommandEntity() 80 | { 81 | $entity = $this->request->json('message.entities.0'); 82 | 83 | if (Arr::get($entity, 'type') === self::COMMAND_ENTITY) { 84 | return $entity; 85 | } 86 | } 87 | 88 | /** 89 | * Get the text of a given entity. 90 | * 91 | * @param array $entity 92 | * @return array 93 | */ 94 | protected function getEntityText(array $entity) 95 | { 96 | $text = $this->request->json('message.text'); 97 | 98 | return [ 99 | Str::substr($text, $entity['offset'], $entity['length']), 100 | Str::substr($text, $entity['offset'] + $entity['length']), 101 | ]; 102 | } 103 | 104 | /** 105 | * Get the command class for a given name. 106 | * 107 | * @param string $name 108 | * @return string|null 109 | */ 110 | protected function getCommand($name) 111 | { 112 | $name = Str::lower($name); 113 | 114 | $commands = $this->config->get('telegram.commands'); 115 | 116 | return Arr::get($commands, $name, Arr::get($commands, ltrim($name, '/'))); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/ServiceProvider.php: -------------------------------------------------------------------------------- 1 | app instanceof LaravelApplication && $this->app->runningInConsole()) { 25 | $this->publishes([ 26 | __DIR__.'/../config/telegram.php' => config_path('telegram.php'), 27 | ]); 28 | } elseif ($this->app instanceof LumenApplication) { 29 | $this->app->configure('telegram'); 30 | } 31 | } 32 | 33 | /** 34 | * Register the service provider. 35 | * 36 | * @return void 37 | */ 38 | public function register() 39 | { 40 | $this->mergeConfigFrom(__DIR__.'/../config/telegram.php', 'telegram'); 41 | 42 | $this->registerBot(); 43 | 44 | $this->registerClient(); 45 | 46 | $this->registerCommands(); 47 | } 48 | 49 | /** 50 | * Register the bot. 51 | * 52 | * @return void 53 | */ 54 | protected function registerBot() 55 | { 56 | $this->app->singleton(Bot::class, function ($app) { 57 | return new Bot($app['config'], $app['request'], $app[Client::class]); 58 | }); 59 | 60 | $this->app->alias(Bot::class, 'telegram.bot'); 61 | } 62 | 63 | /** 64 | * Register the client. 65 | * 66 | * @return void 67 | */ 68 | protected function registerClient() 69 | { 70 | $this->app->bind(Client::class, function ($app) { 71 | return new Client($app['config']); 72 | }); 73 | 74 | $this->app->alias(Client::class, 'telegram.client'); 75 | } 76 | 77 | /** 78 | * Register the commands. 79 | * 80 | * @return void 81 | */ 82 | protected function registerCommands() 83 | { 84 | $this->app->singleton('command.webhook.set', function ($app) { 85 | return new SetCommand($app['config'], $app[Client::class]); 86 | }); 87 | 88 | $this->app->singleton('command.webhook.remove', function ($app) { 89 | return new RemoveCommand($app[Client::class]); 90 | }); 91 | 92 | $this->app->singleton('command.bot-command.make', function ($app) { 93 | return new MakeCommand($app['files']); 94 | }); 95 | 96 | $this->commands( 97 | 'command.webhook.set', 'command.webhook.remove', 98 | 'command.bot-command.make' 99 | ); 100 | } 101 | 102 | /** 103 | * Get the services provided by the provider. 104 | * 105 | * @return array 106 | */ 107 | public function provides() 108 | { 109 | return [ 110 | Bot::class, 'telegram.bot', 111 | Client::class, 'telegram.client', 112 | 'command.webhook.set', 'command.webhook.remove', 113 | 'command.bot-command.make', 114 | ]; 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Laravel Telegram Bot 2 | ==================== 3 | 4 | ![cover](https://cloud.githubusercontent.com/assets/5261079/17837503/ac398cd0-67bd-11e6-8493-962ef6111d58.png) 5 | 6 | ## Installation 7 | 8 | Require this package, with [Composer](https://getcomposer.org/): 9 | 10 | ```bash 11 | composer require black-river/telegram-bot 12 | ``` 13 | 14 | Add the service provider to the `providers` array of your `config/app.php`: 15 | 16 | ```php 17 | BlackRiver\TelegramBot\ServiceProvider::class, 18 | ``` 19 | 20 | ## Configuration 21 | 22 | Publish the config file: 23 | 24 | ```bash 25 | php artisan vendor:publish --provider="BlackRiver\TelegramBot\ServiceProvider" 26 | ``` 27 | 28 | Set environment variables in your `.env`: 29 | 30 | ``` 31 | APP_URL="http://your-bot.com" 32 | ... 33 | TELEGRAM_TOKEN="123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11" 34 | ``` 35 | 36 | To use a [self-signed certificate](https://core.telegram.org/bots/self-signed), you should also specify the certificate path: 37 | 38 | ``` 39 | TELEGRAM_CERTIFICATE="/etc/nginx/ssl/your-bot.com.crt" 40 | ``` 41 | 42 | ## Quickstart 43 | 44 | Define the default webhook route in your route file: 45 | 46 | ```php 47 | Route::post(config('telegram.token'), function (BlackRiver\TelegramBot\Bot $bot) { 48 | $bot->listen(); 49 | }); 50 | ``` 51 | 52 | - Use the Bot's `listen` method to handle commands. 53 | 54 | Set up the webhook url: 55 | 56 | ```bash 57 | php artisan webhook:set 58 | ``` 59 | 60 | To ensure the bot is ready, send the `/ping` message: 61 | 62 | ![ping-command](https://cloud.githubusercontent.com/assets/5261079/17837506/b808b3ba-67bd-11e6-911d-42d1568ab068.png) 63 | 64 | > To make sure there is no middleware or prefix that could "block" the default webhook route, check your `app/Providers/RouteServiceProvider.php`. 65 | 66 | ## Webhook URL 67 | 68 | You can change the default webhook route to your own: 69 | 70 | ```php 71 | Route::post('your-secret-path', function (BlackRiver\TelegramBot\Bot $bot) { 72 | $bot->listen(); 73 | }); 74 | ``` 75 | 76 | ```bash 77 | php artisan webhook:set your-secret-path 78 | ``` 79 | 80 | To remove the webhook integration, run `php artisan webhook:remove`. 81 | 82 | ## Bot Commands 83 | 84 | Create a new Bot Command in the `app/Http/BotCommands` directory: 85 | 86 | ```bash 87 | php artisan make:bot-command NameCommand 88 | ``` 89 | 90 | Edit the `handle` method of `app/Http/BotCommands/NameCommand.php`: 91 | 92 | ```php 93 | $this->client->send('sendMessage', [ 94 | 'chat_id' => $this->request->json('message.chat.id'), 95 | 'text' => 'Hello, '.trim($message), 96 | ]); 97 | ``` 98 | 99 | - Use the Client's `send` method to call any of the [available methods](https://core.telegram.org/bots/api#available-methods). 100 | 101 | - Use the Client's `save` method to save Telegram files. 102 | 103 | - The Client and Request are available within a Command via `$this->client` and `$this->request` respectively. 104 | 105 | Add the new command to the `commands` array of your `config/telegram.php`: 106 | 107 | ```php 108 | '/name' => App\Http\BotCommands\NameCommand::class, 109 | ``` 110 | 111 | Send the `/name Johnny` message: 112 | 113 | ![name-command](https://cloud.githubusercontent.com/assets/5261079/17837505/b5d147c4-67bd-11e6-8aa3-b65a59151815.png) 114 | 115 | ## Raw Webhook 116 | 117 | ```php 118 | Route::post('your-secret-path', function (Illuminate\Http\Request $request, BlackRiver\TelegramBot\Client $client) { 119 | // $bot->listen(); 120 | 121 | $update = $request->json()->all(); 122 | 123 | $client->send('sendMessage', [ 124 | 'chat_id' => $request->json('message.chat.id'), 125 | 'text' => 'I\'ve got it!', 126 | ]); 127 | }); 128 | ``` 129 | 130 | ## Facades 131 | 132 | Add facades to the `aliases` array of your `config/app.php`: 133 | 134 | ```php 135 | 'TelegramBot' => BlackRiver\TelegramBot\Facades\TelegramBot::class, 136 | 'TelegramApi' => BlackRiver\TelegramBot\Facades\TelegramApi::class, 137 | ``` 138 | 139 | Usage: 140 | 141 | ```php 142 | Route::post('your-secret-path', function () { 143 | // TelegramBot::listen(); 144 | 145 | TelegramApi::send('sendMessage', [ 146 | 'chat_id' => Request::json('message.chat.id'), 147 | 'text' => 'Hey!', 148 | ]); 149 | }); 150 | ``` 151 | 152 | ## Examples 153 | 154 | Send a photo to your chat: 155 | 156 | ```php 157 | Route::post('photo', function (BlackRiver\TelegramBot\Client $client) { 158 | $client->send('sendPhoto', [ 159 | 'chat_id' => 'your-chat-id', 160 | 'photo' => fopen(storage_path('photo.png'), 'r'), 161 | ]); 162 | }); 163 | ``` 164 | 165 | Save incoming files: 166 | 167 | ```php 168 | Route::post('your-secret-path', function (Illuminate\Http\Request $request, BlackRiver\TelegramBot\Client $client) { 169 | $doc = $request->json('message.document'); 170 | 171 | $filename = $doc['file_name']; 172 | 173 | $file = $client->send('getFile', ['file_id' => $doc['file_id']]); 174 | 175 | $client->save($file['result']['file_path'], storage_path($filename)); 176 | }); 177 | ``` 178 | 179 | ## Extending 180 | 181 | To extend the Client, add a new macro to the `boot` method of your `app/Providers/AppServiceProvider.php`: 182 | 183 | ```php 184 | app('BlackRiver\TelegramBot\Client')->macro('sendUploadedPhoto', 185 | function ($chatId, \Illuminate\Http\UploadedFile $photo) { 186 | $saved = $photo->move(storage_path(), $photo->getClientOriginalName()); 187 | 188 | $this->send('sendPhoto', [ 189 | 'chat_id' => $chatId, 190 | 'photo' => fopen($saved->getRealPath(), 'r'), 191 | ]); 192 | } 193 | ); 194 | ``` 195 | 196 | Send an uploaded photo to your chat: 197 | 198 | ```php 199 | Route::post('upload', function (Illuminate\Http\Request $request, BlackRiver\TelegramBot\Client $client) { 200 | $client->sendUploadedPhoto('your-chat-id', $request->file('photo')); 201 | }); 202 | ``` 203 | 204 | ## Handle errors 205 | 206 | The Client uses [Guzzle Http Client](http://docs.guzzlephp.org/en/latest/) to interact with Telegram API, so you can handle [Guzzle Exceptions](http://docs.guzzlephp.org/en/latest/quickstart.html#exceptions): 207 | 208 | ```php 209 | try { 210 | $client->send('methodName', []); 211 | } catch (GuzzleHttp\Exception\ClientException $e) { 212 | // 400 level errors... 213 | } catch (GuzzleHttp\Exception\ServerException $e) { 214 | // 500 level errors... 215 | } catch (GuzzleHttp\Exception\RequestException $e) { 216 | // Connection timeout, DNS errors, etc. 217 | } 218 | ``` 219 | 220 | ## Lumen 221 | 222 | Require this package, with [Composer](https://getcomposer.org/): 223 | 224 | ```bash 225 | composer require black-river/telegram-bot 226 | ``` 227 | 228 | Add the service provider to the `Register Service Providers` section of your `bootstrap/app.php`: 229 | 230 | ```php 231 | $app->register(BlackRiver\TelegramBot\ServiceProvider::class); 232 | ``` 233 | 234 | Set the `APP_URL`, `TELEGRAM_TOKEN` and `TELEGRAM_CERTIFICATE` variables in your `.env`. 235 | 236 | Copy the vendor's `telegram.php` config file to your `config` directory: 237 | 238 | ```bash 239 | mkdir config/ && cp vendor/black-river/telegram-bot/config/telegram.php config/ 240 | ``` 241 | 242 | Define the default webhook route in your route file: 243 | 244 | ```php 245 | $app->post(config('telegram.token'), function (BlackRiver\TelegramBot\Bot $bot) { 246 | $bot->listen(); 247 | }); 248 | ``` 249 | 250 | ## License 251 | 252 | Laravel Telegram Bot is licensed under [The MIT License (MIT)](LICENSE). 253 | --------------------------------------------------------------------------------