Handlers
Concept
The framework provides to you a nice API event-like to handling incoming updates:
use SergiX44\Nutgram\Nutgram;
$bot = new Nutgram($_ENV['TOKEN']);
$bot->onMessage(function (Nutgram $bot) {
$bot->sendMessage('You sent a message!');
});
$bot->run();
Every ->on*
handler is called based on the update type defined
in Telegram's update object, there are also some specific handlers, which
may respond based on specific patterns or types of messages.
As you can also see from the example above, some required parameters (like the chat_id
) can be omitted, while the
bot is in the context of managing an update, so those fields are automatically extracted from the current update.
Of course, you can override them at any time, simply by specifying them in the $opt
array.
Available Handlers
Here a full list of all the handler that listens to specific type of updates:
Update Handlers
onMessage($callable)
Handles any incoming message.
onMessageType(string $type, $callable)
Handles messages defined by type.
onEditedMessage($callable)
Handles any incoming edited message.
onChannelPost($callable)
Handles any message posted in a channel where the bot is administrator.
onEditedChannelPost($callable)
Handles any message edited in a channel where the bot is administrator.
onInlineQuery($callable)
Handles any incoming inline query.
onChosenInlineResult($callable)
Handles any incoming chosen inline result.
onCallbackQuery($callable)
Handles any incoming callback query.
onCallbackQueryData(string $pattern, $callable)
Handles callback query with a specific pattern, similar to `onText`.
onShippingQuery($callable)
Handles any incoming shipping query.
onPreCheckoutQuery($callable)
Handles any incoming pre checkout query.
onPreCheckoutQueryPayload(string $pattern, $callable)
Handles any incoming pre checkout query with a specific payload pattern.
onPoll($callable)
Handles any incoming poll.
onUpdatePoll($callable)
Handles any incoming poll.
onPollAnswer($callable)
Handles any incoming poll answer.
onMyChatMember($callable)
Handles any chat member when updated.
onChatMember($callable)
Handles any chat member in other chats when updated.
onChatJoinRequest($callable)
Handles any chat join request.
Message Handlers
onText(string $pattern, $callable)
Handles text messages that match the given pattern (regex or parameters).
onCommand(string $command, $callable)
Handles text messages that begin with `/`.
Automatically parses commands like `cmd@botname`.
onAnimation($callable)
Handles animation messages
onAudio($callable)
Handles audio messages.
onDocument($callable)
Handles document messages.
onPhoto($callable)
Handles photo messages.
onSticker($callable)
Handles sticker messages.
onVideo($callable)
Handles video messages.
onVideoNote($callable)
Handles video_note messages.
onVoice($callable)
Handles voice messages.
onContact($callable)
Handles contact messages.
onDice($callable)
Handles dice messages.
onGame($callable)
Handles game messages.
onMessagePoll($callable)
Handles poll messages.
onVenue($callable)
Handles venue messages.
onLocation($callable)
Handles location messages.
onNewChatMembers($callable)
Handles new_chat_members messages.
onLeftChatMember($callable)
Handles left_chat_member messages.
onNewChatTitle($callable)
Handles new_chat_title messages.
onNewChatPhoto($callable)
Handles new_chat_photo messages.
onDeleteChatPhoto($callable)
Handles delete_chat_photo messages.
onGroupChatCreated($callable)
Handles group_chat_created messages.
onSupergroupChatCreated($callable)
Handles supergroup_chat_created messages.
onChannelChatCreated($callable)
Handles channel_chat_created messages.
onMessageAutoDeleteTimerChanged($callable)
Handles message_auto_delete_timer_changed messages.
onMigrateToChatId($callable)
Handles migrate_to_chat_id messages.
onMigrateFromChatId($callable)
Handles migrate_from_chat_id messages.
onPinnedMessage($callable)
Handles pinned_message messages.
onInvoice($callable)
Handles invoice messages.
onSuccessfulPayment($callable)
Handles successful_payment messages.
onSuccessfulPaymentPayload(string $pattern, $callable)
Handles successful_payment messages with a specific payload pattern.
onConnectedWebsite($callable)
Handles connected_website messages.
onPassportData($callable)
Handles any incoming passport data messages.
onProximityAlertTriggered($callable)
Handles proximity_alert_triggered messages.
onForumTopicCreated($callable)
Handles any forum topic created.
onForumTopicClosed($callable)
Handles any forum topic closed.
onForumTopicReopened($callable)
Handles any forum topic reopened.
onVideoChatScheduled($callable)
Handles video_chat_scheduled messages.
onVideoChatStarted($callable)
Handles video_chat_started messages.
onVideoChatEnded($callable)
Handles video_chat_ended messages.
onVideoChatParticipantsInvited($callable)
Handles video_chat_participants_invited messages.
onWebAppData($callable)
Handles web_app_data messages.
Special Handlers
fallback($callable)
This handler if defined will be called if no handler, specific or generic, has been found for the current update.
fallbackOn(string $type, $callable)
This handler has the same behavior as the previous one, but allows you to put a filter on the type of updates it can handle.
fallbackOn(string $type, $callable)
This handler has the same behavior as the previous one, but allows you to put a filter on the type of updates it can handle.
onException($callable)
This handler will be called whenever the handling of an update throws an exception, if undefined the exception will not be caught.
Check the next paragraph for more details.
onApiError($callable)
This handler will be called every time a call to Telegram's api fails, if undefined the exception will not be caught.
Check the next paragraph for more details.
beforeApiRequest($callable)
This handler will be called before every call to Telegram's api, if undefined the call will be made without any modification.
Check the next paragraph for more details.
afterApiRequest($callable)
This handler will be called after every call to Telegram's api, if undefined the call will be made without any modification.
Check the next paragraph for more details.
Specific & Special Handlers
onCommand
For the implicit style see the registerCommand
section.
It's possible to handle to specific commands, also with named parameters:
use SergiX44\Nutgram\Nutgram;
$bot = new Nutgram($_ENV['TOKEN']);
// Called when a message contains the command "/start someParameter"
$bot->onCommand('start {parameter}', function (Nutgram $bot, $parameter) {
$bot->sendMessage("The parameter is {$parameter}");
});
// Called on command "/help"
$bot->onCommand('help', function (Nutgram $bot) {
$bot->sendMessage('Help me!');
});
$bot->run();
registerCommand
A different and clean way to register commands implicitly.
// 1. create a command class like "StartCommand.php":
namespace App\Telegram\Commands;
use SergiX44\Nutgram\Handlers\Type\Command;
use SergiX44\Nutgram\Nutgram;
class StartCommand extends Command
{
protected string $command = 'start';
protected ?string $description = 'A lovely start command';
public function handle(Nutgram $bot): void
{
$bot->sendMessage('Hello there!');
}
}
// 2. Register StartCommand inside Nutgram
$bot->registerCommand(StartCommand::class);
onText
For text messages, is possible also put parameters to match a regex, or to match part of text:
use SergiX44\Nutgram\Nutgram;
$bot = new Nutgram($_ENV['TOKEN']);
// ex. called when a message contains "My name is Mario"
$bot->onText('My name is {name}', function (Nutgram $bot, $name) {
$bot->sendMessage("Hi {$name}");
});
// ex. called when a message contains "I want 6 pizzas"
$bot->onText('I want ([0-9]+) pizzas', function (Nutgram $bot, $n) {
$bot->sendMessage("You will get {$n} pizzas!");
});
$bot->onText('I want ([0-9]+) portions of (pizza|cake)', function (Nutgram $bot, $amount, $dish) {
$bot->sendMessage("You will get {$amount} portions of {$dish}!");
});
$bot->run();
onMessageType
It's like the onMessage
handler, but you can specify to which type of message you should handle:
use SergiX44\Nutgram\Nutgram;
use SergiX44\Nutgram\Telegram\Attributes\MessageTypes;
$bot = new Nutgram($_ENV['TOKEN']);
// Called only when you send a photo
$bot->onMessageType(MessageTypes::PHOTO, function (Nutgram $bot) {
$photos = $bot->message()->photo;
$bot->sendMessage('Nice pic!');
});
// Called only when you send an audio file
$bot->onMessageType(MessageTypes::AUDIO, function (Nutgram $bot) {
$audio = $bot->message()->audio;
$bot->sendMessage('I love this song!');
});
$bot->run();
You can see all the constants, in the MessageTypes::class.
onCallbackQueryData
It's like the onText
handler, but you can specify to which data
contained in CallbackQuery to handle:
use SergiX44\Nutgram\Nutgram;
$bot = new Nutgram($_ENV['TOKEN']);
$bot->onCommand('start', function (Nutgram $bot) {
$bot->sendMessage('Choose an option:', [
'reply_markup' => InlineKeyboardMarkup::make()->addRow(
InlineKeyboardButton::make('One', callback_data: 'one'),
InlineKeyboardButton::make('Two', callback_data: 'two'),
InlineKeyboardButton::make('Cancel', callback_data: 'cancel'),
)
]);
});
$bot->onCallbackQueryData('one|two', function (Nutgram $bot) {
$bot->sendMessage('Nice!');
$bot->answerCallbackQuery();
});
$bot->onCallbackQueryData('cancel', function (Nutgram $bot) {
$bot->sendMessage('Canceled!');
$bot->answerCallbackQuery();
});
$bot->run();
The same thing also applies for custom parameters:
use SergiX44\Nutgram\Nutgram;
$bot = new Nutgram($_ENV['TOKEN']);
$bot->onCommand('start', function (Nutgram $bot) {
$bot->sendMessage('Choose an option:', [
'reply_markup' => InlineKeyboardMarkup::make()->addRow(
InlineKeyboardButton::make('One', callback_data: 'number 1'),
InlineKeyboardButton::make('Two', callback_data: 'number 2'),
InlineKeyboardButton::make('Cancel', callback_data: 'cancel'),
)
]);
});
$bot->onCallbackQueryData('number {param}', function (Nutgram $bot, $param) {
$bot->sendMessage($param); // 1 or 2
$bot->answerCallbackQuery();
});
$bot->onCallbackQueryData('cancel', function (Nutgram $bot) {
$bot->sendMessage('Canceled!');
$bot->answerCallbackQuery();
});
$bot->run();
fallback
This handler, if defined, will be called every time an Update
will not match any other defined handler:
use SergiX44\Nutgram\Nutgram;
$bot = new Nutgram($_ENV['TOKEN']);
// But the user send something else than /start
$bot->onCommand('start', function (Nutgram $bot) {
$bot->sendMessage('Started!');
});
$bot->fallback(function (Nutgram $bot) {
$bot->sendMessage('Sorry, I don\'t understand.');
});
$bot->run();
fallbackOn
This has the same behaviour of the fallback
, but allow you to define handlers based on the Update
type:
use SergiX44\Nutgram\Nutgram;
use SergiX44\Nutgram\Telegram\Attributes\UpdateTypes;
$bot = new Nutgram($_ENV['TOKEN']);
// define some handlers ...
// Called only for unmatched callback queries
$bot->fallbackOn(UpdateTypes::CALLBACK_QUERY, function (Nutgram $bot) {
$bot->answerCallbackQuery();
$bot->editMessageReplyMarkup([/* ... */]);
});
// Called only for unmatched messages
$bot->fallbackOn(UpdateTypes::MESSAGE, function (Nutgram $bot) {
$bot->sendMessage('Sorry, I don\'t understand.');
});
$bot->run();
You can see all the constants, in the UpdateTypes::class.
onException
This handler, if defined, will be called if something on your other handlers goes wrong, passing the $exception
as
second argument:
use SergiX44\Nutgram\Nutgram;
$bot = new Nutgram($_ENV['TOKEN']);
// define some handlers ...
// and exception is thrown...
$bot->onMessage(function (Nutgram $bot) {
// do stuff
throw new Exception('Oh no!');
});
// ... and passed to the exception handler
$bot->onException(function (Nutgram $bot, \Throwable $exception) {
echo $exception->getMessage(); // Oh no!
error_log($exception);
$bot->sendMessage('Whoops!');
});
$bot->run();
The onException
handler supports also different callbacks based on the exception instance:
use SergiX44\Nutgram\Nutgram;
$bot = new Nutgram($_ENV['TOKEN']);
// and exception is thrown...
$bot->onMessage(function (Nutgram $bot) {
if (random_int(0, 1)) {
throw new InvalidArgumentException();
}
throw new Exception('Oh no!');
});
$bot->onException(InvalidArgumentException::class, function (Nutgram $bot, InvalidArgumentException $exception) {
//
});
$bot->onException(Exception::class, function (Nutgram $bot, Exception $exception) {
//
});
$bot->run();
onApiError
The same concept of the onException
, but for outgoing requests:
- Chat not found
- Too many requests
use SergiX44\Nutgram\Nutgram;
use SergiX44\Nutgram\Telegram\Exceptions\TelegramException;
$bot = new Nutgram($_ENV['TOKEN']);
$bot->onMessage(function (Nutgram $bot) {
$bot->sendMessage('Invalid call!', ['chat_id' => null]);
});
$bot->onApiError(function (Nutgram $bot, TelegramException $exception) {
echo $exception->getMessage(); // Bad Request: chat not found
echo $exception->getCode(); // 400
error_log($exception);
});
$bot->run();
use SergiX44\Nutgram\Nutgram;
use SergiX44\Nutgram\Telegram\Exceptions\TelegramException;
$bot = new Nutgram($_ENV['TOKEN']);
$bot->onMessage(function (Nutgram $bot) {
foreach(range(1,200) as $i){
$bot->sendMessage('Too many calls!');
}
});
$bot->onApiError(function (Nutgram $bot, TelegramException $exception) {
echo $exception->getMessage(); // Too Many Requests: retry after 14
echo $exception->getCode(); // 429
echo $exception->getParameters(); // ['retry_after' => 14]
echo $exception->getParameter('retry_after'); // 14
echo $exception->hasParameter('retry_after'); // true
error_log($exception);
});
$bot->run();
Like the onException
, the handler support a regex matching the text returned by the telegram api:
use SergiX44\Nutgram\Nutgram;
use SergiX44\Nutgram\Telegram\Exceptions\TelegramException;
$bot = new Nutgram($_ENV['TOKEN']);
$bot->onMessage(function (Nutgram $bot) {
$bot->sendMessage('Invalid call!', ['chat_id' => null]);
});
$bot->onApiError('chat not found', function (Nutgram $bot, TelegramException $exception) {
//
});
$bot->onApiError('user(.*)deactivated', function (Nutgram $bot, TelegramException $exception) {
//
});
$bot->run();
beforeApiRequest
This handler, if defined, will be called before every outgoing request, passing the $request
as second argument:
use SergiX44\Nutgram\Nutgram;
$bot = new Nutgram($_ENV['TOKEN']);
$bot->beforeApiRequest(function (Nutgram $bot, array $request) {
// print a value from the request
echo $request['json']['text'];
// modify the request
$request['json']['text'] = 'Modified!';
return $request;
});
$bot->run();
afterApiRequest
This handler, if defined, will be called after every outgoing request, passing the $response
as second argument:
use SergiX44\Nutgram\Nutgram;
$bot = new Nutgram($_ENV['TOKEN']);
$bot->afterApiRequest(function (Nutgram $bot, object $response) {
// print a value from the response
echo $response->result->text;
// modify the response
$response->result->text = 'Modified!';
return $response;
});
$bot->run();
Handlers Priority
If you declare handlers, they will block the execution of handlers.
Please keep this in mind when using Nutgram.
Some examples to clarify this:
- Text
- SuccessfulPayment
- CallbackQuery
- PreCheckoutQuery
// Input text: something
// Specific handler: ✅ it will be executed!
$bot->onText('something', SomethingHandler::class);
// Generic handler: ❌ it will not be executed!
$bot->onMessageType(MessageTypes::TEXT, MessageTypeTextHandler::class);
// Generic handler: ❌ it will not be executed!
$bot->onMessage(MessageHandler::class);
// Input payload: donation
// Specific handler: ✅ it will be executed!
$bot->onSuccessfulPaymentPayload('donation', DonationHandler::class);
// Generic handler: ❌ it will not be executed!
$bot->onSuccessfulPayment(SuccessfulPaymentHandler::class);
// Input payload: mydata
// Specific handler: ✅ it will be executed!
$bot->onCallbackQueryData('mydata', MyDataHandler::class);
// Generic handler: ❌ it will not be executed!
$bot->onCallbackQuery(CallbackQueryHandler::class);
// Input payload: mydata
// Specific handler: ✅ it will be executed!
$bot->onPreCheckoutQueryPayload('mydata', MyDataHandler::class);
// Generic handler: ❌ it will not be executed!
$bot->onPreCheckoutQuery(PreCheckoutQueryHandler::class);
Automatically register bot commands
The framework can also automatically set the bot commands for you, if you configure the description on it:
- onCommand Handler
- Command Class
use SergiX44\Nutgram\Nutgram;
$bot = new Nutgram($_ENV['TOKEN']);
// Called on command "/start"
// It's possible to set a description for each command
// this WILL be automatically registered
$bot->onCommand('start', function (Nutgram $bot) {
return $bot->sendMessage('Hello, world!');
})->description('The start command!');
// Called on command "/secret"
// this WILL NOT be automatically registered
$bot->onCommand('secret', function (Nutgram $bot) {
return $bot->sendMessage('Shhh');
});
// 1. create a command class like "StartCommand.php":
namespace App\Telegram\Commands;
use SergiX44\Nutgram\Handlers\Type\Command;
use SergiX44\Nutgram\Nutgram;
class StartCommand extends Command
{
// Called on command "/start"
protected string $command = 'start';
// It's possible to set a description for the current command
// this WILL be automatically registered
protected ?string $description = 'A lovely start command';
public function handle(Nutgram $bot): void
{
$bot->sendMessage('Hello there!');
}
}
// 2. Register StartCommand inside Nutgram
$bot->registerCommand(StartCommand::class);
You can also explicitly set a command as hidden overriding the isHidden
method adding your logic:
namespace App\Telegram\Commands;
use SergiX44\Nutgram\Handlers\Type\Command;
use SergiX44\Nutgram\Nutgram;
class HiddenCommand extends Command
{
protected string $command = 'hidden';
protected ?string $description = 'A super secret command';
public function handle(Nutgram $bot): void
{
$bot->sendMessage('Shhh!');
}
public function isHidden(): bool
{
return true;
}
}
Scope support
Command Scopes allows for more granular control over which users or groups can see specific commands in the bot.
However, the support for language_code
in command scope is not supported.
If the language_code
support is required, commands must be manually set using the setMyCommands
method.
How to set a scope
- onCommand Handler
- Command Class
Just use the scope
method:
//single scope
$bot
->onCommand('start', StartCommand::class)
->description('Start command')
->scope(new BotCommandScopeAllPrivateChats);
// multiple scope (multiple calls)
$bot
->onCommand('start', StartCommand::class)
->description('Start command')
->scope(new BotCommandScopeAllPrivateChats)
->scope(new BotCommandScopeAllGroupChats);
// multiple scope (as array)
$bot
->onCommand('start', StartCommand::class)
->description('Start command')
->scope([
new BotCommandScopeAllPrivateChats,
new BotCommandScopeAllGroupChats,
]);
Just override the scopes
method:
class StartCommand extends Command
{
protected string $command = 'start';
protected ?string $description = 'Start command';
public function scopes(): array
{
return [
new BotCommandScopeAllPrivateChats,
];
}
public function handle(Nutgram $bot): void
{
$bot->sendMessage('Hello there!');
}
}
Available scopes
new BotCommandScopeDefault();
new BotCommandScopeAllChatAdministrators();
new BotCommandScopeAllGroupChats();
new BotCommandScopeAllPrivateChats();
new BotCommandScopeChat(chat_id: 123);
new BotCommandScopeChatAdministrators(chat_id: 123);
new BotCommandScopeChatMember(chat_id: 123, user_id: 987);
For a description of each scope, please refer to the official documentation page.
Registering commands
To register the commands, you can use the registerMyCommands
method, which will automatically call the setMyCommands
method:
$bot->registerMyCommands();
And the result will be:
Please do not use the registerMyCommands
method in the same file where you register your bot handlers when using the Webhook running mode,
because the bot will register your commands on every webhook call, causing a lot of useless requests. Just call the method manually or after a deploy.
OOP
So far you have seen handlers defined only as closures. But the framework, any definition that accepts a $callable
, also
accepts a class-method definition, or invokable classes, like this:
use SergiX44\Nutgram\Nutgram;
class MyCommand {
public function __invoke(Nutgram $bot, $param)
{
//do stuff
}
}
use SergiX44\Nutgram\Nutgram;
$bot = new Nutgram($_ENV['TOKEN']);
// all of those are valid definitions:
$bot->onCommand('start {param}', MyCommand::class); // with __invoke
$bot->onCommand('start1 {param}', [MyCommand::class, 'handle']); // class-method
$bot->onCommand('start2 {param}', [$instance, 'handle']); // instance-method
$bot->run();
Update Helpers
When dealing with updates, sometimes you may need to access data that is nested in the update structure, which can be tedious and produce a lot of boilerplate, since the same objects can often be nested in other objects, depending on the type of update. For this reason, the framework provides a number of support methods to quickly access the most used data, no matter the update type, like this:
use SergiX44\Nutgram\Nutgram;
$bot = new Nutgram($_ENV['TOKEN']);
$bot->onCommand('help', function (Nutgram $bot) {
// Get the Message object
$bot->message();
// Access the Chat object
$bot->chat();
});
$bot->onCommand('my_chat', function (Nutgram $bot) {
$bot->sendMessage('Your chat id is ' . $bot->chatId());
});
$bot->run();
Available helpers
Method | Return type | Description |
---|---|---|
update() | ?Update | The current Update object. |
chatId() | ?int | The current chat_id if available, null otherwise. |
chat() | ?Chat | The current Chat if available, null otherwise. |
userId() | ?int | The current from .id if available, null otherwise. |
user() | ?User | The current User (from Telegram's object) if available, null otherwise. |
messageId() | ?int | The current message .message_id if available, null otherwise. |
message() | ?Message | The current Message if available, null otherwise. |
isCallbackQuery() | bool | If the current update contains a callback_query . |
callbackQuery() | ?CallbackQuery | The current CallbackQuery if available, null otherwise. |
isInlineQuery() | bool | If the current update contains an inline_query . |
inlineQuery() | ?InlineQuery | The current InlineQuery if available, null otherwise. |
chosenInlineResult() | ?ChosenInlineResult | The current ChosenInlineResult if available, null otherwise. |
shippingQuery() | ?ShippingQuery | The current ShippingQuery if available, null otherwise. |
isPreCheckoutQuery() | bool | If the current update contains a pre_checkout_query . |
preCheckoutQuery() | ?PreCheckoutQuery | The current PreCheckoutQuery if available, null otherwise. |
poll() | ?Poll | The current Poll if available, null otherwise. |
pollAnswer() | ?PollAnswer | The current PollAnswer if available, null otherwise. |
isMyChatMember() | bool | If the current ChatMemberUpdated is in the my_chat_member . |
chatMember() | ?ChatMemberUpdated | The current ChatMemberUpdated if available, null otherwise. |
Persisting data
The framework gives you the ability to store data based on the update context: you can store data as globally or per-user:
use SergiX44\Nutgram\Nutgram;
$bot = new Nutgram($_ENV['TOKEN']);
$bot->setGlobalData('mykey', 'Hi!');
$bot->setUserData('mykey', 'Ciao!', $userId);
$value = $bot->getGlobalData('mykey'); // Hi!
$value = $bot->getUserData('mykey', $userId); // Ciao!
// when used inside a context, the $userId can be omitted.
$bot->onCommand('help', function (Nutgram $bot) {
$bot->setUserData('mykey', 'called help!');
$value = $bot->getUserData('mykey'); // called help!
});
$bot->run();
If you need to persist data on disk, be sure to choose an appropriate cache adapter!
Available methods
getGlobalData($key, $default = null)
Returns the data associated to the
$key
, if null $default
is returned.setGlobalData($key, $value, DateInterval|int|null $ttl = null)
Returns
bool
deleteGlobalData($key)
Returns
bool
getUserData($key, ?int $userId = null, $default = null)
Returns the data associated to the
$key
, if null $default
is returned.setUserData($key, $value, ?int $userId = null, DateInterval|int|null $ttl = null)
Returns
bool
deleteUserData($key, ?int $userId = null)
Returns
bool