Skip to main content
Version: 3.x

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:

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();

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

caution

If you declare handlers, they will block the execution of handlers.
Please keep this in mind when using Nutgram.

Some examples to clarify this:

// 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);

Automatically register bot commands

The framework can also automatically set the bot commands for you, if you configure the description on it:

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');
});

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

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,
]);

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:

commands

caution

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

MethodReturn typeDescription
update()?UpdateThe current Update object.
chatId()?intThe current chat_id if available, null otherwise.
chat()?ChatThe current Chat if available, null otherwise.
userId()?intThe current from.id if available, null otherwise.
user()?UserThe current User (from Telegram's object) if available, null otherwise.
messageId()?intThe current message.message_id if available, null otherwise.
message()?MessageThe current Message if available, null otherwise.
isCallbackQuery()boolIf the current update contains a callback_query.
callbackQuery()?CallbackQueryThe current CallbackQuery if available, null otherwise.
isInlineQuery()boolIf the current update contains an inline_query.
inlineQuery()?InlineQueryThe current InlineQuery if available, null otherwise.
chosenInlineResult()?ChosenInlineResultThe current ChosenInlineResult if available, null otherwise.
shippingQuery()?ShippingQueryThe current ShippingQuery if available, null otherwise.
isPreCheckoutQuery()boolIf the current update contains a pre_checkout_query.
preCheckoutQuery()?PreCheckoutQueryThe current PreCheckoutQuery if available, null otherwise.
poll()?PollThe current Poll if available, null otherwise.
pollAnswer()?PollAnswerThe current PollAnswer if available, null otherwise.
isMyChatMember()boolIf the current ChatMemberUpdated is in the my_chat_member.
chatMember()?ChatMemberUpdatedThe 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();
tip

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