TelegramPHP Documentation

Parts

Every object the Bot API describes has a part : a small model class under Telegram\Parts that holds the attributes Telegram sent and knows how to turn them back into what Telegram expects. They are the same idea as DiscordPHP's parts, and there are 400 of them, one per documented type.

You rarely construct one yourself - they arrive from calls and from updates.

Reading attributes

Attributes are readable as properties or as array keys, whichever reads better at the call site:

$message->message_id;
$message->chat->title;
$message['from']['username'];

isset($message->text);        // false when Telegram omitted it

Attribute names are Telegram's own - message_id , not messageId - so the documentation and your code say the same thing. Every part declares its attributes as @property tags, so an editor completes them and tells you the type and whether it is optional.

An attribute Telegram did not send is null rather than an error, which is what makes the optional fields of Message bearable:

if ($message->photo !== null) {
    // a photo message
}

Nested objects

Nested objects are hydrated into parts, recursively, and arrays of them into a Discord\Helpers\Collection :

$message->chat;                     // Telegram\Parts\Chat
$message->from;                     // Telegram\Parts\User
$message->entities;                 // Collection of MessageEntity
$message->entities->first()->type;  // 'bold'
$message->photo->last()->file_id;   // the largest size Telegram made

Timestamps

Fields the Bot API sends as Unix timestamps read back as CarbonImmutable:

$message->date->toIso8601String();
$message->date->diffForHumans();
$member->until_date?->isPast();

Serialisation keeps the original integer, so a part that goes back to Telegram is byte-identical to the one that arrived:

$message->jsonSerialize()['date'];   // 1700000000
json_encode($message);               // the payload Telegram sent

Union types

A few Bot API types are really a choice between several - ChatMember is one of six, InputMedia one of six, ReactionType one of three. Those are abstract parts, and an incoming payload resolves to the concrete one:

use Telegram\Parts\ChatMemberAdministrator;

$telegram->getChatMember($chatId, $userId)->then(function (ChatMember $member) {
    if ($member instanceof ChatMemberAdministrator) {
        $member->can_delete_messages;
    }

    $member->status;   // 'administrator'
});

Resolution is by the field Telegram tags the union with - status here, type for most others. A tag this build has never seen, because Telegram added a member since it was generated, resolves to the closest match by shape rather than failing the whole update.

Behaviour

Parts are not only data. The ones worth acting on carry methods that use the client they came from, so the ids come off the object instead of out of your variables.

Message

$message->reply('on it');                       // quotes the message
$message->say('anyone there?');                 // same chat, no quote
$message->replyWithPhoto(InputFile::fromPath('cat.jpg'));
$message->edit('changed');                      // bot's own messages only
$message->editCaption('a better caption');
$message->editReplyMarkup($keyboard);
$message->react('👍');                          // or react(null) to clear
$message->pin();
$message->unpin();
$message->forward($otherChatId);
$message->copy($otherChatId);                   // no link back to the original
$message->delete();

Each takes an $options array for any other field of the underlying method, and what you pass wins over what the part filled in:

$message->reply('<b>on it</b>', ['parse_mode' => 'HTML']);

Chat

$chat->sendMessage('hello');
$chat->sendPhoto(InputFile::fromPath('cat.jpg'));
$chat->sendAction('typing');
$chat->fetch();                    // the full ChatFullInfo record
$chat->getMember($userId);
$chat->countMembers();
$chat->ban($userId, ['until_date' => time() + 3600]);
$chat->unban($userId);
$chat->leave();

User

$user->getFullName();     // 'Ada Lovelace'
$user->getHandle();       // '@ada', or the full name when they have no username
$user->mention();         // an HTML link that works even without a username
$user->mention('Markdown');
$user->sendMessage('hi'); // only works if they started the bot first
$user->getProfilePhotos();

CallbackQuery

$query->answer();                        // always answer - the button spins until you do
$query->answer('Done', showAlert: true);
$query->answerWithUrl('https://t.me/...');
$query->editMessage('Deployed.');        // edits whichever message the button is under

File

$file->getUrl();              // includes the bot token - do not log it
$file->download();            // promise of the bytes
$file->save('/tmp/photo.jpg');

Others

InlineQuery::answer() , ChatJoinRequest::approve() and decline() , PreCheckoutQuery::answer() , ShippingQuery::answer() .

Building a part yourself

When you need one - to pass a structured object to a method, say - the factory builds it:

use Telegram\Parts\Message;

$message = $telegram->getFactory()->part(Message::class, $rawArray);

Though for arguments you can equally pass a plain array: every field the API types as an object accepts an array, a part, or anything else that is JsonSerializable .

Search results