This package helps you to easily use the Telegram Bot API in your Laravel project and utilize its features to build a great Telegram bot.
Please read The Telegram API documentation to gain dipper understanding about how to work with this package.
To install the package in your project, run the following command in your project root folder:
composer require mohammad-zarifiyan/laravel-telegram-bot:^6.8
If you would like to publish the configuration file, run the following command (optional):
php artisan vendor:publish --provider="MohammadZarifiyan\Telegram\Providers\TelegramServiceProvider" --tag="telegram-config"
To use Telegram bots, you must have an API key. Obtain your API key via @BotFather. Then, set your bot's API key. By default, you should add the following code to the config/services.php
file:
<?php
return [
// The rest of your code
'telegram' => [
'api-key' => env('TELEGRAM_API_KEY'),
],
];
Then add TELEGRAM_API_KEY
to your .env
file.
If you want to obtain the API Key through another way, such as a database, you can create your own repository instead of the above method. Simple create a class and implement MohammadZarifiyan\Telegram\Interfaces\ApiKeyRepository
in it. Then, in the telegram.php
configuration file, set the value of api-key-repository
to the address of your class.
app/Repositories/TelegramApiKeyRepository.php
file:
<?php
namespace App\Repositories;
use \MohammadZarifiyan\Telegram\Interfaces\ApiKeyRepository;
class TelegramApiKeyRepository implements ApiKeyRepository
{
public function get(): ?string
{
return '123456:abcdefg';// Return API Key
}
}
The telegram.php
configuration file:
<?php
return [
// The rest of the file
'api-key-repository' => \App\Repositories\TelegramApiKeyRepository::class,// Set your own custom repository
// The rest of the file
];
You can use an arbitrary endpoint to send HTTP requests to it instead of the default endpoint of Telegram bots (api.telegram.org).
By default, you should add the following code to the config/services.php
file.
<?php
return [
// The rest of your code
'telegram' => [
'endpoint' => env('TELEGRAM_ENDPOINT'),
],
];
Then add TELEGRAM_ENDPOINT
to your .env
file.
If you want to get the endpoint through another way, such as a database, you can create a repository for yourself instead of the above method. Just create a class and implement MohammadZarifiyan\Telegram\Interfaces\EndpointRepository
in it. Then, in the telegram.php
configuration file, set the value of endpoint-repository
to the address of your class.
app/Repositories/TelegramEndpointRepository.php
file:
<?php
namespace App\Repositories;
use \MohammadZarifiyan\Telegram\Interfaces\EndpointRepository;
class TelegramEndpointRepository implements EndpointRepository
{
public function get(): ?string
{
return 'https://example.com';// Return your own custom endpoint
}
}
The telegram.php
configuration file:
<?php
return [
// The rest of the file
'endpoint-repository' => \App\Repositories\TelegramEndpointRepository::class,// Set your own custom repository
// The rest of the file
];
You can also set your endpoint's tls certificate to be verified when you send a request to it. For this you can set TELEGRAM_VERIFY_ENDPOINT
in your .env
. It is recommended that the TELEGRAM_VERIFY_ENDPOINT
value is always true
.
The telegram.php
configuration file:
<?php
return [
// The rest of the file
'verify-endpoint' => (bool) env('TELEGRAM_VERIFY_ENDPOINT', true),
// The rest of the file
];
Use the perform
method to send a request to the Telegram API. The first parameter is the method and the second parameter is the data you want to send to the Telegram API.
Note: See available Telegram methods at this link
In the following example, Hello world is sent.
use \MohammadZarifiyan\Telegram\Facades\Telegram;
Telegram::perform('sendMessage', [
'text' => 'Hello world!',
'chat_id' => 1234
]);
Sometimes, you may wish to make multiple HTTP requests concurrently. In other words, you want several requests to be dispatched at the same time instead of issuing the requests sequentially.
Thankfully, you may accomplish this using the concurrent
method. The concurrent
method accepts a closure which receives an MohammadZarifiyan\Telegram\Interfaces\PendingRequestStack
instance, allowing you to easily add requests to the request pool for dispatching.
In the example below, three messages are sent to the user simultaneously.
use \MohammadZarifiyan\Telegram\Interfaces\PendingRequestStack;
use \MohammadZarifiyan\Telegram\Facades\Telegram;
$responses = Telegram::concurrent(fn (PendingRequestStack $pendingRequestStack) => [
$pendingRequestStack->add('sendMessage', [
'text' => 'Message 1',
'chat_id' => 1234
]),
$pendingRequestStack->add('sendMessage', [
'text' => 'Message 2',
'chat_id' => 1234
]),
$pendingRequestStack->add('sendMessage', [
'text' => 'Message 3',
'chat_id' => 1234
]),
]);
$result = $responses[0]->json('ok')
&& $responses[1]->json('ok')
&& $responses[2]->json('ok');
if ($result) {
// All messages have been sent successfully.
}
else {
// There was a problem sending some messages.
}
To send a notification via Telegram add the routeNotificationForTelegram
method to your notifiable model. Then, return the Telegram chat_id
of the notifiable.
public function routeNotificationForTelegram($notification)
{
// return telegram id
}
Return the telegram
channel from your notification's via
method.
public function via($notifiable): array
{
return ['telegram'];
}
Finally, add toTelegram
to your notification class and use MohammadZarifiyan\Telegram\TelegramRequestContent
to specify your Telegram notification data.
use \MohammadZarifiyan\Telegram\Interfaces\TelegramRequestContent as TelegramRequestContentInterface;
public function toTelegram($notifiable): TelegramRequestContentInterface
{
// Return an instance of \MohammadZarifiyan\Telegram\TelegramRequestContent
}
In the example below, Hello is sent to all users.
use App\Notifications\HelloNotification;
use App\Models\User;
use Illuminate\Support\Facades\Notification;
$users = User::all();
Notification::send($users, HelloNotification::class);
The User.php
file:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Notifications\Notifiable;
class User extends Model
{
use Notifiable;
protected $fillable = ['telegram_id'];
protected $casts = [
'telegram_id' => 'integer'
];
public function routeNotificationForTelegram($notification)
{
return $this->telegram_id;
}
}
The HelloNotification.php
file:
<?php
namespace App\Notifications;
use \Illuminate\Notifications\Notification;
use \MohammadZarifiyan\Telegram\Interfaces\TelegramRequestContent as TelegramRequestContentInterface;
use \MohammadZarifiyan\Telegram\TelegramRequestContent;
class PaymentPaidNotification extends Notification
{
public function via($notifiable): array
{
return ['telegram'];
}
public function toTelegram($notifiable): TelegramRequestContentInterface
{
return TelegramRequestContent::fresh()
->setMethod('sendMessage')
->setData([
'text' => 'Hello'
]);
}
}
If you want to add a reusable reply markup to your request payload, simply create a ReplyMarkup class. To create a ReplyMarkup class run the following command:
php artisan make:telegram-reply-markup <ReplyMarkupName>
php artisan make:telegram-reply-markup MyKeyboard
The app\Telegram\ReplyMarkups\MyKeyboard.php
file:
<?php
namespace App\Telegram\ReplyMarkups;
use \MohammadZarifiyan\Telegram\Interfaces\ReplyMarkup;
class MyKeyboard implements ReplyMarkup
{
public function __invoke(): array
{
return [
'resize_keyboard' => true,
'keyboard' => [
[
// This array contains a row
[
// This array contains a column
'text' => 'top left button'
],
[
// This array contains a column
'text' => 'top right button'
]
],
[
// This array contains a row
[
// This array contains a column
'text' => 'bottom left button'
],
[
// This array contains a column
'text' => 'bottom right button'
]
]
]
];
}
}
Here is the final code:
use \App\Telegram\ReplyMarkups\MyKeyboard;
use \MohammadZarifiyan\Telegram\Facades\Telegram;
Telegram::perform(
'sendMessage',
[
'text' => 'Hello world!',
'chat_id' => 1234
],
MyKeyboard::class
);
You can even use your ReplyMarkup
class inside notifications using setReplyMarkup
method.
<?php
namespace App\Notifications;
use \App\Telegram\ReplyMarkups\MyKeyboard;
use \Illuminate\Notifications\Notification;
use MohammadZarifiyan\Telegram\Interfaces\TelegramRequestContent as TelegramRequestContentInterface;
use \MohammadZarifiyan\Telegram\TelegramRequestContent;
class PaymentPaidNotification extends Notification
{
public function via($notifiable): array
{
return ['telegram'];
}
public function toTelegram($notifiable): TelegramRequestContentInterface
{
return TelegramRequestContent::fresh()
->setMethod('sendMessage')
->setData([
'text' => 'Hello'
])
->setReplyMarkup(MyKeyboard::class);
}
}
Use the MohammadZarifiyan\Telegram\Attachment
class to attach a file stored on the server to the request.
In the example below, The photo stored on the server will be sent in the chat.
use MohammadZarifiyan\Telegram\Facades\Telegram;
use MohammadZarifiyan\Telegram\Attachment;
$file_contents = file_get_contents('path/to/file.png');
$file_name = 'my-file.png';
Telegram::perform('sendPhoto', [
'photo' => new Attachment($file_contents, $file_name),
'chat_id' => 1234
]);
Sometimes, you may want to manipulate request before sending executing it.
First, create a class and implement \MohammadZarifiyan\Telegram\Interfaces\PendingRequest
. Then, retrieve \MohammadZarifiyan\Telegram\Interfaces\PendingRequest
in its constructor.
Next, in the telegram.php
configuration file, set the value of pending-request-manipulator
to the address of your class.
You can then manipulate the request received in the constructor as needed.
The telegram.php
configuration file:
<?php
return [
// The rest of the file
'pending-request-manipulator' => App\Telegram\PendingRequestManipulator::class,
// The rest of the file
];
The PendingRequestManipulator.php
file:
<?php
namespace App\Telegram;
use MohammadZarifiyan\Telegram\Interfaces\PendingRequest as PendingRequestInterface;
class PendingRequestManipulator implements PendingRequestInterface
{
public function __construct(public PendingRequestInterface $pendingRequest)
{
//
}
public function getUrl(): string
{
return $this->pendingRequest->getUrl();// You can change the URL of the request here.
}
public function getBody(): array
{
return $this->pendingRequest->getBody();// You can change the body of the request here.
}
public function getAttachments(): array
{
return $this->pendingRequest->getAttachments();// You can change the attachments of the request here.
}
}
Use the generateFileUrl
method to create a link for a file located on Telegram's servers.
use MohammadZarifiyan\Telegram\Facades\Telegram;
$response = Telegram::perform('getFile', [
'file_id' => 'abcdefg'// Your file id
]);
if ($response->json('ok')) {
$file_path = $response->json('result.file_path');
$file_url = Telegram::generateFileUrl($file_path);
// You can use $file_url to download the file.
}
else {
$error = $response->json('description');
// There was an error receiving file information. You can use $error to display the error.
}
Use the getBotId
method to get the ID of the bot whose API Key you set.
The .env
file:
TELEGRAM_API_KEY=123456:abcdefg
use MohammadZarifiyan\Telegram\Facades\Telegram;
echo Telegram::getBotId();// Output: 123456
It is strongly recommend to set a secure token for your bot to make sure that the updates are sent by Telegram webhook.
By default, you should add the following code to the config/services.php
file.
<?php
return [
// The rest of your code
'telegram' => [
'secure-token' => env('TELEGRAM_SECURE_TOKEN'),
],
];
Then add TELEGRAM_SECURE_TOKEN
to your .env
file.
Note: Only characters A-Z, a-z, 0-9, _ and - are allowed.
Note: After changing the secure token, you must set your bot's webhook again.
If you want to get the secure token through another way, such as a database, you can create a repository for yourself instead of the above method. Just create a class and implement MohammadZarifiyan\Telegram\Interfaces\SecureTokenRepository
in it. Then, in the telegram.php
configuration file, set the value of secure-token-repository
to the address of your class.
app/Repositories/TelegramSecureTokenRepository.php
file:
<?php
namespace App\Repositories;
use MohammadZarifiyan\Telegram\Interfaces\SecureTokenRepository;
class TelegramSecureTokenRepository implements SecureTokenRepository
{
public function get(): ?string
{
return 'abcdefg';// Return your secure token
}
}
The telegram.php
configuration file:
<?php
return [
// The rest of the file
'secure-token-repository' => \App\Repositories\TelegramSecureTokenRepository::class,// Set your own custom repository
// The rest of the file
];
Handling an update can happen in different steps depending on your needs. This method makes you able to implement all kinds of capabilities you need without considering the obstacles.
After creating a route, handles request using the handleRequest
method. You can also store requests in database and handle them later.
The api.php
file:
<?php
use MohammadZarifiyan\Telegram\Facades\Telegram;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
Route::post('telegram-update', function (Request $request) {
Telegram::handleRequest($request);
});
If you want to run some codes to modify the processing update or prevent processing it, you can use a Telegram middleware.
Telegram middlewares run before Command Handler and Breakers, If a Middleware returns anything other than an instance of MohammadZarifiyan\Telegram\Update
, an instance of MohammadZarifiyan\Telegram\TelegramMiddlewareFailedException
will be thrown and processing of the Telegram update will stop.
The method that is called in the Telegram middleware is based on the type of Telegram update that is being processed.
For example, if the update type is callback_query
, the method called will be handleCallbackQuery
.
If there is no specific method for handling the Telegram update based on its type, a method named handle
is called in the Telegram middleware.
Both handle
and handleCallbackQuery
will receive an instance of MohammadZarifiyan\Telegram\Update
as the first argument.
Note that handleCallbackQuery
is used as an example, and the method name will vary with different Telegram updates based on the update type.
To generate a new Telegram middleware, run the following command.
php artisan make:telegram-middleware <MiddlewareName>
You should add your Telegram middleware in telegram.php
configuration file.
<?php
return [
// The rest of the file
'middlewares' => [
App\Telegram\Middlewares\YourMiddleware::class,
],
// The rest of the file
];
Telegram middlewares will be executed in the same order as you place them in the telegram.php
configuration file.
The commands that users send to your bot must be handled by a Command Handler.
The Telegram Command handler will run after all Telegram middlewares have run successfully.
If no Command handler is found for the command the user submitted, an instance of MohammadZarifiyan\Telegram\TelegramCommandNotFoundException
is thrown.
You can disable throwing exceptions by setting allow-incognito-command
to true
in the telegram.php
configuration file.
Additionally, you can check with $update->isCommand()
whether the Telegram update was created due to a bot command or not.
To generate a new Command handler, run the following command.
php artisan make:telegram-command-handler <CommandHandlerName>
You should add your Telegram Command handler to the telegram.php
configuration file.
<?php
return [
// The rest of the file
'command_handlers' => [
App\Telegram\CommandHandlers\YourCommandHandler::class,
],
// The rest of the file
];
A command handler must implement the MohammadZarifiyan\Telegram\Interfaces\CommandHandler
interface and will look like this:
<?php
namespace App\Telegram\CommandHandlers;
use MohammadZarifiyan\Telegram\Interfaces\CommandHandler;
use MohammadZarifiyan\Telegram\Update;
class StartCommandHandler implements CommandHandler
{
/**
* The signature(s) of the Telegram bot command that can be handled by current CommandHandler.
*
* @param Update $update
* @return string|array
*/
public function getSignature(Update $update): string|array
{
return 'start';
}
/**
* Handles the Telegram command.
*
* @param Update $update
*/
public function handle(Update $update)
{
// Handle command here
}
}
The getSignature
method must return the name of the command or commands that the handle
method can handle.
Sometimes a Telegram update comes with a command parameter.
You can access Telegram bot command data by calling the toCommand
method. Then, you will receive an instance of MohammadZarifiyan\Telegram\Interfaces\Command
.
For example, if your Telegram bot username is MyAwesomeBot
, then you can add a parameter to your /start
command:
https://t.me/MyAwesomeBot?start=abc
$command = $update->toCommand();
$command->getSignature(); // Returns 'start'
$command->getValue(); // Returns 'abc'
Sometimes you may want to create a command handler without specifying the signature. This feature is mostly used for commands that are case-insensitive or commands with dynamic signature. For this purpose, you can create a class and implement MohammadZarifiyan\Telegram\Interfaces\AnonymousCommandHandler
in it. Then add the address of your class to command_handlers
in the telegram.php
configuration file.
In anonymous command handlers, there is a matchesSignature
method, in which you should check the command match.
<?php
namespace App\Telegram\CommandHandlers;
use MohammadZarifiyan\Telegram\Interfaces\AnonymousCommandHandler;
use MohammadZarifiyan\Telegram\Update;
class MyAnonymousCommandHandler implements AnonymousCommandHandler
{
/**
* Checks whether the current CommandHandler can process the command.
*
* @param Update $update
* @return bool
*/
public function matchesSignature(Update $update): bool
{
$signature = $update->toCommand()->getSignature();
return $signature === 'start';
}
/**
* Handles the Telegram command.
*
* @param Update $update
*/
public function handle(Update $update)
{
// Handle command here
}
}
If the Update is not handled by a CommandHandler
, it continues its path and reaches the Breakers. A Breaker is a class that can handle the request based on its type, but unlike Telegram Middlewares, it cannot modify the update.
The method that is called when a Breaker is executed is exactly like a Telegram middleware.
A Breaker should extend MohammadZarifiyan\Telegram\Breaker
.
If a Breaker returns true
, update processing stops; otherwise, the next breakers will be executed. You can also stop processing the update with the stop
method in your Breaker. If you do not want to stop processing the update through subsequent Breakers or stages, you should return false
or anything other than true
. Similarly, you can achieve the same result by calling the continue
method in your Telegram Breaker.
Note that having too many Telegram Middlewares and Breakers can reduce the performance of your Telegram bot.
To generate a new Breaker, run the following command.
php artisan make:telegram-breaker <BreakerName>
You should add your Telegram Breaker to the telegram.php
configuration file.
<?php
return [
// The rest of the file
'breakers' => [
App\Telegram\Breakers\YourBreaker::class,
],
// The rest of the file
];
In the example below, we handle all callback query updates related to an inline button to cancel sending notifications to all users.
<?php
namespace App\Telegram\Breakers;
use MohammadZarifiyan\Telegram\Breaker;
use MohammadZarifiyan\Telegram\Update;
class CancelBulkNotificationBreaker extends Breaker
{
public function handleCallbackQuery(Update $update): bool
{
if ($update->input('callback_query.data') === 'cancel-sending-all-notifications') {
// Cancel sending notifications
/*
* Stop processing request by other Telegram breakers,
* Because we already handled the Update and the is no need to continue processing this Update in our application
*/
return $this->stop();
}
else {
/*
* Continue processing the Telegram Update using other Telegram Breakers or Telegram Stages,
* Because this Telegram breaker could was not able to handle the Telegram update
*/
return $this->continue();
}
}
}
You may want to access something like Illuminate\Support\Facades\Request::user()
in your application to interact with the database regarding the current update. This functionality is provided for you in this package. Simply create a class called GainerResolver
in app/Telegram
and implement the MohammadZarifiyan\Telegram\Interfaces\GainerResolver
interface in it. Then set the name of the class you created in the telegram.php
configuration file.
The telegram.php
configuration file:
<?php
return [
// The rest of the file
'gainer-resolver' => \App\Telegram\GainerResolver::class,// Set your own class name
// The rest of the file
];
By using $update->gainer()
, you can access the value returned by the handle
method in GainerResolver.php
throughout your application.
The app/Models/User.php
file:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
protected $fillable = ['telegram_id'];
protected $casts = [
'telegram_id' => 'integer',
];
}
The app/Telegram/GainerResolver.php
file:
<?php
use App\Models\User;
use MohammadZarifiyan\Telegram\Update;
use MohammadZarifiyan\Telegram\Interfaces\GainerResolver as GainerResolverInterface;
class GainerResolver implements GainerResolverInterface
{
public function handle(Update $update)
{
if ($update->type() === 'message') {
return User::firstOrCreate([
'telegram_id' => $update->input('message.from.id')
]);
}
return null;
}
}
use MohammadZarifiyan\Telegram\Facades\Telegram;
use App\Models\User;
$update = Telegram::getUpdate();
$gainer = $update->gainer();
if ($gainer instanceof User) {
echo 'The user became a member of the bot on date ' . $gainer->created_at;
}
else {
echo 'The $gainer value is null.';
}
Some bots have an interactive mode where they allow users to access new features upon receiving a new Telegram update. For example, when a user clicks a button on the keyboard, a new set of buttons is displayed, which the user can interact with only after clicking the initial button. This feature is commonly seen in bots that guide users through stages of information gathering. To implement this, you need to save a stage that checks the user's next update, so that when the next update arrives, the corresponding stage code executes. Fortunately, this package simplifies this process significantly. The Stage class has methods similar to those in Middleware and Breaker classes. These methods handle bot updates; for instance, the handleMessage
method manages updates triggered by sending messages in the bot's chat. To utilize this feature, define a model for your Gainer, add a stage column
to your database table, and store the fully qualified class name that should handle the next update in the stage
column. Finally, implement the MohammadZarifiyan\Telegram\Interfaces\Gainer
interface in your Gainer model.
To create a stage, run the following command:
php artisan make:telegram-stage <StageName>
The above command creates a file in the path app/Telegram/Stages
.
You can validate updates in the update handler methods within the stage. Update validation works similar to FormRequest
in Laravel. To validate updates, you must create a class that extends MohammadZarifiyan\Telegram\FormUpdate
and use it as the first parameter in the update handler method within the stage class. If validation fails, an exception of type MohammadZarifiyan\Telegram\Exceptions\TelegramValidationException
is thrown. You can catch TelegramValidationException
in the app/Exceptions/Handler.php
file and send validation errors to the user.
You can also use the authorize
method. If the output of this method is false
, an exception of type MohammadZarifiyan\Telegram\Exceptions\TelegramAuthorizationException
will be thrown. You can catch TelegramAuthorizationException
in the app/Exceptions/Handler.php
file and send an authorization error to the user.
Run the following command to create a new update:
php artisan make:telegram-update <UpdateName>
This command creates a file at the path app/Telegram/Updates
.
In the following example, the bot asks the user for their age, expecting a number between 1 and 100. If the user sends anything other than this number, the bot will respond with a validation error message.
Users migration:
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('telegram_id');
$table->string('stage')->nullable();
$table->unsignedTinyInteger('age')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('users');
}
};
The app\Models\User.php
file:
<?php
namespace App\Models;
use MohammadZarifiyan\Telegram\Interfaces\Gainer as GainerInterface;
use MohammadZarifiyan\Telegram\Traits\Gainer;
use Illuminate\Database\Eloquent\Model;
class User extends Model implements GainerInterface
{
use Gainer;
protected $fillable = [
'telegram_id',
'age',
];
protected $casts = [
'telegram_id' => 'integer',
'age' => 'integer',
];
}
App\Telegram\Stages\Age.php
file:
<?php
namespace App\Telegram\Stages;
use App\Telegram\Updates\AgeUpdate;
use MohammadZarifiyan\Telegram\Facades\Telegram;
class Age
{
public function handleMessage(AgeUpdate $update): void
{
$update->gainer()->update([
'stage' => null,
'age' => $update->integer('message.text')
]);
Telegram::perform('sendMessage', [
'text' => 'Your age has been saved in the database.',
'chat_id' => $update->integer('message.chat.id'),
]);
}
}
The app\Telegram\Updates\AgeUpdate.php
file:
<?php
namespace App\Telegram\Updates;
use MohammadZarifiyan\Telegram\FormUpdate;
class AgeUpdate extends FormUpdate
{
public function rules(): array
{
return [
'message.text' => ['bail', 'required', 'integer', 'between:1,200']
];
}
public function attributes(): array
{
return [
'message.text' => 'age'
];
}
}
use MohammadZarifiyan\Telegram\Facades\Telegram;
use App\Telegram\Stages\Age;
$gainer = Telegram::getUpdate()->gainer();
$gainer->update(['stage' => Age::class]);
Telegram::perform('sendMessage', [
'chat_id' => $gainer->telegram_id,
'text' => 'How old are you?'
]);
By viewing the chart below, you will better understand the update processing.